Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / api / web_navigation / web_navigation_api.cc
blobaa6c568538fcbefa86173414d18b4e8aa8d898da
1 // Copyright (c) 2012 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.
5 // Implements the Chrome Extensions WebNavigation API.
7 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
9 #include "base/lazy_instance.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_constants.h"
12 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h"
13 #include "chrome/browser/extensions/extension_tab_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/tab_contents/retargeting_details.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_iterator.h"
18 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/common/extensions/api/web_navigation.h"
20 #include "content/public/browser/navigation_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_types.h"
23 #include "content/public/browser/render_frame_host.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/resource_request_details.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/common/url_constants.h"
28 #include "extensions/browser/event_router.h"
29 #include "extensions/browser/view_type_utils.h"
30 #include "net/base/net_errors.h"
32 using content::ResourceType;
34 namespace GetFrame = extensions::api::web_navigation::GetFrame;
35 namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;
37 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver);
39 namespace extensions {
41 namespace helpers = web_navigation_api_helpers;
42 namespace keys = web_navigation_api_constants;
43 namespace web_navigation = api::web_navigation;
45 namespace {
47 typedef std::map<content::WebContents*, WebNavigationTabObserver*>
48 TabObserverMap;
49 static base::LazyInstance<TabObserverMap> g_tab_observer =
50 LAZY_INSTANCE_INITIALIZER;
52 } // namespace
54 // WebNavigtionEventRouter -------------------------------------------
56 WebNavigationEventRouter::PendingWebContents::PendingWebContents()
57 : source_web_contents(NULL),
58 source_frame_host(NULL),
59 target_web_contents(NULL),
60 target_url() {
63 WebNavigationEventRouter::PendingWebContents::PendingWebContents(
64 content::WebContents* source_web_contents,
65 content::RenderFrameHost* source_frame_host,
66 content::WebContents* target_web_contents,
67 const GURL& target_url)
68 : source_web_contents(source_web_contents),
69 source_frame_host(source_frame_host),
70 target_web_contents(target_web_contents),
71 target_url(target_url) {
74 WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}
76 WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile)
77 : profile_(profile) {
78 CHECK(registrar_.IsEmpty());
79 registrar_.Add(this,
80 chrome::NOTIFICATION_RETARGETING,
81 content::NotificationService::AllSources());
82 registrar_.Add(this,
83 chrome::NOTIFICATION_TAB_ADDED,
84 content::NotificationService::AllSources());
85 registrar_.Add(this,
86 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
87 content::NotificationService::AllSources());
89 BrowserList::AddObserver(this);
90 for (chrome::BrowserIterator it; !it.done(); it.Next())
91 OnBrowserAdded(*it);
94 WebNavigationEventRouter::~WebNavigationEventRouter() {
95 for (chrome::BrowserIterator it; !it.done(); it.Next())
96 OnBrowserRemoved(*it);
97 BrowserList::RemoveObserver(this);
100 void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
101 if (!profile_->IsSameProfile(browser->profile()))
102 return;
103 browser->tab_strip_model()->AddObserver(this);
106 void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
107 if (!profile_->IsSameProfile(browser->profile()))
108 return;
109 browser->tab_strip_model()->RemoveObserver(this);
112 void WebNavigationEventRouter::TabReplacedAt(
113 TabStripModel* tab_strip_model,
114 content::WebContents* old_contents,
115 content::WebContents* new_contents,
116 int index) {
117 WebNavigationTabObserver* tab_observer =
118 WebNavigationTabObserver::Get(old_contents);
119 if (!tab_observer) {
120 // If you hit this DCHECK(), please add reproduction steps to
121 // http://crbug.com/109464.
122 DCHECK(GetViewType(old_contents) != VIEW_TYPE_TAB_CONTENTS);
123 return;
125 const FrameNavigationState& frame_navigation_state =
126 tab_observer->frame_navigation_state();
128 if (!frame_navigation_state.IsValidUrl(old_contents->GetURL()) ||
129 !frame_navigation_state.IsValidUrl(new_contents->GetURL()))
130 return;
132 helpers::DispatchOnTabReplaced(old_contents, profile_, new_contents);
135 void WebNavigationEventRouter::Observe(
136 int type,
137 const content::NotificationSource& source,
138 const content::NotificationDetails& details) {
139 switch (type) {
140 case chrome::NOTIFICATION_RETARGETING: {
141 Profile* profile = content::Source<Profile>(source).ptr();
142 if (profile->GetOriginalProfile() == profile_) {
143 Retargeting(
144 content::Details<const RetargetingDetails>(details).ptr());
146 break;
149 case chrome::NOTIFICATION_TAB_ADDED:
150 TabAdded(content::Details<content::WebContents>(details).ptr());
151 break;
153 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
154 TabDestroyed(content::Source<content::WebContents>(source).ptr());
155 break;
157 default:
158 NOTREACHED();
162 void WebNavigationEventRouter::Retargeting(const RetargetingDetails* details) {
163 if (details->source_render_frame_id == 0)
164 return;
165 WebNavigationTabObserver* tab_observer =
166 WebNavigationTabObserver::Get(details->source_web_contents);
167 if (!tab_observer) {
168 // If you hit this DCHECK(), please add reproduction steps to
169 // http://crbug.com/109464.
170 DCHECK(GetViewType(details->source_web_contents) != VIEW_TYPE_TAB_CONTENTS);
171 return;
173 const FrameNavigationState& frame_navigation_state =
174 tab_observer->frame_navigation_state();
176 content::RenderFrameHost* frame_host = content::RenderFrameHost::FromID(
177 details->source_web_contents->GetRenderProcessHost()->GetID(),
178 details->source_render_frame_id);
179 if (!frame_navigation_state.CanSendEvents(frame_host))
180 return;
182 // If the WebContents isn't yet inserted into a tab strip, we need to delay
183 // the extension event until the WebContents is fully initialized.
184 if (details->not_yet_in_tabstrip) {
185 pending_web_contents_[details->target_web_contents] =
186 PendingWebContents(details->source_web_contents,
187 frame_host,
188 details->target_web_contents,
189 details->target_url);
190 } else {
191 helpers::DispatchOnCreatedNavigationTarget(
192 details->source_web_contents,
193 details->target_web_contents->GetBrowserContext(),
194 frame_host,
195 details->target_web_contents,
196 details->target_url);
200 void WebNavigationEventRouter::TabAdded(content::WebContents* tab) {
201 std::map<content::WebContents*, PendingWebContents>::iterator iter =
202 pending_web_contents_.find(tab);
203 if (iter == pending_web_contents_.end())
204 return;
206 WebNavigationTabObserver* tab_observer =
207 WebNavigationTabObserver::Get(iter->second.source_web_contents);
208 if (!tab_observer) {
209 NOTREACHED();
210 return;
212 const FrameNavigationState& frame_navigation_state =
213 tab_observer->frame_navigation_state();
215 if (frame_navigation_state.CanSendEvents(iter->second.source_frame_host)) {
216 helpers::DispatchOnCreatedNavigationTarget(
217 iter->second.source_web_contents,
218 iter->second.target_web_contents->GetBrowserContext(),
219 iter->second.source_frame_host,
220 iter->second.target_web_contents,
221 iter->second.target_url);
223 pending_web_contents_.erase(iter);
226 void WebNavigationEventRouter::TabDestroyed(content::WebContents* tab) {
227 pending_web_contents_.erase(tab);
228 for (std::map<content::WebContents*, PendingWebContents>::iterator i =
229 pending_web_contents_.begin(); i != pending_web_contents_.end(); ) {
230 if (i->second.source_web_contents == tab)
231 pending_web_contents_.erase(i++);
232 else
233 ++i;
237 // WebNavigationTabObserver ------------------------------------------
239 WebNavigationTabObserver::WebNavigationTabObserver(
240 content::WebContents* web_contents)
241 : WebContentsObserver(web_contents) {
242 g_tab_observer.Get().insert(TabObserverMap::value_type(web_contents, this));
243 navigation_state_.FrameHostCreated(web_contents->GetMainFrame());
246 WebNavigationTabObserver::~WebNavigationTabObserver() {}
248 // static
249 WebNavigationTabObserver* WebNavigationTabObserver::Get(
250 content::WebContents* web_contents) {
251 TabObserverMap::iterator i = g_tab_observer.Get().find(web_contents);
252 return i == g_tab_observer.Get().end() ? NULL : i->second;
255 void WebNavigationTabObserver::RenderFrameDeleted(
256 content::RenderFrameHost* render_frame_host) {
257 if (navigation_state_.CanSendEvents(render_frame_host) &&
258 !navigation_state_.GetNavigationCompleted(render_frame_host)) {
259 helpers::DispatchOnErrorOccurred(
260 web_contents(),
261 render_frame_host,
262 navigation_state_.GetUrl(render_frame_host),
263 net::ERR_ABORTED);
264 navigation_state_.SetErrorOccurredInFrame(render_frame_host);
268 void WebNavigationTabObserver::FrameDeleted(
269 content::RenderFrameHost* render_frame_host) {
270 navigation_state_.FrameHostDeleted(render_frame_host);
273 void WebNavigationTabObserver::RenderFrameHostChanged(
274 content::RenderFrameHost* old_host,
275 content::RenderFrameHost* new_host) {
276 if (old_host) {
277 RenderFrameDeleted(old_host);
278 navigation_state_.FrameHostDeleted(old_host);
281 navigation_state_.FrameHostCreated(new_host);
284 void WebNavigationTabObserver::DidStartProvisionalLoadForFrame(
285 content::RenderFrameHost* render_frame_host,
286 const GURL& validated_url,
287 bool is_error_page,
288 bool is_iframe_srcdoc) {
289 DVLOG(2) << "DidStartProvisionalLoad("
290 << "render_frame_host=" << render_frame_host
291 << ", frame_num=" << render_frame_host->GetRoutingID()
292 << ", url=" << validated_url << ")";
293 navigation_state_.StartTrackingNavigation(render_frame_host, validated_url,
294 is_error_page, is_iframe_srcdoc);
296 if (!navigation_state_.CanSendEvents(render_frame_host))
297 return;
299 helpers::DispatchOnBeforeNavigate(
300 web_contents(),
301 render_frame_host,
302 navigation_state_.GetUrl(render_frame_host));
305 void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
306 content::RenderFrameHost* render_frame_host,
307 const GURL& url,
308 ui::PageTransition transition_type) {
309 DVLOG(2) << "DidCommitProvisionalLoad("
310 << "render_frame_host=" << render_frame_host
311 << ", frame_num=" << render_frame_host->GetRoutingID()
312 << ", url=" << url << ")";
313 bool is_reference_fragment_navigation =
314 IsReferenceFragmentNavigation(render_frame_host, url);
315 bool is_history_state_modification =
316 navigation_state_.GetNavigationCommitted(render_frame_host);
318 // Update the URL as it might have changed.
319 navigation_state_.UpdateFrame(render_frame_host, url);
320 navigation_state_.SetNavigationCommitted(render_frame_host);
322 if (!navigation_state_.CanSendEvents(render_frame_host))
323 return;
325 if (is_reference_fragment_navigation) {
326 helpers::DispatchOnCommitted(
327 web_navigation::OnReferenceFragmentUpdated::kEventName,
328 web_contents(),
329 render_frame_host,
330 navigation_state_.GetUrl(render_frame_host),
331 transition_type);
332 } else if (is_history_state_modification) {
333 helpers::DispatchOnCommitted(
334 web_navigation::OnHistoryStateUpdated::kEventName,
335 web_contents(),
336 render_frame_host,
337 navigation_state_.GetUrl(render_frame_host),
338 transition_type);
339 } else {
340 if (navigation_state_.GetIsServerRedirected(render_frame_host)) {
341 transition_type = ui::PageTransitionFromInt(
342 transition_type | ui::PAGE_TRANSITION_SERVER_REDIRECT);
344 helpers::DispatchOnCommitted(web_navigation::OnCommitted::kEventName,
345 web_contents(),
346 render_frame_host,
347 navigation_state_.GetUrl(render_frame_host),
348 transition_type);
352 void WebNavigationTabObserver::DidFailProvisionalLoad(
353 content::RenderFrameHost* render_frame_host,
354 const GURL& validated_url,
355 int error_code,
356 const base::string16& error_description) {
357 DVLOG(2) << "DidFailProvisionalLoad("
358 << "render_frame_host=" << render_frame_host
359 << ", frame_num=" << render_frame_host->GetRoutingID()
360 << ", url=" << validated_url << ")";
361 if (navigation_state_.CanSendEvents(render_frame_host)) {
362 helpers::DispatchOnErrorOccurred(
363 web_contents(),
364 render_frame_host,
365 navigation_state_.GetUrl(render_frame_host),
366 error_code);
368 navigation_state_.SetErrorOccurredInFrame(render_frame_host);
371 void WebNavigationTabObserver::DocumentLoadedInFrame(
372 content::RenderFrameHost* render_frame_host) {
373 DVLOG(2) << "DocumentLoadedInFrame("
374 << "render_frame_host=" << render_frame_host
375 << ", frame_num=" << render_frame_host->GetRoutingID() << ")";
376 if (!navigation_state_.CanSendEvents(render_frame_host))
377 return;
378 navigation_state_.SetParsingFinished(render_frame_host);
379 helpers::DispatchOnDOMContentLoaded(
380 web_contents(),
381 render_frame_host,
382 navigation_state_.GetUrl(render_frame_host));
384 if (!navigation_state_.GetNavigationCompleted(render_frame_host))
385 return;
387 // The load might already have finished by the time we finished parsing. For
388 // compatibility reasons, we artifically delay the load completed signal until
389 // after parsing was completed.
390 helpers::DispatchOnCompleted(web_contents(),
391 render_frame_host,
392 navigation_state_.GetUrl(render_frame_host));
395 void WebNavigationTabObserver::DidFinishLoad(
396 content::RenderFrameHost* render_frame_host,
397 const GURL& validated_url) {
398 DVLOG(2) << "DidFinishLoad("
399 << "render_frame_host=" << render_frame_host
400 << ", frame_num=" << render_frame_host->GetRoutingID()
401 << ", url=" << validated_url << ")";
402 // When showing replacement content, we might get load signals for frames
403 // that weren't reguarly loaded.
404 if (!navigation_state_.IsValidFrame(render_frame_host))
405 return;
406 navigation_state_.SetNavigationCompleted(render_frame_host);
407 if (!navigation_state_.CanSendEvents(render_frame_host))
408 return;
409 DCHECK(navigation_state_.GetUrl(render_frame_host) == validated_url ||
410 (navigation_state_.GetUrl(render_frame_host) ==
411 GURL(content::kAboutSrcDocURL) &&
412 validated_url == GURL(url::kAboutBlankURL)))
413 << "validated URL is " << validated_url << " but we expected "
414 << navigation_state_.GetUrl(render_frame_host);
416 // The load might already have finished by the time we finished parsing. For
417 // compatibility reasons, we artifically delay the load completed signal until
418 // after parsing was completed.
419 if (!navigation_state_.GetParsingFinished(render_frame_host))
420 return;
421 helpers::DispatchOnCompleted(web_contents(),
422 render_frame_host,
423 navigation_state_.GetUrl(render_frame_host));
426 void WebNavigationTabObserver::DidFailLoad(
427 content::RenderFrameHost* render_frame_host,
428 const GURL& validated_url,
429 int error_code,
430 const base::string16& error_description) {
431 DVLOG(2) << "DidFailLoad("
432 << "render_frame_host=" << render_frame_host
433 << ", frame_num=" << render_frame_host->GetRoutingID()
434 << ", url=" << validated_url << ")";
435 // When showing replacement content, we might get load signals for frames
436 // that weren't reguarly loaded.
437 if (!navigation_state_.IsValidFrame(render_frame_host))
438 return;
439 if (navigation_state_.CanSendEvents(render_frame_host)) {
440 helpers::DispatchOnErrorOccurred(
441 web_contents(),
442 render_frame_host,
443 navigation_state_.GetUrl(render_frame_host),
444 error_code);
446 navigation_state_.SetErrorOccurredInFrame(render_frame_host);
449 void WebNavigationTabObserver::DidGetRedirectForResourceRequest(
450 content::RenderFrameHost* render_frame_host,
451 const content::ResourceRedirectDetails& details) {
452 if (details.resource_type != content::RESOURCE_TYPE_MAIN_FRAME &&
453 details.resource_type != content::RESOURCE_TYPE_SUB_FRAME) {
454 return;
456 navigation_state_.SetIsServerRedirected(render_frame_host);
459 void WebNavigationTabObserver::DidOpenRequestedURL(
460 content::WebContents* new_contents,
461 content::RenderFrameHost* source_render_frame_host,
462 const GURL& url,
463 const content::Referrer& referrer,
464 WindowOpenDisposition disposition,
465 ui::PageTransition transition) {
466 if (!navigation_state_.CanSendEvents(source_render_frame_host))
467 return;
469 // We only send the onCreatedNavigationTarget if we end up creating a new
470 // window.
471 if (disposition != SINGLETON_TAB &&
472 disposition != NEW_FOREGROUND_TAB &&
473 disposition != NEW_BACKGROUND_TAB &&
474 disposition != NEW_POPUP &&
475 disposition != NEW_WINDOW &&
476 disposition != OFF_THE_RECORD)
477 return;
479 helpers::DispatchOnCreatedNavigationTarget(web_contents(),
480 new_contents->GetBrowserContext(),
481 source_render_frame_host,
482 new_contents,
483 url);
486 void WebNavigationTabObserver::WebContentsDestroyed() {
487 g_tab_observer.Get().erase(web_contents());
488 registrar_.RemoveAll();
491 // See also NavigationController::IsURLInPageNavigation.
492 bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
493 content::RenderFrameHost* render_frame_host,
494 const GURL& url) {
495 GURL existing_url = navigation_state_.GetUrl(render_frame_host);
496 if (existing_url == url)
497 return false;
499 url::Replacements<char> replacements;
500 replacements.ClearRef();
501 return existing_url.ReplaceComponents(replacements) ==
502 url.ReplaceComponents(replacements);
505 bool WebNavigationGetFrameFunction::RunSync() {
506 scoped_ptr<GetFrame::Params> params(GetFrame::Params::Create(*args_));
507 EXTENSION_FUNCTION_VALIDATE(params.get());
508 int tab_id = params->details.tab_id;
509 int frame_id = params->details.frame_id;
510 int process_id = params->details.process_id;
512 SetResult(base::Value::CreateNullValue());
514 content::WebContents* web_contents;
515 if (!ExtensionTabUtil::GetTabById(tab_id,
516 GetProfile(),
517 include_incognito(),
518 NULL,
519 NULL,
520 &web_contents,
521 NULL) ||
522 !web_contents) {
523 return true;
526 WebNavigationTabObserver* observer =
527 WebNavigationTabObserver::Get(web_contents);
528 DCHECK(observer);
530 const FrameNavigationState& frame_navigation_state =
531 observer->frame_navigation_state();
533 content::RenderFrameHost* render_frame_host =
534 frame_id == 0 ? web_contents->GetMainFrame()
535 : content::RenderFrameHost::FromID(process_id, frame_id);
536 if (!frame_navigation_state.IsValidFrame(render_frame_host))
537 return true;
539 GURL frame_url = frame_navigation_state.GetUrl(render_frame_host);
540 if (!frame_navigation_state.IsValidUrl(frame_url))
541 return true;
543 GetFrame::Results::Details frame_details;
544 frame_details.url = frame_url.spec();
545 frame_details.error_occurred =
546 frame_navigation_state.GetErrorOccurredInFrame(render_frame_host);
547 frame_details.parent_frame_id =
548 helpers::GetFrameId(render_frame_host->GetParent());
549 results_ = GetFrame::Results::Create(frame_details);
550 return true;
553 bool WebNavigationGetAllFramesFunction::RunSync() {
554 scoped_ptr<GetAllFrames::Params> params(GetAllFrames::Params::Create(*args_));
555 EXTENSION_FUNCTION_VALIDATE(params.get());
556 int tab_id = params->details.tab_id;
558 SetResult(base::Value::CreateNullValue());
560 content::WebContents* web_contents;
561 if (!ExtensionTabUtil::GetTabById(tab_id,
562 GetProfile(),
563 include_incognito(),
564 NULL,
565 NULL,
566 &web_contents,
567 NULL) ||
568 !web_contents) {
569 return true;
572 WebNavigationTabObserver* observer =
573 WebNavigationTabObserver::Get(web_contents);
574 DCHECK(observer);
576 const FrameNavigationState& navigation_state =
577 observer->frame_navigation_state();
579 std::vector<linked_ptr<GetAllFrames::Results::DetailsType> > result_list;
580 for (FrameNavigationState::const_iterator it = navigation_state.begin();
581 it != navigation_state.end(); ++it) {
582 GURL frame_url = navigation_state.GetUrl(*it);
583 if (!navigation_state.IsValidUrl(frame_url))
584 continue;
585 linked_ptr<GetAllFrames::Results::DetailsType> frame(
586 new GetAllFrames::Results::DetailsType());
587 frame->url = frame_url.spec();
588 frame->frame_id = helpers::GetFrameId(*it);
589 frame->parent_frame_id = helpers::GetFrameId((*it)->GetParent());
590 frame->process_id = (*it)->GetProcess()->GetID();
591 frame->error_occurred = navigation_state.GetErrorOccurredInFrame(*it);
592 result_list.push_back(frame);
594 results_ = GetAllFrames::Results::Create(result_list);
595 return true;
598 WebNavigationAPI::WebNavigationAPI(content::BrowserContext* context)
599 : browser_context_(context) {
600 EventRouter* event_router = EventRouter::Get(browser_context_);
601 event_router->RegisterObserver(this,
602 web_navigation::OnBeforeNavigate::kEventName);
603 event_router->RegisterObserver(this, web_navigation::OnCommitted::kEventName);
604 event_router->RegisterObserver(this, web_navigation::OnCompleted::kEventName);
605 event_router->RegisterObserver(
606 this, web_navigation::OnCreatedNavigationTarget::kEventName);
607 event_router->RegisterObserver(
608 this, web_navigation::OnDOMContentLoaded::kEventName);
609 event_router->RegisterObserver(
610 this, web_navigation::OnHistoryStateUpdated::kEventName);
611 event_router->RegisterObserver(this,
612 web_navigation::OnErrorOccurred::kEventName);
613 event_router->RegisterObserver(
614 this, web_navigation::OnReferenceFragmentUpdated::kEventName);
615 event_router->RegisterObserver(this,
616 web_navigation::OnTabReplaced::kEventName);
619 WebNavigationAPI::~WebNavigationAPI() {
622 void WebNavigationAPI::Shutdown() {
623 EventRouter::Get(browser_context_)->UnregisterObserver(this);
626 static base::LazyInstance<BrowserContextKeyedAPIFactory<WebNavigationAPI> >
627 g_factory = LAZY_INSTANCE_INITIALIZER;
629 // static
630 BrowserContextKeyedAPIFactory<WebNavigationAPI>*
631 WebNavigationAPI::GetFactoryInstance() {
632 return g_factory.Pointer();
635 void WebNavigationAPI::OnListenerAdded(const EventListenerInfo& details) {
636 web_navigation_event_router_.reset(new WebNavigationEventRouter(
637 Profile::FromBrowserContext(browser_context_)));
638 EventRouter::Get(browser_context_)->UnregisterObserver(this);
641 } // namespace extensions