Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / web_navigation / web_navigation_api.cc
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.
4
5 // Implements the Chrome Extensions WebNavigation API.
6
7 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api.h"
8
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_process_host.h"
24 #include "content/public/browser/render_view_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/extension_system.h"
30 #include "extensions/browser/view_type_utils.h"
31 #include "net/base/net_errors.h"
32
33 namespace GetFrame = extensions::api::web_navigation::GetFrame;
34 namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;
35
36 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver);
37
38 namespace extensions {
39
40 #if !defined(OS_ANDROID)
41
42 namespace helpers = web_navigation_api_helpers;
43 namespace keys = web_navigation_api_constants;
44 namespace web_navigation = api::web_navigation;
45
46 namespace {
47
48 typedef std::map<content::WebContents*, WebNavigationTabObserver*>
49     TabObserverMap;
50 static base::LazyInstance<TabObserverMap> g_tab_observer =
51     LAZY_INSTANCE_INITIALIZER;
52
53 }  // namespace
54
55 // WebNavigtionEventRouter -------------------------------------------
56
57 WebNavigationEventRouter::PendingWebContents::PendingWebContents()
58     : source_web_contents(NULL),
59       source_frame_id(0),
60       source_frame_is_main_frame(false),
61       target_web_contents(NULL),
62       target_url() {
63 }
64
65 WebNavigationEventRouter::PendingWebContents::PendingWebContents(
66     content::WebContents* source_web_contents,
67     int64 source_frame_id,
68     bool source_frame_is_main_frame,
69     content::WebContents* target_web_contents,
70     const GURL& target_url)
71     : source_web_contents(source_web_contents),
72       source_frame_id(source_frame_id),
73       source_frame_is_main_frame(source_frame_is_main_frame),
74       target_web_contents(target_web_contents),
75       target_url(target_url) {
76 }
77
78 WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}
79
80 WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile)
81     : profile_(profile) {
82   CHECK(registrar_.IsEmpty());
83   registrar_.Add(this,
84                  chrome::NOTIFICATION_RETARGETING,
85                  content::NotificationService::AllSources());
86   registrar_.Add(this,
87                  chrome::NOTIFICATION_TAB_ADDED,
88                  content::NotificationService::AllSources());
89   registrar_.Add(this,
90                  content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
91                  content::NotificationService::AllSources());
92
93   BrowserList::AddObserver(this);
94   for (chrome::BrowserIterator it; !it.done(); it.Next())
95     OnBrowserAdded(*it);
96 }
97
98 WebNavigationEventRouter::~WebNavigationEventRouter() {
99   for (chrome::BrowserIterator it; !it.done(); it.Next())
100     OnBrowserRemoved(*it);
101   BrowserList::RemoveObserver(this);
102 }
103
104 void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
105   if (!profile_->IsSameProfile(browser->profile()))
106     return;
107   browser->tab_strip_model()->AddObserver(this);
108 }
109
110 void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
111   if (!profile_->IsSameProfile(browser->profile()))
112     return;
113   browser->tab_strip_model()->RemoveObserver(this);
114 }
115
116 void WebNavigationEventRouter::TabReplacedAt(
117     TabStripModel* tab_strip_model,
118     content::WebContents* old_contents,
119     content::WebContents* new_contents,
120     int index) {
121   WebNavigationTabObserver* tab_observer =
122       WebNavigationTabObserver::Get(old_contents);
123   if (!tab_observer) {
124     // If you hit this DCHECK(), please add reproduction steps to
125     // http://crbug.com/109464.
126     DCHECK(GetViewType(old_contents) != VIEW_TYPE_TAB_CONTENTS);
127     return;
128   }
129   const FrameNavigationState& frame_navigation_state =
130       tab_observer->frame_navigation_state();
131
132   if (!frame_navigation_state.IsValidUrl(old_contents->GetURL()) ||
133       !frame_navigation_state.IsValidUrl(new_contents->GetURL()))
134     return;
135
136   helpers::DispatchOnTabReplaced(old_contents, profile_, new_contents);
137 }
138
139 void WebNavigationEventRouter::Observe(
140     int type,
141     const content::NotificationSource& source,
142     const content::NotificationDetails& details) {
143   switch (type) {
144     case chrome::NOTIFICATION_RETARGETING: {
145       Profile* profile = content::Source<Profile>(source).ptr();
146       if (profile->GetOriginalProfile() == profile_) {
147         Retargeting(
148             content::Details<const RetargetingDetails>(details).ptr());
149       }
150       break;
151     }
152
153     case chrome::NOTIFICATION_TAB_ADDED:
154       TabAdded(content::Details<content::WebContents>(details).ptr());
155       break;
156
157     case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
158       TabDestroyed(content::Source<content::WebContents>(source).ptr());
159       break;
160
161     default:
162       NOTREACHED();
163   }
164 }
165
166 void WebNavigationEventRouter::Retargeting(const RetargetingDetails* details) {
167   if (details->source_frame_id == 0)
168     return;
169   WebNavigationTabObserver* tab_observer =
170       WebNavigationTabObserver::Get(details->source_web_contents);
171   if (!tab_observer) {
172     // If you hit this DCHECK(), please add reproduction steps to
173     // http://crbug.com/109464.
174     DCHECK(GetViewType(details->source_web_contents) != VIEW_TYPE_TAB_CONTENTS);
175     return;
176   }
177   const FrameNavigationState& frame_navigation_state =
178       tab_observer->frame_navigation_state();
179
180   FrameNavigationState::FrameID frame_id(
181       details->source_frame_id,
182       details->source_web_contents->GetRenderViewHost());
183   if (!frame_navigation_state.CanSendEvents(frame_id))
184     return;
185
186   // If the WebContents isn't yet inserted into a tab strip, we need to delay
187   // the extension event until the WebContents is fully initialized.
188   if (details->not_yet_in_tabstrip) {
189     pending_web_contents_[details->target_web_contents] =
190         PendingWebContents(
191             details->source_web_contents,
192             details->source_frame_id,
193             frame_navigation_state.IsMainFrame(frame_id),
194             details->target_web_contents,
195             details->target_url);
196   } else {
197     helpers::DispatchOnCreatedNavigationTarget(
198         details->source_web_contents,
199         details->target_web_contents->GetBrowserContext(),
200         details->source_frame_id,
201         frame_navigation_state.IsMainFrame(frame_id),
202         details->target_web_contents,
203         details->target_url);
204   }
205 }
206
207 void WebNavigationEventRouter::TabAdded(content::WebContents* tab) {
208   std::map<content::WebContents*, PendingWebContents>::iterator iter =
209       pending_web_contents_.find(tab);
210   if (iter == pending_web_contents_.end())
211     return;
212
213   WebNavigationTabObserver* tab_observer =
214       WebNavigationTabObserver::Get(iter->second.source_web_contents);
215   if (!tab_observer) {
216     NOTREACHED();
217     return;
218   }
219   const FrameNavigationState& frame_navigation_state =
220       tab_observer->frame_navigation_state();
221
222   FrameNavigationState::FrameID frame_id(
223       iter->second.source_frame_id,
224       iter->second.source_web_contents->GetRenderViewHost());
225   if (frame_navigation_state.CanSendEvents(frame_id)) {
226     helpers::DispatchOnCreatedNavigationTarget(
227         iter->second.source_web_contents,
228         iter->second.target_web_contents->GetBrowserContext(),
229         iter->second.source_frame_id,
230         iter->second.source_frame_is_main_frame,
231         iter->second.target_web_contents,
232         iter->second.target_url);
233   }
234   pending_web_contents_.erase(iter);
235 }
236
237 void WebNavigationEventRouter::TabDestroyed(content::WebContents* tab) {
238   pending_web_contents_.erase(tab);
239   for (std::map<content::WebContents*, PendingWebContents>::iterator i =
240            pending_web_contents_.begin(); i != pending_web_contents_.end(); ) {
241     if (i->second.source_web_contents == tab)
242       pending_web_contents_.erase(i++);
243     else
244       ++i;
245   }
246 }
247
248 // WebNavigationTabObserver ------------------------------------------
249
250 WebNavigationTabObserver::WebNavigationTabObserver(
251     content::WebContents* web_contents)
252     : WebContentsObserver(web_contents),
253       render_view_host_(NULL),
254       pending_render_view_host_(NULL) {
255   g_tab_observer.Get().insert(TabObserverMap::value_type(web_contents, this));
256   registrar_.Add(this,
257                  content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
258                  content::NotificationService::AllSources());
259 }
260
261 WebNavigationTabObserver::~WebNavigationTabObserver() {}
262
263 // static
264 WebNavigationTabObserver* WebNavigationTabObserver::Get(
265     content::WebContents* web_contents) {
266   TabObserverMap::iterator i = g_tab_observer.Get().find(web_contents);
267   return i == g_tab_observer.Get().end() ? NULL : i->second;
268 }
269
270 content::RenderViewHost* WebNavigationTabObserver::GetRenderViewHostInProcess(
271     int process_id) const {
272   if (render_view_host_ &&
273       render_view_host_->GetProcess()->GetID() == process_id) {
274     return render_view_host_;
275   }
276   if (pending_render_view_host_ &&
277       pending_render_view_host_->GetProcess()->GetID() == process_id) {
278     return pending_render_view_host_;
279   }
280   return NULL;
281 }
282
283 void WebNavigationTabObserver::Observe(
284     int type,
285     const content::NotificationSource& source,
286     const content::NotificationDetails& details) {
287   switch (type) {
288     case content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW: {
289       // The RenderView is technically not yet deleted, but the RenderViewHost
290       // already starts to filter out some IPCs. In order to not get confused,
291       // we consider the RenderView dead already now.
292       RenderViewDeleted(content::Source<content::RenderViewHost>(source).ptr());
293       break;
294     }
295
296     default:
297       NOTREACHED();
298   }
299 }
300
301 void WebNavigationTabObserver::RenderViewDeleted(
302     content::RenderViewHost* render_view_host) {
303   if (render_view_host == render_view_host_) {
304     render_view_host_ = NULL;
305     if (pending_render_view_host_) {
306       render_view_host_ = pending_render_view_host_;
307       pending_render_view_host_ = NULL;
308     }
309   } else if (render_view_host == pending_render_view_host_) {
310     pending_render_view_host_ = NULL;
311   } else {
312     return;
313   }
314   SendErrorEvents(
315       web_contents(), render_view_host, FrameNavigationState::FrameID());
316 }
317
318 void WebNavigationTabObserver::AboutToNavigateRenderView(
319     content::RenderViewHost* render_view_host) {
320   if (!render_view_host_) {
321     render_view_host_ = render_view_host;
322   } else if (render_view_host != render_view_host_) {
323     if (pending_render_view_host_) {
324       SendErrorEvents(web_contents(),
325                       pending_render_view_host_,
326                       FrameNavigationState::FrameID());
327     }
328     pending_render_view_host_ = render_view_host;
329   }
330 }
331
332 void WebNavigationTabObserver::DidStartProvisionalLoadForFrame(
333     int64 frame_num,
334     int64 parent_frame_num,
335     bool is_main_frame,
336     const GURL& validated_url,
337     bool is_error_page,
338     bool is_iframe_srcdoc,
339     content::RenderViewHost* render_view_host) {
340   DVLOG(2) << "DidStartProvisionalLoad("
341            << "render_view_host=" << render_view_host
342            << ", frame_num=" << frame_num
343            << ", url=" << validated_url << ")";
344   if (!render_view_host_)
345     render_view_host_ = render_view_host;
346   if (render_view_host != render_view_host_ &&
347       render_view_host != pending_render_view_host_)
348     return;
349
350   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
351   FrameNavigationState::FrameID parent_frame_id(
352       parent_frame_num, render_view_host);
353
354   navigation_state_.TrackFrame(frame_id,
355                                parent_frame_id,
356                                validated_url,
357                                is_main_frame,
358                                is_error_page,
359                                is_iframe_srcdoc);
360
361   if (!navigation_state_.CanSendEvents(frame_id))
362     return;
363
364   helpers::DispatchOnBeforeNavigate(
365       web_contents(),
366       render_view_host->GetProcess()->GetID(),
367       frame_num,
368       is_main_frame,
369       parent_frame_num,
370       navigation_state_.IsMainFrame(parent_frame_id),
371       navigation_state_.GetUrl(frame_id));
372 }
373
374 void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
375     int64 frame_num,
376     const base::string16& frame_unique_name,
377     bool is_main_frame,
378     const GURL& url,
379     content::PageTransition transition_type,
380     content::RenderViewHost* render_view_host) {
381   DVLOG(2) << "DidCommitProvisionalLoad("
382            << "render_view_host=" << render_view_host
383            << ", frame_num=" << frame_num
384            << ", url=" << url << ")";
385   if (render_view_host != render_view_host_ &&
386       render_view_host != pending_render_view_host_)
387     return;
388   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
389
390   bool is_reference_fragment_navigation =
391       IsReferenceFragmentNavigation(frame_id, url);
392   bool is_history_state_modification =
393       navigation_state_.GetNavigationCommitted(frame_id);
394
395   if (is_main_frame && render_view_host_ == render_view_host) {
396     // Changing the reference fragment or the history state using
397     // history.pushState or history.replaceState does not cancel on-going
398     // iframe navigations.
399     if (!is_reference_fragment_navigation && !is_history_state_modification)
400       SendErrorEvents(web_contents(), render_view_host_, frame_id);
401     if (pending_render_view_host_) {
402       SendErrorEvents(web_contents(),
403                       pending_render_view_host_,
404                       FrameNavigationState::FrameID());
405       pending_render_view_host_ = NULL;
406     }
407   } else if (pending_render_view_host_ == render_view_host) {
408     SendErrorEvents(
409         web_contents(), render_view_host_, FrameNavigationState::FrameID());
410     render_view_host_ = pending_render_view_host_;
411     pending_render_view_host_ = NULL;
412   }
413
414   // Update the URL as it might have changed.
415   navigation_state_.UpdateFrame(frame_id, url);
416   navigation_state_.SetNavigationCommitted(frame_id);
417
418   if (!navigation_state_.CanSendEvents(frame_id))
419     return;
420
421   if (is_reference_fragment_navigation) {
422     helpers::DispatchOnCommitted(
423         web_navigation::OnReferenceFragmentUpdated::kEventName,
424         web_contents(),
425         frame_num,
426         is_main_frame,
427         navigation_state_.GetUrl(frame_id),
428         transition_type);
429   } else if (is_history_state_modification) {
430     helpers::DispatchOnCommitted(
431         web_navigation::OnHistoryStateUpdated::kEventName,
432         web_contents(),
433         frame_num,
434         is_main_frame,
435         navigation_state_.GetUrl(frame_id),
436         transition_type);
437   } else {
438     if (navigation_state_.GetIsServerRedirected(frame_id)) {
439       transition_type = static_cast<content::PageTransition>(
440           transition_type | content::PAGE_TRANSITION_SERVER_REDIRECT);
441     }
442     helpers::DispatchOnCommitted(
443         web_navigation::OnCommitted::kEventName,
444         web_contents(),
445         frame_num,
446         is_main_frame,
447         navigation_state_.GetUrl(frame_id),
448         transition_type);
449   }
450 }
451
452 void WebNavigationTabObserver::DidFailProvisionalLoad(
453     int64 frame_num,
454     const base::string16& frame_unique_id,
455     bool is_main_frame,
456     const GURL& validated_url,
457     int error_code,
458     const base::string16& error_description,
459     content::RenderViewHost* render_view_host) {
460   DVLOG(2) << "DidFailProvisionalLoad("
461            << "render_view_host=" << render_view_host
462            << ", frame_num=" << frame_num
463            << ", url=" << validated_url << ")";
464   if (render_view_host != render_view_host_ &&
465       render_view_host != pending_render_view_host_)
466     return;
467   bool stop_tracking_frames = false;
468   if (render_view_host == pending_render_view_host_) {
469     pending_render_view_host_ = NULL;
470     stop_tracking_frames = true;
471   }
472
473   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
474   if (navigation_state_.CanSendEvents(frame_id)) {
475     helpers::DispatchOnErrorOccurred(
476         web_contents(),
477         render_view_host->GetProcess()->GetID(),
478         navigation_state_.GetUrl(frame_id),
479         frame_num,
480         is_main_frame,
481         error_code);
482   }
483   navigation_state_.SetErrorOccurredInFrame(frame_id);
484   if (stop_tracking_frames) {
485     navigation_state_.StopTrackingFramesInRVH(render_view_host,
486                                               FrameNavigationState::FrameID());
487   }
488 }
489
490 void WebNavigationTabObserver::DocumentLoadedInFrame(
491     int64 frame_num,
492     content::RenderViewHost* render_view_host) {
493   DVLOG(2) << "DocumentLoadedInFrame("
494            << "render_view_host=" << render_view_host
495            << ", frame_num=" << frame_num << ")";
496   if (render_view_host != render_view_host_)
497     return;
498   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
499   if (!navigation_state_.CanSendEvents(frame_id))
500     return;
501   navigation_state_.SetParsingFinished(frame_id);
502   helpers::DispatchOnDOMContentLoaded(web_contents(),
503                                       navigation_state_.GetUrl(frame_id),
504                                       navigation_state_.IsMainFrame(frame_id),
505                                       frame_num);
506
507   if (!navigation_state_.GetNavigationCompleted(frame_id))
508     return;
509
510   // The load might already have finished by the time we finished parsing. For
511   // compatibility reasons, we artifically delay the load completed signal until
512   // after parsing was completed.
513   helpers::DispatchOnCompleted(web_contents(),
514                                navigation_state_.GetUrl(frame_id),
515                                navigation_state_.IsMainFrame(frame_id),
516                                frame_num);
517 }
518
519 void WebNavigationTabObserver::DidFinishLoad(
520     int64 frame_num,
521     const GURL& validated_url,
522     bool is_main_frame,
523     content::RenderViewHost* render_view_host) {
524   DVLOG(2) << "DidFinishLoad("
525            << "render_view_host=" << render_view_host
526            << ", frame_num=" << frame_num
527            << ", url=" << validated_url << ")";
528   if (render_view_host != render_view_host_)
529     return;
530   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
531   // When showing replacement content, we might get load signals for frames
532   // that weren't reguarly loaded.
533   if (!navigation_state_.IsValidFrame(frame_id))
534     return;
535   navigation_state_.SetNavigationCompleted(frame_id);
536   if (!navigation_state_.CanSendEvents(frame_id))
537     return;
538   DCHECK(
539       navigation_state_.GetUrl(frame_id) == validated_url ||
540       (navigation_state_.GetUrl(frame_id) == GURL(content::kAboutSrcDocURL) &&
541        validated_url == GURL(content::kAboutBlankURL)))
542       << "validated URL is " << validated_url << " but we expected "
543       << navigation_state_.GetUrl(frame_id);
544   DCHECK_EQ(navigation_state_.IsMainFrame(frame_id), is_main_frame);
545
546   // The load might already have finished by the time we finished parsing. For
547   // compatibility reasons, we artifically delay the load completed signal until
548   // after parsing was completed.
549   if (!navigation_state_.GetParsingFinished(frame_id))
550     return;
551   helpers::DispatchOnCompleted(web_contents(),
552                                navigation_state_.GetUrl(frame_id),
553                                is_main_frame,
554                                frame_num);
555 }
556
557 void WebNavigationTabObserver::DidFailLoad(
558     int64 frame_num,
559     const GURL& validated_url,
560     bool is_main_frame,
561     int error_code,
562     const base::string16& error_description,
563     content::RenderViewHost* render_view_host) {
564   DVLOG(2) << "DidFailLoad("
565            << "render_view_host=" << render_view_host
566            << ", frame_num=" << frame_num
567            << ", url=" << validated_url << ")";
568   if (render_view_host != render_view_host_)
569     return;
570   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
571   // When showing replacement content, we might get load signals for frames
572   // that weren't reguarly loaded.
573   if (!navigation_state_.IsValidFrame(frame_id))
574     return;
575   if (navigation_state_.CanSendEvents(frame_id)) {
576     helpers::DispatchOnErrorOccurred(
577         web_contents(),
578         render_view_host->GetProcess()->GetID(),
579         navigation_state_.GetUrl(frame_id),
580         frame_num,
581         is_main_frame,
582         error_code);
583   }
584   navigation_state_.SetErrorOccurredInFrame(frame_id);
585 }
586
587 void WebNavigationTabObserver::DidGetRedirectForResourceRequest(
588     content::RenderViewHost* render_view_host,
589     const content::ResourceRedirectDetails& details) {
590   if (details.resource_type != ResourceType::MAIN_FRAME &&
591       details.resource_type != ResourceType::SUB_FRAME) {
592     return;
593   }
594   FrameNavigationState::FrameID frame_id(details.frame_id, render_view_host);
595   navigation_state_.SetIsServerRedirected(frame_id);
596 }
597
598 void WebNavigationTabObserver::DidOpenRequestedURL(
599     content::WebContents* new_contents,
600     const GURL& url,
601     const content::Referrer& referrer,
602     WindowOpenDisposition disposition,
603     content::PageTransition transition,
604     int64 source_frame_num) {
605   FrameNavigationState::FrameID frame_id(source_frame_num, render_view_host_);
606   if (!navigation_state_.CanSendEvents(frame_id))
607     return;
608
609   // We only send the onCreatedNavigationTarget if we end up creating a new
610   // window.
611   if (disposition != SINGLETON_TAB &&
612       disposition != NEW_FOREGROUND_TAB &&
613       disposition != NEW_BACKGROUND_TAB &&
614       disposition != NEW_POPUP &&
615       disposition != NEW_WINDOW &&
616       disposition != OFF_THE_RECORD)
617     return;
618
619   helpers::DispatchOnCreatedNavigationTarget(
620       web_contents(),
621       new_contents->GetBrowserContext(),
622       source_frame_num,
623       navigation_state_.IsMainFrame(frame_id),
624       new_contents,
625       url);
626 }
627
628 void WebNavigationTabObserver::FrameDetached(
629     content::RenderViewHost* render_view_host,
630     int64 frame_num) {
631   if (render_view_host != render_view_host_ &&
632       render_view_host != pending_render_view_host_) {
633     return;
634   }
635   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
636   if (navigation_state_.CanSendEvents(frame_id) &&
637       !navigation_state_.GetNavigationCompleted(frame_id)) {
638     helpers::DispatchOnErrorOccurred(
639         web_contents(),
640         render_view_host->GetProcess()->GetID(),
641         navigation_state_.GetUrl(frame_id),
642         frame_num,
643         navigation_state_.IsMainFrame(frame_id),
644         net::ERR_ABORTED);
645   }
646   navigation_state_.FrameDetached(frame_id);
647 }
648
649 void WebNavigationTabObserver::WebContentsDestroyed(content::WebContents* tab) {
650   g_tab_observer.Get().erase(tab);
651   registrar_.RemoveAll();
652   SendErrorEvents(tab, NULL, FrameNavigationState::FrameID());
653 }
654
655 void WebNavigationTabObserver::SendErrorEvents(
656     content::WebContents* web_contents,
657     content::RenderViewHost* render_view_host,
658     FrameNavigationState::FrameID id_to_skip) {
659   for (FrameNavigationState::const_iterator frame = navigation_state_.begin();
660        frame != navigation_state_.end(); ++frame) {
661     if (!navigation_state_.GetNavigationCompleted(*frame) &&
662         navigation_state_.CanSendEvents(*frame) &&
663         *frame != id_to_skip &&
664         (!render_view_host || frame->render_view_host == render_view_host)) {
665       navigation_state_.SetErrorOccurredInFrame(*frame);
666       helpers::DispatchOnErrorOccurred(
667           web_contents,
668           frame->render_view_host->GetProcess()->GetID(),
669           navigation_state_.GetUrl(*frame),
670           frame->frame_num,
671           navigation_state_.IsMainFrame(*frame),
672           net::ERR_ABORTED);
673     }
674   }
675   if (render_view_host)
676     navigation_state_.StopTrackingFramesInRVH(render_view_host, id_to_skip);
677 }
678
679 // See also NavigationController::IsURLInPageNavigation.
680 bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
681     FrameNavigationState::FrameID frame_id,
682     const GURL& url) {
683   GURL existing_url = navigation_state_.GetUrl(frame_id);
684   if (existing_url == url)
685     return false;
686
687   url_canon::Replacements<char> replacements;
688   replacements.ClearRef();
689   return existing_url.ReplaceComponents(replacements) ==
690       url.ReplaceComponents(replacements);
691 }
692
693 bool WebNavigationGetFrameFunction::RunImpl() {
694   scoped_ptr<GetFrame::Params> params(GetFrame::Params::Create(*args_));
695   EXTENSION_FUNCTION_VALIDATE(params.get());
696   int tab_id = params->details.tab_id;
697   int frame_id = params->details.frame_id;
698   int process_id = params->details.process_id;
699
700   SetResult(base::Value::CreateNullValue());
701
702   content::WebContents* web_contents;
703   if (!ExtensionTabUtil::GetTabById(tab_id,
704                                     GetProfile(),
705                                     include_incognito(),
706                                     NULL,
707                                     NULL,
708                                     &web_contents,
709                                     NULL) ||
710       !web_contents) {
711     return true;
712   }
713
714   WebNavigationTabObserver* observer =
715       WebNavigationTabObserver::Get(web_contents);
716   DCHECK(observer);
717
718   const FrameNavigationState& frame_navigation_state =
719       observer->frame_navigation_state();
720
721   if (frame_id == 0)
722     frame_id = frame_navigation_state.GetMainFrameID().frame_num;
723
724   content::RenderViewHost* render_view_host =
725       observer->GetRenderViewHostInProcess(process_id);
726   if (!render_view_host)
727     return true;
728
729   FrameNavigationState::FrameID internal_frame_id(frame_id, render_view_host);
730   if (!frame_navigation_state.IsValidFrame(internal_frame_id))
731     return true;
732
733   GURL frame_url = frame_navigation_state.GetUrl(internal_frame_id);
734   if (!frame_navigation_state.IsValidUrl(frame_url))
735     return true;
736
737   GetFrame::Results::Details frame_details;
738   frame_details.url = frame_url.spec();
739   frame_details.error_occurred =
740       frame_navigation_state.GetErrorOccurredInFrame(internal_frame_id);
741   FrameNavigationState::FrameID parent_frame_id =
742       frame_navigation_state.GetParentFrameID(internal_frame_id);
743   frame_details.parent_frame_id = helpers::GetFrameId(
744       frame_navigation_state.IsMainFrame(parent_frame_id),
745       parent_frame_id.frame_num);
746   results_ = GetFrame::Results::Create(frame_details);
747   return true;
748 }
749
750 bool WebNavigationGetAllFramesFunction::RunImpl() {
751   scoped_ptr<GetAllFrames::Params> params(GetAllFrames::Params::Create(*args_));
752   EXTENSION_FUNCTION_VALIDATE(params.get());
753   int tab_id = params->details.tab_id;
754
755   SetResult(base::Value::CreateNullValue());
756
757   content::WebContents* web_contents;
758   if (!ExtensionTabUtil::GetTabById(tab_id,
759                                     GetProfile(),
760                                     include_incognito(),
761                                     NULL,
762                                     NULL,
763                                     &web_contents,
764                                     NULL) ||
765       !web_contents) {
766     return true;
767   }
768
769   WebNavigationTabObserver* observer =
770       WebNavigationTabObserver::Get(web_contents);
771   DCHECK(observer);
772
773   const FrameNavigationState& navigation_state =
774       observer->frame_navigation_state();
775
776   std::vector<linked_ptr<GetAllFrames::Results::DetailsType> > result_list;
777   for (FrameNavigationState::const_iterator it = navigation_state.begin();
778        it != navigation_state.end(); ++it) {
779     FrameNavigationState::FrameID frame_id = *it;
780     FrameNavigationState::FrameID parent_frame_id =
781         navigation_state.GetParentFrameID(frame_id);
782     GURL frame_url = navigation_state.GetUrl(frame_id);
783     if (!navigation_state.IsValidUrl(frame_url))
784       continue;
785     linked_ptr<GetAllFrames::Results::DetailsType> frame(
786         new GetAllFrames::Results::DetailsType());
787     frame->url = frame_url.spec();
788     frame->frame_id = helpers::GetFrameId(
789         navigation_state.IsMainFrame(frame_id), frame_id.frame_num);
790     frame->parent_frame_id = helpers::GetFrameId(
791         navigation_state.IsMainFrame(parent_frame_id),
792         parent_frame_id.frame_num);
793     frame->process_id = frame_id.render_view_host->GetProcess()->GetID();
794     frame->error_occurred = navigation_state.GetErrorOccurredInFrame(frame_id);
795     result_list.push_back(frame);
796   }
797   results_ = GetAllFrames::Results::Create(result_list);
798   return true;
799 }
800
801 WebNavigationAPI::WebNavigationAPI(Profile* profile)
802     : profile_(profile) {
803   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
804       this, web_navigation::OnBeforeNavigate::kEventName);
805   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
806       this, web_navigation::OnCommitted::kEventName);
807   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
808       this, web_navigation::OnCompleted::kEventName);
809   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
810       this, web_navigation::OnCreatedNavigationTarget::kEventName);
811   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
812       this, web_navigation::OnDOMContentLoaded::kEventName);
813   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
814       this, web_navigation::OnHistoryStateUpdated::kEventName);
815   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
816       this, web_navigation::OnErrorOccurred::kEventName);
817   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
818       this, web_navigation::OnReferenceFragmentUpdated::kEventName);
819   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
820       this, web_navigation::OnTabReplaced::kEventName);
821 }
822
823 WebNavigationAPI::~WebNavigationAPI() {
824 }
825
826 void WebNavigationAPI::Shutdown() {
827   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
828 }
829
830 static base::LazyInstance<ProfileKeyedAPIFactory<WebNavigationAPI> >
831 g_factory = LAZY_INSTANCE_INITIALIZER;
832
833 // static
834 ProfileKeyedAPIFactory<WebNavigationAPI>*
835 WebNavigationAPI::GetFactoryInstance() {
836   return g_factory.Pointer();
837 }
838
839 void WebNavigationAPI::OnListenerAdded(const EventListenerInfo& details) {
840   web_navigation_event_router_.reset(new WebNavigationEventRouter(profile_));
841   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
842 }
843
844 #endif  // OS_ANDROID
845
846 }  // namespace extensions