// 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()));
}
}
}