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
;
47 typedef std::map
<content::WebContents
*, WebNavigationTabObserver
*>
49 static base::LazyInstance
<TabObserverMap
> g_tab_observer
=
50 LAZY_INSTANCE_INITIALIZER
;
54 // WebNavigtionEventRouter -------------------------------------------
56 WebNavigationEventRouter::PendingWebContents::PendingWebContents()
57 : source_web_contents(NULL
),
58 source_frame_host(NULL
),
59 target_web_contents(NULL
),
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
)
78 CHECK(registrar_
.IsEmpty());
80 chrome::NOTIFICATION_RETARGETING
,
81 content::NotificationService::AllSources());
83 chrome::NOTIFICATION_TAB_ADDED
,
84 content::NotificationService::AllSources());
86 content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
87 content::NotificationService::AllSources());
89 BrowserList::AddObserver(this);
90 for (chrome::BrowserIterator it
; !it
.done(); it
.Next())
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()))
103 browser
->tab_strip_model()->AddObserver(this);
106 void WebNavigationEventRouter::OnBrowserRemoved(Browser
* browser
) {
107 if (!profile_
->IsSameProfile(browser
->profile()))
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
,
117 WebNavigationTabObserver
* tab_observer
=
118 WebNavigationTabObserver::Get(old_contents
);
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
);
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()))
132 helpers::DispatchOnTabReplaced(old_contents
, profile_
, new_contents
);
135 void WebNavigationEventRouter::Observe(
137 const content::NotificationSource
& source
,
138 const content::NotificationDetails
& details
) {
140 case chrome::NOTIFICATION_RETARGETING
: {
141 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
142 if (profile
->GetOriginalProfile() == profile_
) {
144 content::Details
<const RetargetingDetails
>(details
).ptr());
149 case chrome::NOTIFICATION_TAB_ADDED
:
150 TabAdded(content::Details
<content::WebContents
>(details
).ptr());
153 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED
:
154 TabDestroyed(content::Source
<content::WebContents
>(source
).ptr());
162 void WebNavigationEventRouter::Retargeting(const RetargetingDetails
* details
) {
163 if (details
->source_render_frame_id
== 0)
165 WebNavigationTabObserver
* tab_observer
=
166 WebNavigationTabObserver::Get(details
->source_web_contents
);
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
);
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
))
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
,
188 details
->target_web_contents
,
189 details
->target_url
);
191 helpers::DispatchOnCreatedNavigationTarget(
192 details
->source_web_contents
,
193 details
->target_web_contents
->GetBrowserContext(),
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())
206 WebNavigationTabObserver
* tab_observer
=
207 WebNavigationTabObserver::Get(iter
->second
.source_web_contents
);
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
++);
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() {}
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(
262 navigation_state_
.GetUrl(render_frame_host
),
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
) {
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
,
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
))
299 helpers::DispatchOnBeforeNavigate(
302 navigation_state_
.GetUrl(render_frame_host
));
305 void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
306 content::RenderFrameHost
* render_frame_host
,
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
))
325 if (is_reference_fragment_navigation
) {
326 helpers::DispatchOnCommitted(
327 web_navigation::OnReferenceFragmentUpdated::kEventName
,
330 navigation_state_
.GetUrl(render_frame_host
),
332 } else if (is_history_state_modification
) {
333 helpers::DispatchOnCommitted(
334 web_navigation::OnHistoryStateUpdated::kEventName
,
337 navigation_state_
.GetUrl(render_frame_host
),
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
,
347 navigation_state_
.GetUrl(render_frame_host
),
352 void WebNavigationTabObserver::DidFailProvisionalLoad(
353 content::RenderFrameHost
* render_frame_host
,
354 const GURL
& validated_url
,
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(
365 navigation_state_
.GetUrl(render_frame_host
),
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
))
378 navigation_state_
.SetParsingFinished(render_frame_host
);
379 helpers::DispatchOnDOMContentLoaded(
382 navigation_state_
.GetUrl(render_frame_host
));
384 if (!navigation_state_
.GetNavigationCompleted(render_frame_host
))
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(),
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
))
406 navigation_state_
.SetNavigationCompleted(render_frame_host
);
407 if (!navigation_state_
.CanSendEvents(render_frame_host
))
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
))
421 helpers::DispatchOnCompleted(web_contents(),
423 navigation_state_
.GetUrl(render_frame_host
));
426 void WebNavigationTabObserver::DidFailLoad(
427 content::RenderFrameHost
* render_frame_host
,
428 const GURL
& validated_url
,
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
))
439 if (navigation_state_
.CanSendEvents(render_frame_host
)) {
440 helpers::DispatchOnErrorOccurred(
443 navigation_state_
.GetUrl(render_frame_host
),
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
) {
456 navigation_state_
.SetIsServerRedirected(render_frame_host
);
459 void WebNavigationTabObserver::DidOpenRequestedURL(
460 content::WebContents
* new_contents
,
461 content::RenderFrameHost
* source_render_frame_host
,
463 const content::Referrer
& referrer
,
464 WindowOpenDisposition disposition
,
465 ui::PageTransition transition
) {
466 if (!navigation_state_
.CanSendEvents(source_render_frame_host
))
469 // We only send the onCreatedNavigationTarget if we end up creating a new
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
)
479 helpers::DispatchOnCreatedNavigationTarget(web_contents(),
480 new_contents
->GetBrowserContext(),
481 source_render_frame_host
,
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
,
495 GURL existing_url
= navigation_state_
.GetUrl(render_frame_host
);
496 if (existing_url
== url
)
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
,
526 WebNavigationTabObserver
* observer
=
527 WebNavigationTabObserver::Get(web_contents
);
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
))
539 GURL frame_url
= frame_navigation_state
.GetUrl(render_frame_host
);
540 if (!frame_navigation_state
.IsValidUrl(frame_url
))
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
);
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
,
572 WebNavigationTabObserver
* observer
=
573 WebNavigationTabObserver::Get(web_contents
);
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
))
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
);
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
;
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