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 15 TabEventObserver::TabEventObserver() { } 16 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 24 void TabEventObserver::StartObserving(AutomationTabHelper* tab_helper) { 25 tab_helper->AddObserver(this); 26 event_sources_.push_back(tab_helper->AsWeakPtr()); 27 } 28 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 37 AutomationTabHelper::AutomationTabHelper(TabContents* tab_contents) 38 : TabContentsObserver(tab_contents), 39 is_loading_(false) { 40 } 41 42 AutomationTabHelper::~AutomationTabHelper() { } 43 44 void AutomationTabHelper::AddObserver(TabEventObserver* observer) { 45 observers_.AddObserver(observer); 46 } 47 48 void AutomationTabHelper::RemoveObserver(TabEventObserver* observer) { 49 observers_.RemoveObserver(observer); 50 } 51 52 bool AutomationTabHelper::has_pending_loads() const { 53 return is_loading_ || !pending_client_redirects_.empty(); 54 } 55 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 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 84 void AutomationTabHelper::RenderViewGone() { 85 OnTabOrRenderViewDestroyed(tab_contents()); 86 } 87 88 void AutomationTabHelper::TabContentsDestroyed(TabContents* tab_contents) { 89 OnTabOrRenderViewDestroyed(tab_contents); 90 } 91 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 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 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 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