1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/automation/automation_tab_helper.h"
6
7 #include <algorithm>
8
9 #include "content/browser/tab_contents/navigation_controller.h"
10 #include "content/browser/tab_contents/tab_contents.h"
11 #include "chrome/common/automation_messages.h"
12 #include "ipc/ipc_message.h"
13 #include "ipc/ipc_message_macros.h"
14
TabEventObserver()15 TabEventObserver::TabEventObserver() { }
16
~TabEventObserver()17 TabEventObserver::~TabEventObserver() {
18 for (size_t i = 0; i < event_sources_.size(); ++i) {
19 if (event_sources_[i])
20 event_sources_[i]->RemoveObserver(this);
21 }
22 }
23
StartObserving(AutomationTabHelper * tab_helper)24 void TabEventObserver::StartObserving(AutomationTabHelper* tab_helper) {
25 tab_helper->AddObserver(this);
26 event_sources_.push_back(tab_helper->AsWeakPtr());
27 }
28
StopObserving(AutomationTabHelper * tab_helper)29 void TabEventObserver::StopObserving(AutomationTabHelper* tab_helper) {
30 tab_helper->RemoveObserver(this);
31 EventSourceVector::iterator iter =
32 std::find(event_sources_.begin(), event_sources_.end(), tab_helper);
33 if (iter != event_sources_.end())
34 event_sources_.erase(iter);
35 }
36
AutomationTabHelper(TabContents * tab_contents)37 AutomationTabHelper::AutomationTabHelper(TabContents* tab_contents)
38 : TabContentsObserver(tab_contents),
39 is_loading_(false) {
40 }
41
~AutomationTabHelper()42 AutomationTabHelper::~AutomationTabHelper() { }
43
AddObserver(TabEventObserver * observer)44 void AutomationTabHelper::AddObserver(TabEventObserver* observer) {
45 observers_.AddObserver(observer);
46 }
47
RemoveObserver(TabEventObserver * observer)48 void AutomationTabHelper::RemoveObserver(TabEventObserver* observer) {
49 observers_.RemoveObserver(observer);
50 }
51
has_pending_loads() const52 bool AutomationTabHelper::has_pending_loads() const {
53 return is_loading_ || !pending_client_redirects_.empty();
54 }
55
DidStartLoading()56 void AutomationTabHelper::DidStartLoading() {
57 if (is_loading_) {
58 // DidStartLoading is often called twice. Once when the renderer sends a
59 // load start message, and once when the browser calls it directly as a
60 // result of some user-initiated navigation.
61 VLOG(1) << "Received DidStartLoading while loading already started.";
62 return;
63 }
64 bool had_pending_loads = has_pending_loads();
65 is_loading_ = true;
66 if (!had_pending_loads) {
67 FOR_EACH_OBSERVER(TabEventObserver, observers_,
68 OnFirstPendingLoad(tab_contents()));
69 }
70 }
71
DidStopLoading()72 void AutomationTabHelper::DidStopLoading() {
73 if (!is_loading_) {
74 LOG(WARNING) << "Received DidStopLoading while loading already stopped.";
75 return;
76 }
77 is_loading_ = false;
78 if (!has_pending_loads()) {
79 FOR_EACH_OBSERVER(TabEventObserver, observers_,
80 OnNoMorePendingLoads(tab_contents()));
81 }
82 }
83
RenderViewGone()84 void AutomationTabHelper::RenderViewGone() {
85 OnTabOrRenderViewDestroyed(tab_contents());
86 }
87
TabContentsDestroyed(TabContents * tab_contents)88 void AutomationTabHelper::TabContentsDestroyed(TabContents* tab_contents) {
89 OnTabOrRenderViewDestroyed(tab_contents);
90 }
91
OnTabOrRenderViewDestroyed(TabContents * tab_contents)92 void AutomationTabHelper::OnTabOrRenderViewDestroyed(
93 TabContents* tab_contents) {
94 if (has_pending_loads()) {
95 is_loading_ = false;
96 pending_client_redirects_.clear();
97 FOR_EACH_OBSERVER(TabEventObserver, observers_,
98 OnNoMorePendingLoads(tab_contents));
99 }
100 }
101
OnMessageReceived(const IPC::Message & message)102 bool AutomationTabHelper::OnMessageReceived(const IPC::Message& message) {
103 bool handled = true;
104 bool msg_is_good = true;
105 IPC_BEGIN_MESSAGE_MAP_EX(AutomationTabHelper, message, msg_is_good)
106 IPC_MESSAGE_HANDLER(AutomationMsg_WillPerformClientRedirect,
107 OnWillPerformClientRedirect)
108 IPC_MESSAGE_HANDLER(AutomationMsg_DidCompleteOrCancelClientRedirect,
109 OnDidCompleteOrCancelClientRedirect)
110 IPC_MESSAGE_UNHANDLED(handled = false)
111 IPC_END_MESSAGE_MAP_EX()
112 if (!msg_is_good) {
113 LOG(ERROR) << "Failed to deserialize an IPC message";
114 }
115 return handled;
116 }
117
OnWillPerformClientRedirect(int64 frame_id,double delay_seconds)118 void AutomationTabHelper::OnWillPerformClientRedirect(
119 int64 frame_id, double delay_seconds) {
120 // Ignore all non-zero delays.
121 // TODO(kkania): Handle timed redirects.
122 if (delay_seconds > 0) {
123 LOG(WARNING) << "Ignoring timed redirect scheduled for " << delay_seconds
124 << " seconds later. Will not wait for the redirect to occur";
125 return;
126 }
127
128 bool first_pending_load = !has_pending_loads();
129 pending_client_redirects_.insert(frame_id);
130 if (first_pending_load) {
131 FOR_EACH_OBSERVER(TabEventObserver, observers_,
132 OnFirstPendingLoad(tab_contents()));
133 }
134 }
135
OnDidCompleteOrCancelClientRedirect(int64 frame_id)136 void AutomationTabHelper::OnDidCompleteOrCancelClientRedirect(int64 frame_id) {
137 std::set<int64>::iterator iter =
138 pending_client_redirects_.find(frame_id);
139 // It is possible that we did not track the redirect becasue it had a non-zero
140 // delay. See the comment in |OnWillPerformClientRedirect|.
141 if (iter != pending_client_redirects_.end()) {
142 pending_client_redirects_.erase(iter);
143 if (!has_pending_loads()) {
144 FOR_EACH_OBSERVER(TabEventObserver, observers_,
145 OnNoMorePendingLoads(tab_contents()));
146 }
147 }
148 }
149