| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/automation/automation_tab_helper.h" |
| |
| #include <algorithm> |
| |
| #include "content/browser/tab_contents/navigation_controller.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "chrome/common/automation_messages.h" |
| #include "ipc/ipc_message.h" |
| #include "ipc/ipc_message_macros.h" |
| |
| TabEventObserver::TabEventObserver() { } |
| |
| TabEventObserver::~TabEventObserver() { |
| for (size_t i = 0; i < event_sources_.size(); ++i) { |
| if (event_sources_[i]) |
| event_sources_[i]->RemoveObserver(this); |
| } |
| } |
| |
| void TabEventObserver::StartObserving(AutomationTabHelper* tab_helper) { |
| tab_helper->AddObserver(this); |
| event_sources_.push_back(tab_helper->AsWeakPtr()); |
| } |
| |
| void TabEventObserver::StopObserving(AutomationTabHelper* tab_helper) { |
| tab_helper->RemoveObserver(this); |
| EventSourceVector::iterator iter = |
| std::find(event_sources_.begin(), event_sources_.end(), tab_helper); |
| if (iter != event_sources_.end()) |
| event_sources_.erase(iter); |
| } |
| |
| AutomationTabHelper::AutomationTabHelper(TabContents* tab_contents) |
| : TabContentsObserver(tab_contents), |
| is_loading_(false) { |
| } |
| |
| AutomationTabHelper::~AutomationTabHelper() { } |
| |
| void AutomationTabHelper::AddObserver(TabEventObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void AutomationTabHelper::RemoveObserver(TabEventObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool AutomationTabHelper::has_pending_loads() const { |
| return is_loading_ || !pending_client_redirects_.empty(); |
| } |
| |
| void AutomationTabHelper::DidStartLoading() { |
| if (is_loading_) { |
| // DidStartLoading is often called twice. Once when the renderer sends a |
| // load start message, and once when the browser calls it directly as a |
| // result of some user-initiated navigation. |
| VLOG(1) << "Received DidStartLoading while loading already started."; |
| return; |
| } |
| bool had_pending_loads = has_pending_loads(); |
| is_loading_ = true; |
| if (!had_pending_loads) { |
| FOR_EACH_OBSERVER(TabEventObserver, observers_, |
| OnFirstPendingLoad(tab_contents())); |
| } |
| } |
| |
| void AutomationTabHelper::DidStopLoading() { |
| if (!is_loading_) { |
| LOG(WARNING) << "Received DidStopLoading while loading already stopped."; |
| return; |
| } |
| is_loading_ = false; |
| if (!has_pending_loads()) { |
| FOR_EACH_OBSERVER(TabEventObserver, observers_, |
| OnNoMorePendingLoads(tab_contents())); |
| } |
| } |
| |
| void AutomationTabHelper::RenderViewGone() { |
| OnTabOrRenderViewDestroyed(tab_contents()); |
| } |
| |
| void AutomationTabHelper::TabContentsDestroyed(TabContents* tab_contents) { |
| OnTabOrRenderViewDestroyed(tab_contents); |
| } |
| |
| void AutomationTabHelper::OnTabOrRenderViewDestroyed( |
| TabContents* tab_contents) { |
| if (has_pending_loads()) { |
| is_loading_ = false; |
| pending_client_redirects_.clear(); |
| FOR_EACH_OBSERVER(TabEventObserver, observers_, |
| OnNoMorePendingLoads(tab_contents)); |
| } |
| } |
| |
| bool AutomationTabHelper::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| bool msg_is_good = true; |
| IPC_BEGIN_MESSAGE_MAP_EX(AutomationTabHelper, message, msg_is_good) |
| IPC_MESSAGE_HANDLER(AutomationMsg_WillPerformClientRedirect, |
| OnWillPerformClientRedirect) |
| IPC_MESSAGE_HANDLER(AutomationMsg_DidCompleteOrCancelClientRedirect, |
| OnDidCompleteOrCancelClientRedirect) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP_EX() |
| if (!msg_is_good) { |
| LOG(ERROR) << "Failed to deserialize an IPC message"; |
| } |
| return handled; |
| } |
| |
| void AutomationTabHelper::OnWillPerformClientRedirect( |
| int64 frame_id, double delay_seconds) { |
| // Ignore all non-zero delays. |
| // TODO(kkania): Handle timed redirects. |
| if (delay_seconds > 0) { |
| LOG(WARNING) << "Ignoring timed redirect scheduled for " << delay_seconds |
| << " seconds later. Will not wait for the redirect to occur"; |
| return; |
| } |
| |
| bool first_pending_load = !has_pending_loads(); |
| pending_client_redirects_.insert(frame_id); |
| if (first_pending_load) { |
| FOR_EACH_OBSERVER(TabEventObserver, observers_, |
| OnFirstPendingLoad(tab_contents())); |
| } |
| } |
| |
| void AutomationTabHelper::OnDidCompleteOrCancelClientRedirect(int64 frame_id) { |
| std::set<int64>::iterator iter = |
| pending_client_redirects_.find(frame_id); |
| // It is possible that we did not track the redirect becasue it had a non-zero |
| // delay. See the comment in |OnWillPerformClientRedirect|. |
| if (iter != pending_client_redirects_.end()) { |
| pending_client_redirects_.erase(iter); |
| if (!has_pending_loads()) { |
| FOR_EACH_OBSERVER(TabEventObserver, observers_, |
| OnNoMorePendingLoads(tab_contents())); |
| } |
| } |
| } |