// 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_frame_host.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/view_type_utils.h" #include "net/base/net_errors.h" using content::ResourceType; namespace GetFrame = extensions::api::web_navigation::GetFrame; namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames; DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver); namespace extensions { 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_host(NULL), target_web_contents(NULL), target_url() { } WebNavigationEventRouter::PendingWebContents::PendingWebContents( content::WebContents* source_web_contents, content::RenderFrameHost* source_frame_host, content::WebContents* target_web_contents, const GURL& target_url) : source_web_contents(source_web_contents), source_frame_host(source_frame_host), 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(); content::RenderFrameHost* frame_host = content::RenderFrameHost::FromID( details->source_web_contents->GetRenderProcessHost()->GetID(), details->source_render_frame_id); if (!frame_navigation_state.CanSendEvents(frame_host)) 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, frame_host, details->target_web_contents, details->target_url); } else { helpers::DispatchOnCreatedNavigationTarget( details->source_web_contents, details->target_web_contents->GetBrowserContext(), frame_host, 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(); if (frame_navigation_state.CanSendEvents(iter->second.source_frame_host)) { helpers::DispatchOnCreatedNavigationTarget( iter->second.source_web_contents, iter->second.target_web_contents->GetBrowserContext(), iter->second.source_frame_host, 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::RenderFrameDeleted( content::RenderFrameHost* render_frame_host) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); if (render_view_host != render_view_host_ && render_view_host != pending_render_view_host_) { return; } if (navigation_state_.CanSendEvents(render_frame_host) && !navigation_state_.GetNavigationCompleted(render_frame_host)) { helpers::DispatchOnErrorOccurred( web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host), net::ERR_ABORTED); } navigation_state_.FrameDetached(render_frame_host); } 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, NULL); } 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_, NULL); } pending_render_view_host_ = render_view_host; } } void WebNavigationTabObserver::DidStartProvisionalLoadForFrame( content::RenderFrameHost* render_frame_host, const GURL& validated_url, bool is_error_page, bool is_iframe_srcdoc) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); DVLOG(2) << "DidStartProvisionalLoad(" << "render_view_host=" << render_view_host << ", frame_num=" << render_frame_host->GetRoutingID() << ", 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; navigation_state_.TrackFrame( render_frame_host, validated_url, is_error_page, is_iframe_srcdoc); if (!navigation_state_.CanSendEvents(render_frame_host)) return; helpers::DispatchOnBeforeNavigate( web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host)); } void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame( content::RenderFrameHost* render_frame_host, const GURL& url, ui::PageTransition transition_type) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); DVLOG(2) << "DidCommitProvisionalLoad(" << "render_view_host=" << render_view_host << ", frame_num=" << render_frame_host->GetRoutingID() << ", url=" << url << ")"; if (render_view_host != render_view_host_ && render_view_host != pending_render_view_host_) return; bool is_reference_fragment_navigation = IsReferenceFragmentNavigation(render_frame_host, url); bool is_history_state_modification = navigation_state_.GetNavigationCommitted(render_frame_host); if (!render_frame_host->GetParent() && 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_, render_frame_host); if (pending_render_view_host_) { SendErrorEvents(web_contents(), pending_render_view_host_, NULL); pending_render_view_host_ = NULL; } } else if (pending_render_view_host_ == render_view_host) { SendErrorEvents(web_contents(), render_view_host_, NULL); render_view_host_ = pending_render_view_host_; pending_render_view_host_ = NULL; } // Update the URL as it might have changed. navigation_state_.UpdateFrame(render_frame_host, url); navigation_state_.SetNavigationCommitted(render_frame_host); if (!navigation_state_.CanSendEvents(render_frame_host)) return; if (is_reference_fragment_navigation) { helpers::DispatchOnCommitted( web_navigation::OnReferenceFragmentUpdated::kEventName, web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host), transition_type); } else if (is_history_state_modification) { helpers::DispatchOnCommitted( web_navigation::OnHistoryStateUpdated::kEventName, web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host), transition_type); } else { if (navigation_state_.GetIsServerRedirected(render_frame_host)) { transition_type = ui::PageTransitionFromInt( transition_type | ui::PAGE_TRANSITION_SERVER_REDIRECT); } helpers::DispatchOnCommitted(web_navigation::OnCommitted::kEventName, web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host), transition_type); } } void WebNavigationTabObserver::DidFailProvisionalLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); DVLOG(2) << "DidFailProvisionalLoad(" << "render_view_host=" << render_view_host << ", frame_num=" << render_frame_host->GetRoutingID() << ", 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; } if (navigation_state_.CanSendEvents(render_frame_host)) { helpers::DispatchOnErrorOccurred( web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host), error_code); } navigation_state_.SetErrorOccurredInFrame(render_frame_host); if (stop_tracking_frames) { navigation_state_.StopTrackingFramesInRVH(render_view_host, NULL); } } void WebNavigationTabObserver::DocumentLoadedInFrame( content::RenderFrameHost* render_frame_host) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); DVLOG(2) << "DocumentLoadedInFrame(" << "render_view_host=" << render_view_host << ", frame_num=" << render_frame_host->GetRoutingID() << ")"; if (render_view_host != render_view_host_) return; if (!navigation_state_.CanSendEvents(render_frame_host)) return; navigation_state_.SetParsingFinished(render_frame_host); helpers::DispatchOnDOMContentLoaded( web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host)); if (!navigation_state_.GetNavigationCompleted(render_frame_host)) 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(), render_frame_host, navigation_state_.GetUrl(render_frame_host)); } void WebNavigationTabObserver::DidFinishLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); DVLOG(2) << "DidFinishLoad(" << "render_view_host=" << render_view_host << ", frame_num=" << render_frame_host->GetRoutingID() << ", url=" << validated_url << ")"; if (render_view_host != render_view_host_) return; // When showing replacement content, we might get load signals for frames // that weren't reguarly loaded. if (!navigation_state_.IsValidFrame(render_frame_host)) return; navigation_state_.SetNavigationCompleted(render_frame_host); if (!navigation_state_.CanSendEvents(render_frame_host)) return; DCHECK(navigation_state_.GetUrl(render_frame_host) == validated_url || (navigation_state_.GetUrl(render_frame_host) == GURL(content::kAboutSrcDocURL) && validated_url == GURL(url::kAboutBlankURL))) << "validated URL is " << validated_url << " but we expected " << navigation_state_.GetUrl(render_frame_host); // 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(render_frame_host)) return; helpers::DispatchOnCompleted(web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host)); } void WebNavigationTabObserver::DidFailLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code, const base::string16& error_description) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); DVLOG(2) << "DidFailLoad(" << "render_view_host=" << render_view_host << ", frame_num=" << render_frame_host->GetRoutingID() << ", url=" << validated_url << ")"; if (render_view_host != render_view_host_) return; // When showing replacement content, we might get load signals for frames // that weren't reguarly loaded. if (!navigation_state_.IsValidFrame(render_frame_host)) return; if (navigation_state_.CanSendEvents(render_frame_host)) { helpers::DispatchOnErrorOccurred( web_contents(), render_frame_host, navigation_state_.GetUrl(render_frame_host), error_code); } navigation_state_.SetErrorOccurredInFrame(render_frame_host); } void WebNavigationTabObserver::DidGetRedirectForResourceRequest( content::RenderViewHost* render_view_host, const content::ResourceRedirectDetails& details) { if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME && details.resource_type != content::RESOURCE_TYPE_SUB_FRAME) { return; } content::RenderFrameHost* render_frame_host = content::RenderFrameHost::FromID(render_view_host->GetProcess()->GetID(), details.render_frame_id); navigation_state_.SetIsServerRedirected(render_frame_host); } void WebNavigationTabObserver::DidOpenRequestedURL( content::WebContents* new_contents, const GURL& url, const content::Referrer& referrer, WindowOpenDisposition disposition, ui::PageTransition transition, int64 source_frame_num) { content::RenderFrameHost* render_frame_host = content::RenderFrameHost::FromID(render_view_host_->GetProcess()->GetID(), source_frame_num); if (!navigation_state_.CanSendEvents(render_frame_host)) 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(), render_frame_host, new_contents, url); } void WebNavigationTabObserver::WebContentsDestroyed() { g_tab_observer.Get().erase(web_contents()); registrar_.RemoveAll(); SendErrorEvents(web_contents(), NULL, NULL); } void WebNavigationTabObserver::SendErrorEvents( content::WebContents* web_contents, content::RenderViewHost* render_view_host, content::RenderFrameHost* frame_host_to_skip) { for (FrameNavigationState::const_iterator it = navigation_state_.begin(); it != navigation_state_.end(); ++it) { if (!navigation_state_.GetNavigationCompleted(*it) && navigation_state_.CanSendEvents(*it) && *it != frame_host_to_skip && (!render_view_host || (*it)->GetRenderViewHost() == render_view_host)) { navigation_state_.SetErrorOccurredInFrame(*it); helpers::DispatchOnErrorOccurred( web_contents, *it, navigation_state_.GetUrl(*it), net::ERR_ABORTED); } } if (render_view_host) navigation_state_.StopTrackingFramesInRVH(render_view_host, frame_host_to_skip); } // See also NavigationController::IsURLInPageNavigation. bool WebNavigationTabObserver::IsReferenceFragmentNavigation( content::RenderFrameHost* render_frame_host, const GURL& url) { GURL existing_url = navigation_state_.GetUrl(render_frame_host); if (existing_url == url) return false; url::Replacements<char> replacements; replacements.ClearRef(); return existing_url.ReplaceComponents(replacements) == url.ReplaceComponents(replacements); } bool WebNavigationGetFrameFunction::RunSync() { 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(); content::RenderFrameHost* render_frame_host = frame_id == 0 ? frame_navigation_state.GetLastCommittedMainFrameHost() : content::RenderFrameHost::FromID(process_id, frame_id); if (!frame_navigation_state.IsValidFrame(render_frame_host)) return true; GURL frame_url = frame_navigation_state.GetUrl(render_frame_host); 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(render_frame_host); frame_details.parent_frame_id = helpers::GetFrameId(render_frame_host->GetParent()); results_ = GetFrame::Results::Create(frame_details); return true; } bool WebNavigationGetAllFramesFunction::RunSync() { 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) { GURL frame_url = navigation_state.GetUrl(*it); 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(*it); frame->parent_frame_id = helpers::GetFrameId((*it)->GetParent()); frame->process_id = (*it)->GetRenderViewHost()->GetProcess()->GetID(); frame->error_occurred = navigation_state.GetErrorOccurredInFrame(*it); result_list.push_back(frame); } results_ = GetAllFrames::Results::Create(result_list); return true; } WebNavigationAPI::WebNavigationAPI(content::BrowserContext* context) : browser_context_(context) { EventRouter* event_router = EventRouter::Get(browser_context_); 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() { EventRouter::Get(browser_context_)->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_))); EventRouter::Get(browser_context_)->UnregisterObserver(this); } } // namespace extensions