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 if (!FrameNavigationState::IsValidUrl(old_contents
->GetURL()) ||
126 !FrameNavigationState::IsValidUrl(new_contents
->GetURL()))
129 helpers::DispatchOnTabReplaced(old_contents
, profile_
, new_contents
);
132 void WebNavigationEventRouter::Observe(
134 const content::NotificationSource
& source
,
135 const content::NotificationDetails
& details
) {
137 case chrome::NOTIFICATION_RETARGETING
: {
138 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
139 if (profile
->GetOriginalProfile() == profile_
) {
141 content::Details
<const RetargetingDetails
>(details
).ptr());
146 case chrome::NOTIFICATION_TAB_ADDED
:
147 TabAdded(content::Details
<content::WebContents
>(details
).ptr());
150 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED
:
151 TabDestroyed(content::Source
<content::WebContents
>(source
).ptr());
159 void WebNavigationEventRouter::Retargeting(const RetargetingDetails
* details
) {
160 if (details
->source_render_frame_id
== 0)
162 WebNavigationTabObserver
* tab_observer
=
163 WebNavigationTabObserver::Get(details
->source_web_contents
);
165 // If you hit this DCHECK(), please add reproduction steps to
166 // http://crbug.com/109464.
167 DCHECK(GetViewType(details
->source_web_contents
) != VIEW_TYPE_TAB_CONTENTS
);
170 const FrameNavigationState
& frame_navigation_state
=
171 tab_observer
->frame_navigation_state();
173 content::RenderFrameHost
* frame_host
= content::RenderFrameHost::FromID(
174 details
->source_web_contents
->GetRenderProcessHost()->GetID(),
175 details
->source_render_frame_id
);
176 if (!frame_navigation_state
.CanSendEvents(frame_host
))
179 // If the WebContents isn't yet inserted into a tab strip, we need to delay
180 // the extension event until the WebContents is fully initialized.
181 if (details
->not_yet_in_tabstrip
) {
182 pending_web_contents_
[details
->target_web_contents
] =
183 PendingWebContents(details
->source_web_contents
,
185 details
->target_web_contents
,
186 details
->target_url
);
188 helpers::DispatchOnCreatedNavigationTarget(
189 details
->source_web_contents
,
190 details
->target_web_contents
->GetBrowserContext(),
192 details
->target_web_contents
,
193 details
->target_url
);
197 void WebNavigationEventRouter::TabAdded(content::WebContents
* tab
) {
198 std::map
<content::WebContents
*, PendingWebContents
>::iterator iter
=
199 pending_web_contents_
.find(tab
);
200 if (iter
== pending_web_contents_
.end())
203 WebNavigationTabObserver
* tab_observer
=
204 WebNavigationTabObserver::Get(iter
->second
.source_web_contents
);
209 const FrameNavigationState
& frame_navigation_state
=
210 tab_observer
->frame_navigation_state();
212 if (frame_navigation_state
.CanSendEvents(iter
->second
.source_frame_host
)) {
213 helpers::DispatchOnCreatedNavigationTarget(
214 iter
->second
.source_web_contents
,
215 iter
->second
.target_web_contents
->GetBrowserContext(),
216 iter
->second
.source_frame_host
,
217 iter
->second
.target_web_contents
,
218 iter
->second
.target_url
);
220 pending_web_contents_
.erase(iter
);
223 void WebNavigationEventRouter::TabDestroyed(content::WebContents
* tab
) {
224 pending_web_contents_
.erase(tab
);
225 for (std::map
<content::WebContents
*, PendingWebContents
>::iterator i
=
226 pending_web_contents_
.begin(); i
!= pending_web_contents_
.end(); ) {
227 if (i
->second
.source_web_contents
== tab
)
228 pending_web_contents_
.erase(i
++);
234 // WebNavigationTabObserver ------------------------------------------
236 WebNavigationTabObserver::WebNavigationTabObserver(
237 content::WebContents
* web_contents
)
238 : WebContentsObserver(web_contents
) {
239 g_tab_observer
.Get().insert(TabObserverMap::value_type(web_contents
, this));
240 navigation_state_
.FrameHostCreated(web_contents
->GetMainFrame());
243 WebNavigationTabObserver::~WebNavigationTabObserver() {}
246 WebNavigationTabObserver
* WebNavigationTabObserver::Get(
247 content::WebContents
* web_contents
) {
248 TabObserverMap::iterator i
= g_tab_observer
.Get().find(web_contents
);
249 return i
== g_tab_observer
.Get().end() ? NULL
: i
->second
;
252 void WebNavigationTabObserver::RenderFrameDeleted(
253 content::RenderFrameHost
* render_frame_host
) {
254 if (navigation_state_
.CanSendEvents(render_frame_host
) &&
255 !navigation_state_
.GetNavigationCompleted(render_frame_host
)) {
256 helpers::DispatchOnErrorOccurred(
259 navigation_state_
.GetUrl(render_frame_host
),
261 navigation_state_
.SetErrorOccurredInFrame(render_frame_host
);
265 void WebNavigationTabObserver::FrameDeleted(
266 content::RenderFrameHost
* render_frame_host
) {
267 navigation_state_
.FrameHostDeleted(render_frame_host
);
270 void WebNavigationTabObserver::RenderFrameHostChanged(
271 content::RenderFrameHost
* old_host
,
272 content::RenderFrameHost
* new_host
) {
274 RenderFrameDeleted(old_host
);
275 navigation_state_
.FrameHostDeleted(old_host
);
278 navigation_state_
.FrameHostCreated(new_host
);
281 void WebNavigationTabObserver::DidStartProvisionalLoadForFrame(
282 content::RenderFrameHost
* render_frame_host
,
283 const GURL
& validated_url
,
285 bool is_iframe_srcdoc
) {
286 DVLOG(2) << "DidStartProvisionalLoad("
287 << "render_frame_host=" << render_frame_host
288 << ", frame_num=" << render_frame_host
->GetRoutingID()
289 << ", url=" << validated_url
<< ")";
290 navigation_state_
.StartTrackingNavigation(render_frame_host
, validated_url
,
291 is_error_page
, is_iframe_srcdoc
);
293 if (!navigation_state_
.CanSendEvents(render_frame_host
))
296 helpers::DispatchOnBeforeNavigate(
299 navigation_state_
.GetUrl(render_frame_host
));
302 void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
303 content::RenderFrameHost
* render_frame_host
,
305 ui::PageTransition transition_type
) {
306 DVLOG(2) << "DidCommitProvisionalLoad("
307 << "render_frame_host=" << render_frame_host
308 << ", frame_num=" << render_frame_host
->GetRoutingID()
309 << ", url=" << url
<< ")";
310 bool is_reference_fragment_navigation
=
311 IsReferenceFragmentNavigation(render_frame_host
, url
);
312 bool is_history_state_modification
=
313 navigation_state_
.GetNavigationCommitted(render_frame_host
);
315 // Update the URL as it might have changed.
316 navigation_state_
.UpdateFrame(render_frame_host
, url
);
317 navigation_state_
.SetNavigationCommitted(render_frame_host
);
319 if (!navigation_state_
.CanSendEvents(render_frame_host
))
322 events::HistogramValue histogram_value
= events::UNKNOWN
;
323 std::string event_name
;
324 if (is_reference_fragment_navigation
) {
325 histogram_value
= events::WEB_NAVIGATION_ON_REFERENCE_FRAGMENT_UPDATED
;
326 event_name
= web_navigation::OnReferenceFragmentUpdated::kEventName
;
327 } else if (is_history_state_modification
) {
328 histogram_value
= events::WEB_NAVIGATION_ON_HISTORY_STATE_UPDATED
;
329 event_name
= web_navigation::OnHistoryStateUpdated::kEventName
;
331 if (navigation_state_
.GetIsServerRedirected(render_frame_host
)) {
332 transition_type
= ui::PageTransitionFromInt(
333 transition_type
| ui::PAGE_TRANSITION_SERVER_REDIRECT
);
335 histogram_value
= events::WEB_NAVIGATION_ON_COMMITTED
;
336 event_name
= web_navigation::OnCommitted::kEventName
;
338 helpers::DispatchOnCommitted(
339 histogram_value
, event_name
, web_contents(), render_frame_host
,
340 navigation_state_
.GetUrl(render_frame_host
), transition_type
);
343 void WebNavigationTabObserver::DidFailProvisionalLoad(
344 content::RenderFrameHost
* render_frame_host
,
345 const GURL
& validated_url
,
347 const base::string16
& error_description
,
348 bool was_ignored_by_handler
) {
349 DVLOG(2) << "DidFailProvisionalLoad("
350 << "render_frame_host=" << render_frame_host
351 << ", frame_num=" << render_frame_host
->GetRoutingID()
352 << ", url=" << validated_url
<< ")";
353 if (navigation_state_
.CanSendEvents(render_frame_host
)) {
354 helpers::DispatchOnErrorOccurred(
357 navigation_state_
.GetUrl(render_frame_host
),
360 navigation_state_
.SetErrorOccurredInFrame(render_frame_host
);
363 void WebNavigationTabObserver::DocumentLoadedInFrame(
364 content::RenderFrameHost
* render_frame_host
) {
365 DVLOG(2) << "DocumentLoadedInFrame("
366 << "render_frame_host=" << render_frame_host
367 << ", frame_num=" << render_frame_host
->GetRoutingID() << ")";
368 if (!navigation_state_
.CanSendEvents(render_frame_host
))
370 navigation_state_
.SetParsingFinished(render_frame_host
);
371 helpers::DispatchOnDOMContentLoaded(
374 navigation_state_
.GetUrl(render_frame_host
));
376 if (!navigation_state_
.GetNavigationCompleted(render_frame_host
))
379 // The load might already have finished by the time we finished parsing. For
380 // compatibility reasons, we artifically delay the load completed signal until
381 // after parsing was completed.
382 helpers::DispatchOnCompleted(web_contents(),
384 navigation_state_
.GetUrl(render_frame_host
));
387 void WebNavigationTabObserver::DidFinishLoad(
388 content::RenderFrameHost
* render_frame_host
,
389 const GURL
& validated_url
) {
390 DVLOG(2) << "DidFinishLoad("
391 << "render_frame_host=" << render_frame_host
392 << ", frame_num=" << render_frame_host
->GetRoutingID()
393 << ", url=" << validated_url
<< ")";
394 // When showing replacement content, we might get load signals for frames
395 // that weren't reguarly loaded.
396 if (!navigation_state_
.IsValidFrame(render_frame_host
))
398 navigation_state_
.SetNavigationCompleted(render_frame_host
);
399 if (!navigation_state_
.CanSendEvents(render_frame_host
))
401 DCHECK(navigation_state_
.GetUrl(render_frame_host
) == validated_url
||
402 (navigation_state_
.GetUrl(render_frame_host
) ==
403 GURL(content::kAboutSrcDocURL
) &&
404 validated_url
== GURL(url::kAboutBlankURL
)))
405 << "validated URL is " << validated_url
<< " but we expected "
406 << navigation_state_
.GetUrl(render_frame_host
);
408 // The load might already have finished by the time we finished parsing. For
409 // compatibility reasons, we artifically delay the load completed signal until
410 // after parsing was completed.
411 if (!navigation_state_
.GetParsingFinished(render_frame_host
))
413 helpers::DispatchOnCompleted(web_contents(),
415 navigation_state_
.GetUrl(render_frame_host
));
418 void WebNavigationTabObserver::DidFailLoad(
419 content::RenderFrameHost
* render_frame_host
,
420 const GURL
& validated_url
,
422 const base::string16
& error_description
,
423 bool was_ignored_by_handler
) {
424 DVLOG(2) << "DidFailLoad("
425 << "render_frame_host=" << render_frame_host
426 << ", frame_num=" << render_frame_host
->GetRoutingID()
427 << ", url=" << validated_url
<< ")";
428 // When showing replacement content, we might get load signals for frames
429 // that weren't reguarly loaded.
430 if (!navigation_state_
.IsValidFrame(render_frame_host
))
432 if (navigation_state_
.CanSendEvents(render_frame_host
)) {
433 helpers::DispatchOnErrorOccurred(
436 navigation_state_
.GetUrl(render_frame_host
),
439 navigation_state_
.SetErrorOccurredInFrame(render_frame_host
);
442 void WebNavigationTabObserver::DidGetRedirectForResourceRequest(
443 content::RenderFrameHost
* render_frame_host
,
444 const content::ResourceRedirectDetails
& details
) {
445 if (details
.resource_type
!= content::RESOURCE_TYPE_MAIN_FRAME
&&
446 details
.resource_type
!= content::RESOURCE_TYPE_SUB_FRAME
) {
449 navigation_state_
.SetIsServerRedirected(render_frame_host
);
452 void WebNavigationTabObserver::DidOpenRequestedURL(
453 content::WebContents
* new_contents
,
454 content::RenderFrameHost
* source_render_frame_host
,
456 const content::Referrer
& referrer
,
457 WindowOpenDisposition disposition
,
458 ui::PageTransition transition
) {
459 if (!navigation_state_
.CanSendEvents(source_render_frame_host
))
462 // We only send the onCreatedNavigationTarget if we end up creating a new
464 if (disposition
!= SINGLETON_TAB
&&
465 disposition
!= NEW_FOREGROUND_TAB
&&
466 disposition
!= NEW_BACKGROUND_TAB
&&
467 disposition
!= NEW_POPUP
&&
468 disposition
!= NEW_WINDOW
&&
469 disposition
!= OFF_THE_RECORD
)
472 helpers::DispatchOnCreatedNavigationTarget(web_contents(),
473 new_contents
->GetBrowserContext(),
474 source_render_frame_host
,
479 void WebNavigationTabObserver::WebContentsDestroyed() {
480 g_tab_observer
.Get().erase(web_contents());
481 registrar_
.RemoveAll();
484 // See also NavigationController::IsURLInPageNavigation.
485 bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
486 content::RenderFrameHost
* render_frame_host
,
488 GURL existing_url
= navigation_state_
.GetUrl(render_frame_host
);
489 if (existing_url
== url
)
492 url::Replacements
<char> replacements
;
493 replacements
.ClearRef();
494 return existing_url
.ReplaceComponents(replacements
) ==
495 url
.ReplaceComponents(replacements
);
498 bool WebNavigationGetFrameFunction::RunSync() {
499 scoped_ptr
<GetFrame::Params
> params(GetFrame::Params::Create(*args_
));
500 EXTENSION_FUNCTION_VALIDATE(params
.get());
501 int tab_id
= params
->details
.tab_id
;
502 int frame_id
= params
->details
.frame_id
;
503 int process_id
= params
->details
.process_id
;
505 SetResult(base::Value::CreateNullValue());
507 content::WebContents
* web_contents
;
508 if (!ExtensionTabUtil::GetTabById(tab_id
,
519 WebNavigationTabObserver
* observer
=
520 WebNavigationTabObserver::Get(web_contents
);
523 const FrameNavigationState
& frame_navigation_state
=
524 observer
->frame_navigation_state();
526 content::RenderFrameHost
* render_frame_host
=
527 frame_id
== 0 ? web_contents
->GetMainFrame()
528 : content::RenderFrameHost::FromID(process_id
, frame_id
);
529 if (!frame_navigation_state
.IsValidFrame(render_frame_host
))
532 GURL frame_url
= frame_navigation_state
.GetUrl(render_frame_host
);
533 if (!frame_navigation_state
.IsValidUrl(frame_url
))
536 GetFrame::Results::Details frame_details
;
537 frame_details
.url
= frame_url
.spec();
538 frame_details
.error_occurred
=
539 frame_navigation_state
.GetErrorOccurredInFrame(render_frame_host
);
540 frame_details
.parent_frame_id
=
541 helpers::GetFrameId(render_frame_host
->GetParent());
542 results_
= GetFrame::Results::Create(frame_details
);
546 bool WebNavigationGetAllFramesFunction::RunSync() {
547 scoped_ptr
<GetAllFrames::Params
> params(GetAllFrames::Params::Create(*args_
));
548 EXTENSION_FUNCTION_VALIDATE(params
.get());
549 int tab_id
= params
->details
.tab_id
;
551 SetResult(base::Value::CreateNullValue());
553 content::WebContents
* web_contents
;
554 if (!ExtensionTabUtil::GetTabById(tab_id
,
565 WebNavigationTabObserver
* observer
=
566 WebNavigationTabObserver::Get(web_contents
);
569 const FrameNavigationState
& navigation_state
=
570 observer
->frame_navigation_state();
572 std::vector
<linked_ptr
<GetAllFrames::Results::DetailsType
> > result_list
;
573 for (FrameNavigationState::const_iterator it
= navigation_state
.begin();
574 it
!= navigation_state
.end(); ++it
) {
575 GURL frame_url
= navigation_state
.GetUrl(*it
);
576 if (!navigation_state
.IsValidUrl(frame_url
))
578 linked_ptr
<GetAllFrames::Results::DetailsType
> frame(
579 new GetAllFrames::Results::DetailsType());
580 frame
->url
= frame_url
.spec();
581 frame
->frame_id
= helpers::GetFrameId(*it
);
582 frame
->parent_frame_id
= helpers::GetFrameId((*it
)->GetParent());
583 frame
->process_id
= (*it
)->GetProcess()->GetID();
584 frame
->error_occurred
= navigation_state
.GetErrorOccurredInFrame(*it
);
585 result_list
.push_back(frame
);
587 results_
= GetAllFrames::Results::Create(result_list
);
591 WebNavigationAPI::WebNavigationAPI(content::BrowserContext
* context
)
592 : browser_context_(context
) {
593 EventRouter
* event_router
= EventRouter::Get(browser_context_
);
594 event_router
->RegisterObserver(this,
595 web_navigation::OnBeforeNavigate::kEventName
);
596 event_router
->RegisterObserver(this, web_navigation::OnCommitted::kEventName
);
597 event_router
->RegisterObserver(this, web_navigation::OnCompleted::kEventName
);
598 event_router
->RegisterObserver(
599 this, web_navigation::OnCreatedNavigationTarget::kEventName
);
600 event_router
->RegisterObserver(
601 this, web_navigation::OnDOMContentLoaded::kEventName
);
602 event_router
->RegisterObserver(
603 this, web_navigation::OnHistoryStateUpdated::kEventName
);
604 event_router
->RegisterObserver(this,
605 web_navigation::OnErrorOccurred::kEventName
);
606 event_router
->RegisterObserver(
607 this, web_navigation::OnReferenceFragmentUpdated::kEventName
);
608 event_router
->RegisterObserver(this,
609 web_navigation::OnTabReplaced::kEventName
);
612 WebNavigationAPI::~WebNavigationAPI() {
615 void WebNavigationAPI::Shutdown() {
616 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
619 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<WebNavigationAPI
> >
620 g_factory
= LAZY_INSTANCE_INITIALIZER
;
623 BrowserContextKeyedAPIFactory
<WebNavigationAPI
>*
624 WebNavigationAPI::GetFactoryInstance() {
625 return g_factory
.Pointer();
628 void WebNavigationAPI::OnListenerAdded(const EventListenerInfo
& details
) {
629 web_navigation_event_router_
.reset(new WebNavigationEventRouter(
630 Profile::FromBrowserContext(browser_context_
)));
631 EventRouter::Get(browser_context_
)->UnregisterObserver(this);
634 } // namespace extensions