root/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. target_url
  2. target_url
  3. OnBrowserAdded
  4. OnBrowserRemoved
  5. TabReplacedAt
  6. Observe
  7. Retargeting
  8. TabAdded
  9. TabDestroyed
  10. pending_render_view_host_
  11. Get
  12. GetRenderViewHostInProcess
  13. RenderViewDeleted
  14. AboutToNavigateRenderView
  15. DidStartProvisionalLoadForFrame
  16. DidCommitProvisionalLoadForFrame
  17. DidFailProvisionalLoad
  18. DocumentLoadedInFrame
  19. DidFinishLoad
  20. DidFailLoad
  21. DidGetRedirectForResourceRequest
  22. DidOpenRequestedURL
  23. FrameDetached
  24. WebContentsDestroyed
  25. SendErrorEvents
  26. IsReferenceFragmentNavigation
  27. RunImpl
  28. RunImpl
  29. Shutdown
  30. GetFactoryInstance
  31. OnListenerAdded

// Copyright (c) 2012 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.

// Implements the Chrome Extensions WebNavigation API.

#include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"

#include "base/lazy_instance.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/web_navigation/web_navigation_api_constants.h"
#include "chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/retargeting_details.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/extensions/api/web_navigation.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_request_details.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/view_type_utils.h"
#include "net/base/net_errors.h"

namespace GetFrame = extensions::api::web_navigation::GetFrame;
namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;

DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver);

namespace extensions {

#if !defined(OS_ANDROID)

namespace helpers = web_navigation_api_helpers;
namespace keys = web_navigation_api_constants;
namespace web_navigation = api::web_navigation;

namespace {

typedef std::map<content::WebContents*, WebNavigationTabObserver*>
    TabObserverMap;
static base::LazyInstance<TabObserverMap> g_tab_observer =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

// WebNavigtionEventRouter -------------------------------------------

WebNavigationEventRouter::PendingWebContents::PendingWebContents()
    : source_web_contents(NULL),
      source_frame_id(0),
      source_frame_is_main_frame(false),
      target_web_contents(NULL),
      target_url() {
}

WebNavigationEventRouter::PendingWebContents::PendingWebContents(
    content::WebContents* source_web_contents,
    int64 source_frame_id,
    bool source_frame_is_main_frame,
    content::WebContents* target_web_contents,
    const GURL& target_url)
    : source_web_contents(source_web_contents),
      source_frame_id(source_frame_id),
      source_frame_is_main_frame(source_frame_is_main_frame),
      target_web_contents(target_web_contents),
      target_url(target_url) {
}

WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}

WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile)
    : profile_(profile) {
  CHECK(registrar_.IsEmpty());
  registrar_.Add(this,
                 chrome::NOTIFICATION_RETARGETING,
                 content::NotificationService::AllSources());
  registrar_.Add(this,
                 chrome::NOTIFICATION_TAB_ADDED,
                 content::NotificationService::AllSources());
  registrar_.Add(this,
                 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
                 content::NotificationService::AllSources());

  BrowserList::AddObserver(this);
  for (chrome::BrowserIterator it; !it.done(); it.Next())
    OnBrowserAdded(*it);
}

WebNavigationEventRouter::~WebNavigationEventRouter() {
  for (chrome::BrowserIterator it; !it.done(); it.Next())
    OnBrowserRemoved(*it);
  BrowserList::RemoveObserver(this);
}

void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
  if (!profile_->IsSameProfile(browser->profile()))
    return;
  browser->tab_strip_model()->AddObserver(this);
}

void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
  if (!profile_->IsSameProfile(browser->profile()))
    return;
  browser->tab_strip_model()->RemoveObserver(this);
}

void WebNavigationEventRouter::TabReplacedAt(
    TabStripModel* tab_strip_model,
    content::WebContents* old_contents,
    content::WebContents* new_contents,
    int index) {
  WebNavigationTabObserver* tab_observer =
      WebNavigationTabObserver::Get(old_contents);
  if (!tab_observer) {
    // If you hit this DCHECK(), please add reproduction steps to
    // http://crbug.com/109464.
    DCHECK(GetViewType(old_contents) != VIEW_TYPE_TAB_CONTENTS);
    return;
  }
  const FrameNavigationState& frame_navigation_state =
      tab_observer->frame_navigation_state();

  if (!frame_navigation_state.IsValidUrl(old_contents->GetURL()) ||
      !frame_navigation_state.IsValidUrl(new_contents->GetURL()))
    return;

  helpers::DispatchOnTabReplaced(old_contents, profile_, new_contents);
}

void WebNavigationEventRouter::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    case chrome::NOTIFICATION_RETARGETING: {
      Profile* profile = content::Source<Profile>(source).ptr();
      if (profile->GetOriginalProfile() == profile_) {
        Retargeting(
            content::Details<const RetargetingDetails>(details).ptr());
      }
      break;
    }

    case chrome::NOTIFICATION_TAB_ADDED:
      TabAdded(content::Details<content::WebContents>(details).ptr());
      break;

    case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
      TabDestroyed(content::Source<content::WebContents>(source).ptr());
      break;

    default:
      NOTREACHED();
  }
}

void WebNavigationEventRouter::Retargeting(const RetargetingDetails* details) {
  if (details->source_render_frame_id == 0)
    return;
  WebNavigationTabObserver* tab_observer =
      WebNavigationTabObserver::Get(details->source_web_contents);
  if (!tab_observer) {
    // If you hit this DCHECK(), please add reproduction steps to
    // http://crbug.com/109464.
    DCHECK(GetViewType(details->source_web_contents) != VIEW_TYPE_TAB_CONTENTS);
    return;
  }
  const FrameNavigationState& frame_navigation_state =
      tab_observer->frame_navigation_state();

  FrameNavigationState::FrameID frame_id(
      details->source_render_frame_id,
      details->source_web_contents->GetRenderViewHost());
  if (!frame_navigation_state.CanSendEvents(frame_id))
    return;

  // If the WebContents isn't yet inserted into a tab strip, we need to delay
  // the extension event until the WebContents is fully initialized.
  if (details->not_yet_in_tabstrip) {
    pending_web_contents_[details->target_web_contents] =
        PendingWebContents(
            details->source_web_contents,
            details->source_render_frame_id,
            frame_navigation_state.IsMainFrame(frame_id),
            details->target_web_contents,
            details->target_url);
  } else {
    helpers::DispatchOnCreatedNavigationTarget(
        details->source_web_contents,
        details->target_web_contents->GetBrowserContext(),
        details->source_render_frame_id,
        frame_navigation_state.IsMainFrame(frame_id),
        details->target_web_contents,
        details->target_url);
  }
}

void WebNavigationEventRouter::TabAdded(content::WebContents* tab) {
  std::map<content::WebContents*, PendingWebContents>::iterator iter =
      pending_web_contents_.find(tab);
  if (iter == pending_web_contents_.end())
    return;

  WebNavigationTabObserver* tab_observer =
      WebNavigationTabObserver::Get(iter->second.source_web_contents);
  if (!tab_observer) {
    NOTREACHED();
    return;
  }
  const FrameNavigationState& frame_navigation_state =
      tab_observer->frame_navigation_state();

  FrameNavigationState::FrameID frame_id(
      iter->second.source_frame_id,
      iter->second.source_web_contents->GetRenderViewHost());
  if (frame_navigation_state.CanSendEvents(frame_id)) {
    helpers::DispatchOnCreatedNavigationTarget(
        iter->second.source_web_contents,
        iter->second.target_web_contents->GetBrowserContext(),
        iter->second.source_frame_id,
        iter->second.source_frame_is_main_frame,
        iter->second.target_web_contents,
        iter->second.target_url);
  }
  pending_web_contents_.erase(iter);
}

void WebNavigationEventRouter::TabDestroyed(content::WebContents* tab) {
  pending_web_contents_.erase(tab);
  for (std::map<content::WebContents*, PendingWebContents>::iterator i =
           pending_web_contents_.begin(); i != pending_web_contents_.end(); ) {
    if (i->second.source_web_contents == tab)
      pending_web_contents_.erase(i++);
    else
      ++i;
  }
}

// WebNavigationTabObserver ------------------------------------------

WebNavigationTabObserver::WebNavigationTabObserver(
    content::WebContents* web_contents)
    : WebContentsObserver(web_contents),
      render_view_host_(NULL),
      pending_render_view_host_(NULL) {
  g_tab_observer.Get().insert(TabObserverMap::value_type(web_contents, this));
  registrar_.Add(this,
                 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
                 content::NotificationService::AllSources());
}

WebNavigationTabObserver::~WebNavigationTabObserver() {}

// static
WebNavigationTabObserver* WebNavigationTabObserver::Get(
    content::WebContents* web_contents) {
  TabObserverMap::iterator i = g_tab_observer.Get().find(web_contents);
  return i == g_tab_observer.Get().end() ? NULL : i->second;
}

content::RenderViewHost* WebNavigationTabObserver::GetRenderViewHostInProcess(
    int process_id) const {
  if (render_view_host_ &&
      render_view_host_->GetProcess()->GetID() == process_id) {
    return render_view_host_;
  }
  if (pending_render_view_host_ &&
      pending_render_view_host_->GetProcess()->GetID() == process_id) {
    return pending_render_view_host_;
  }
  return NULL;
}

void WebNavigationTabObserver::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  switch (type) {
    case content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW: {
      // The RenderView is technically not yet deleted, but the RenderViewHost
      // already starts to filter out some IPCs. In order to not get confused,
      // we consider the RenderView dead already now.
      RenderViewDeleted(content::Source<content::RenderViewHost>(source).ptr());
      break;
    }

    default:
      NOTREACHED();
  }
}

void WebNavigationTabObserver::RenderViewDeleted(
    content::RenderViewHost* render_view_host) {
  if (render_view_host == render_view_host_) {
    render_view_host_ = NULL;
    if (pending_render_view_host_) {
      render_view_host_ = pending_render_view_host_;
      pending_render_view_host_ = NULL;
    }
  } else if (render_view_host == pending_render_view_host_) {
    pending_render_view_host_ = NULL;
  } else {
    return;
  }
  SendErrorEvents(
      web_contents(), render_view_host, FrameNavigationState::FrameID());
}

void WebNavigationTabObserver::AboutToNavigateRenderView(
    content::RenderViewHost* render_view_host) {
  if (!render_view_host_) {
    render_view_host_ = render_view_host;
  } else if (render_view_host != render_view_host_) {
    if (pending_render_view_host_) {
      SendErrorEvents(web_contents(),
                      pending_render_view_host_,
                      FrameNavigationState::FrameID());
    }
    pending_render_view_host_ = render_view_host;
  }
}

void WebNavigationTabObserver::DidStartProvisionalLoadForFrame(
    int64 frame_num,
    int64 parent_frame_num,
    bool is_main_frame,
    const GURL& validated_url,
    bool is_error_page,
    bool is_iframe_srcdoc,
    content::RenderViewHost* render_view_host) {
  DVLOG(2) << "DidStartProvisionalLoad("
           << "render_view_host=" << render_view_host
           << ", frame_num=" << frame_num
           << ", url=" << validated_url << ")";
  if (!render_view_host_)
    render_view_host_ = render_view_host;
  if (render_view_host != render_view_host_ &&
      render_view_host != pending_render_view_host_)
    return;

  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
  FrameNavigationState::FrameID parent_frame_id(
      parent_frame_num, render_view_host);

  navigation_state_.TrackFrame(frame_id,
                               parent_frame_id,
                               validated_url,
                               is_main_frame,
                               is_error_page,
                               is_iframe_srcdoc);

  if (!navigation_state_.CanSendEvents(frame_id))
    return;

  helpers::DispatchOnBeforeNavigate(
      web_contents(),
      render_view_host->GetProcess()->GetID(),
      frame_num,
      is_main_frame,
      parent_frame_num,
      navigation_state_.IsMainFrame(parent_frame_id),
      navigation_state_.GetUrl(frame_id));
}

void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
    int64 frame_num,
    const base::string16& frame_unique_name,
    bool is_main_frame,
    const GURL& url,
    content::PageTransition transition_type,
    content::RenderViewHost* render_view_host) {
  DVLOG(2) << "DidCommitProvisionalLoad("
           << "render_view_host=" << render_view_host
           << ", frame_num=" << frame_num
           << ", url=" << url << ")";
  if (render_view_host != render_view_host_ &&
      render_view_host != pending_render_view_host_)
    return;
  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);

  bool is_reference_fragment_navigation =
      IsReferenceFragmentNavigation(frame_id, url);
  bool is_history_state_modification =
      navigation_state_.GetNavigationCommitted(frame_id);

  if (is_main_frame && render_view_host_ == render_view_host) {
    // Changing the reference fragment or the history state using
    // history.pushState or history.replaceState does not cancel on-going
    // iframe navigations.
    if (!is_reference_fragment_navigation && !is_history_state_modification)
      SendErrorEvents(web_contents(), render_view_host_, frame_id);
    if (pending_render_view_host_) {
      SendErrorEvents(web_contents(),
                      pending_render_view_host_,
                      FrameNavigationState::FrameID());
      pending_render_view_host_ = NULL;
    }
  } else if (pending_render_view_host_ == render_view_host) {
    SendErrorEvents(
        web_contents(), render_view_host_, FrameNavigationState::FrameID());
    render_view_host_ = pending_render_view_host_;
    pending_render_view_host_ = NULL;
  }

  // Update the URL as it might have changed.
  navigation_state_.UpdateFrame(frame_id, url);
  navigation_state_.SetNavigationCommitted(frame_id);

  if (!navigation_state_.CanSendEvents(frame_id))
    return;

  if (is_reference_fragment_navigation) {
    helpers::DispatchOnCommitted(
        web_navigation::OnReferenceFragmentUpdated::kEventName,
        web_contents(),
        frame_num,
        is_main_frame,
        navigation_state_.GetUrl(frame_id),
        transition_type);
  } else if (is_history_state_modification) {
    helpers::DispatchOnCommitted(
        web_navigation::OnHistoryStateUpdated::kEventName,
        web_contents(),
        frame_num,
        is_main_frame,
        navigation_state_.GetUrl(frame_id),
        transition_type);
  } else {
    if (navigation_state_.GetIsServerRedirected(frame_id)) {
      transition_type = static_cast<content::PageTransition>(
          transition_type | content::PAGE_TRANSITION_SERVER_REDIRECT);
    }
    helpers::DispatchOnCommitted(
        web_navigation::OnCommitted::kEventName,
        web_contents(),
        frame_num,
        is_main_frame,
        navigation_state_.GetUrl(frame_id),
        transition_type);
  }
}

void WebNavigationTabObserver::DidFailProvisionalLoad(
    int64 frame_num,
    const base::string16& frame_unique_id,
    bool is_main_frame,
    const GURL& validated_url,
    int error_code,
    const base::string16& error_description,
    content::RenderViewHost* render_view_host) {
  DVLOG(2) << "DidFailProvisionalLoad("
           << "render_view_host=" << render_view_host
           << ", frame_num=" << frame_num
           << ", url=" << validated_url << ")";
  if (render_view_host != render_view_host_ &&
      render_view_host != pending_render_view_host_)
    return;
  bool stop_tracking_frames = false;
  if (render_view_host == pending_render_view_host_) {
    pending_render_view_host_ = NULL;
    stop_tracking_frames = true;
  }

  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
  if (navigation_state_.CanSendEvents(frame_id)) {
    helpers::DispatchOnErrorOccurred(
        web_contents(),
        render_view_host->GetProcess()->GetID(),
        navigation_state_.GetUrl(frame_id),
        frame_num,
        is_main_frame,
        error_code);
  }
  navigation_state_.SetErrorOccurredInFrame(frame_id);
  if (stop_tracking_frames) {
    navigation_state_.StopTrackingFramesInRVH(render_view_host,
                                              FrameNavigationState::FrameID());
  }
}

void WebNavigationTabObserver::DocumentLoadedInFrame(
    int64 frame_num,
    content::RenderViewHost* render_view_host) {
  DVLOG(2) << "DocumentLoadedInFrame("
           << "render_view_host=" << render_view_host
           << ", frame_num=" << frame_num << ")";
  if (render_view_host != render_view_host_)
    return;
  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
  if (!navigation_state_.CanSendEvents(frame_id))
    return;
  navigation_state_.SetParsingFinished(frame_id);
  helpers::DispatchOnDOMContentLoaded(web_contents(),
                                      navigation_state_.GetUrl(frame_id),
                                      navigation_state_.IsMainFrame(frame_id),
                                      frame_num);

  if (!navigation_state_.GetNavigationCompleted(frame_id))
    return;

  // The load might already have finished by the time we finished parsing. For
  // compatibility reasons, we artifically delay the load completed signal until
  // after parsing was completed.
  helpers::DispatchOnCompleted(web_contents(),
                               navigation_state_.GetUrl(frame_id),
                               navigation_state_.IsMainFrame(frame_id),
                               frame_num);
}

void WebNavigationTabObserver::DidFinishLoad(
    int64 frame_num,
    const GURL& validated_url,
    bool is_main_frame,
    content::RenderViewHost* render_view_host) {
  DVLOG(2) << "DidFinishLoad("
           << "render_view_host=" << render_view_host
           << ", frame_num=" << frame_num
           << ", url=" << validated_url << ")";
  if (render_view_host != render_view_host_)
    return;
  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
  // When showing replacement content, we might get load signals for frames
  // that weren't reguarly loaded.
  if (!navigation_state_.IsValidFrame(frame_id))
    return;
  navigation_state_.SetNavigationCompleted(frame_id);
  if (!navigation_state_.CanSendEvents(frame_id))
    return;
  DCHECK(
      navigation_state_.GetUrl(frame_id) == validated_url ||
      (navigation_state_.GetUrl(frame_id) == GURL(content::kAboutSrcDocURL) &&
       validated_url == GURL(content::kAboutBlankURL)))
      << "validated URL is " << validated_url << " but we expected "
      << navigation_state_.GetUrl(frame_id);
  DCHECK_EQ(navigation_state_.IsMainFrame(frame_id), is_main_frame);

  // The load might already have finished by the time we finished parsing. For
  // compatibility reasons, we artifically delay the load completed signal until
  // after parsing was completed.
  if (!navigation_state_.GetParsingFinished(frame_id))
    return;
  helpers::DispatchOnCompleted(web_contents(),
                               navigation_state_.GetUrl(frame_id),
                               is_main_frame,
                               frame_num);
}

void WebNavigationTabObserver::DidFailLoad(
    int64 frame_num,
    const GURL& validated_url,
    bool is_main_frame,
    int error_code,
    const base::string16& error_description,
    content::RenderViewHost* render_view_host) {
  DVLOG(2) << "DidFailLoad("
           << "render_view_host=" << render_view_host
           << ", frame_num=" << frame_num
           << ", url=" << validated_url << ")";
  if (render_view_host != render_view_host_)
    return;
  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
  // When showing replacement content, we might get load signals for frames
  // that weren't reguarly loaded.
  if (!navigation_state_.IsValidFrame(frame_id))
    return;
  if (navigation_state_.CanSendEvents(frame_id)) {
    helpers::DispatchOnErrorOccurred(
        web_contents(),
        render_view_host->GetProcess()->GetID(),
        navigation_state_.GetUrl(frame_id),
        frame_num,
        is_main_frame,
        error_code);
  }
  navigation_state_.SetErrorOccurredInFrame(frame_id);
}

void WebNavigationTabObserver::DidGetRedirectForResourceRequest(
    content::RenderViewHost* render_view_host,
    const content::ResourceRedirectDetails& details) {
  if (details.resource_type != ResourceType::MAIN_FRAME &&
      details.resource_type != ResourceType::SUB_FRAME) {
    return;
  }
  FrameNavigationState::FrameID frame_id(details.render_frame_id,
                                         render_view_host);
  navigation_state_.SetIsServerRedirected(frame_id);
}

void WebNavigationTabObserver::DidOpenRequestedURL(
    content::WebContents* new_contents,
    const GURL& url,
    const content::Referrer& referrer,
    WindowOpenDisposition disposition,
    content::PageTransition transition,
    int64 source_frame_num) {
  FrameNavigationState::FrameID frame_id(source_frame_num, render_view_host_);
  if (!navigation_state_.CanSendEvents(frame_id))
    return;

  // We only send the onCreatedNavigationTarget if we end up creating a new
  // window.
  if (disposition != SINGLETON_TAB &&
      disposition != NEW_FOREGROUND_TAB &&
      disposition != NEW_BACKGROUND_TAB &&
      disposition != NEW_POPUP &&
      disposition != NEW_WINDOW &&
      disposition != OFF_THE_RECORD)
    return;

  helpers::DispatchOnCreatedNavigationTarget(
      web_contents(),
      new_contents->GetBrowserContext(),
      source_frame_num,
      navigation_state_.IsMainFrame(frame_id),
      new_contents,
      url);
}

void WebNavigationTabObserver::FrameDetached(
    content::RenderViewHost* render_view_host,
    int64 frame_num) {
  if (render_view_host != render_view_host_ &&
      render_view_host != pending_render_view_host_) {
    return;
  }
  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
  if (navigation_state_.CanSendEvents(frame_id) &&
      !navigation_state_.GetNavigationCompleted(frame_id)) {
    helpers::DispatchOnErrorOccurred(
        web_contents(),
        render_view_host->GetProcess()->GetID(),
        navigation_state_.GetUrl(frame_id),
        frame_num,
        navigation_state_.IsMainFrame(frame_id),
        net::ERR_ABORTED);
  }
  navigation_state_.FrameDetached(frame_id);
}

void WebNavigationTabObserver::WebContentsDestroyed(content::WebContents* tab) {
  g_tab_observer.Get().erase(tab);
  registrar_.RemoveAll();
  SendErrorEvents(tab, NULL, FrameNavigationState::FrameID());
}

void WebNavigationTabObserver::SendErrorEvents(
    content::WebContents* web_contents,
    content::RenderViewHost* render_view_host,
    FrameNavigationState::FrameID id_to_skip) {
  for (FrameNavigationState::const_iterator frame = navigation_state_.begin();
       frame != navigation_state_.end(); ++frame) {
    if (!navigation_state_.GetNavigationCompleted(*frame) &&
        navigation_state_.CanSendEvents(*frame) &&
        *frame != id_to_skip &&
        (!render_view_host || frame->render_view_host == render_view_host)) {
      navigation_state_.SetErrorOccurredInFrame(*frame);
      helpers::DispatchOnErrorOccurred(
          web_contents,
          frame->render_view_host->GetProcess()->GetID(),
          navigation_state_.GetUrl(*frame),
          frame->frame_num,
          navigation_state_.IsMainFrame(*frame),
          net::ERR_ABORTED);
    }
  }
  if (render_view_host)
    navigation_state_.StopTrackingFramesInRVH(render_view_host, id_to_skip);
}

// See also NavigationController::IsURLInPageNavigation.
bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
    FrameNavigationState::FrameID frame_id,
    const GURL& url) {
  GURL existing_url = navigation_state_.GetUrl(frame_id);
  if (existing_url == url)
    return false;

  url_canon::Replacements<char> replacements;
  replacements.ClearRef();
  return existing_url.ReplaceComponents(replacements) ==
      url.ReplaceComponents(replacements);
}

bool WebNavigationGetFrameFunction::RunImpl() {
  scoped_ptr<GetFrame::Params> params(GetFrame::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());
  int tab_id = params->details.tab_id;
  int frame_id = params->details.frame_id;
  int process_id = params->details.process_id;

  SetResult(base::Value::CreateNullValue());

  content::WebContents* web_contents;
  if (!ExtensionTabUtil::GetTabById(tab_id,
                                    GetProfile(),
                                    include_incognito(),
                                    NULL,
                                    NULL,
                                    &web_contents,
                                    NULL) ||
      !web_contents) {
    return true;
  }

  WebNavigationTabObserver* observer =
      WebNavigationTabObserver::Get(web_contents);
  DCHECK(observer);

  const FrameNavigationState& frame_navigation_state =
      observer->frame_navigation_state();

  if (frame_id == 0)
    frame_id = frame_navigation_state.GetMainFrameID().frame_num;

  content::RenderViewHost* render_view_host =
      observer->GetRenderViewHostInProcess(process_id);
  if (!render_view_host)
    return true;

  FrameNavigationState::FrameID internal_frame_id(frame_id, render_view_host);
  if (!frame_navigation_state.IsValidFrame(internal_frame_id))
    return true;

  GURL frame_url = frame_navigation_state.GetUrl(internal_frame_id);
  if (!frame_navigation_state.IsValidUrl(frame_url))
    return true;

  GetFrame::Results::Details frame_details;
  frame_details.url = frame_url.spec();
  frame_details.error_occurred =
      frame_navigation_state.GetErrorOccurredInFrame(internal_frame_id);
  FrameNavigationState::FrameID parent_frame_id =
      frame_navigation_state.GetParentFrameID(internal_frame_id);
  frame_details.parent_frame_id = helpers::GetFrameId(
      frame_navigation_state.IsMainFrame(parent_frame_id),
      parent_frame_id.frame_num);
  results_ = GetFrame::Results::Create(frame_details);
  return true;
}

bool WebNavigationGetAllFramesFunction::RunImpl() {
  scoped_ptr<GetAllFrames::Params> params(GetAllFrames::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());
  int tab_id = params->details.tab_id;

  SetResult(base::Value::CreateNullValue());

  content::WebContents* web_contents;
  if (!ExtensionTabUtil::GetTabById(tab_id,
                                    GetProfile(),
                                    include_incognito(),
                                    NULL,
                                    NULL,
                                    &web_contents,
                                    NULL) ||
      !web_contents) {
    return true;
  }

  WebNavigationTabObserver* observer =
      WebNavigationTabObserver::Get(web_contents);
  DCHECK(observer);

  const FrameNavigationState& navigation_state =
      observer->frame_navigation_state();

  std::vector<linked_ptr<GetAllFrames::Results::DetailsType> > result_list;
  for (FrameNavigationState::const_iterator it = navigation_state.begin();
       it != navigation_state.end(); ++it) {
    FrameNavigationState::FrameID frame_id = *it;
    FrameNavigationState::FrameID parent_frame_id =
        navigation_state.GetParentFrameID(frame_id);
    GURL frame_url = navigation_state.GetUrl(frame_id);
    if (!navigation_state.IsValidUrl(frame_url))
      continue;
    linked_ptr<GetAllFrames::Results::DetailsType> frame(
        new GetAllFrames::Results::DetailsType());
    frame->url = frame_url.spec();
    frame->frame_id = helpers::GetFrameId(
        navigation_state.IsMainFrame(frame_id), frame_id.frame_num);
    frame->parent_frame_id = helpers::GetFrameId(
        navigation_state.IsMainFrame(parent_frame_id),
        parent_frame_id.frame_num);
    frame->process_id = frame_id.render_view_host->GetProcess()->GetID();
    frame->error_occurred = navigation_state.GetErrorOccurredInFrame(frame_id);
    result_list.push_back(frame);
  }
  results_ = GetAllFrames::Results::Create(result_list);
  return true;
}

WebNavigationAPI::WebNavigationAPI(content::BrowserContext* context)
    : browser_context_(context) {
  EventRouter* event_router =
      ExtensionSystem::Get(browser_context_)->event_router();
  event_router->RegisterObserver(this,
                                 web_navigation::OnBeforeNavigate::kEventName);
  event_router->RegisterObserver(this, web_navigation::OnCommitted::kEventName);
  event_router->RegisterObserver(this, web_navigation::OnCompleted::kEventName);
  event_router->RegisterObserver(
      this, web_navigation::OnCreatedNavigationTarget::kEventName);
  event_router->RegisterObserver(
      this, web_navigation::OnDOMContentLoaded::kEventName);
  event_router->RegisterObserver(
      this, web_navigation::OnHistoryStateUpdated::kEventName);
  event_router->RegisterObserver(this,
                                 web_navigation::OnErrorOccurred::kEventName);
  event_router->RegisterObserver(
      this, web_navigation::OnReferenceFragmentUpdated::kEventName);
  event_router->RegisterObserver(this,
                                 web_navigation::OnTabReplaced::kEventName);
}

WebNavigationAPI::~WebNavigationAPI() {
}

void WebNavigationAPI::Shutdown() {
  ExtensionSystem::Get(browser_context_)->event_router()->UnregisterObserver(
      this);
}

static base::LazyInstance<BrowserContextKeyedAPIFactory<WebNavigationAPI> >
    g_factory = LAZY_INSTANCE_INITIALIZER;

// static
BrowserContextKeyedAPIFactory<WebNavigationAPI>*
WebNavigationAPI::GetFactoryInstance() {
  return g_factory.Pointer();
}

void WebNavigationAPI::OnListenerAdded(const EventListenerInfo& details) {
  web_navigation_event_router_.reset(new WebNavigationEventRouter(
      Profile::FromBrowserContext(browser_context_)));
  ExtensionSystem::Get(browser_context_)->event_router()->UnregisterObserver(
      this);
}

#endif  // OS_ANDROID

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */