Upstream version 8.37.180.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/view_type_utils.h"
30 #include "net/base/net_errors.h"
31
32 namespace GetFrame = extensions::api::web_navigation::GetFrame;
33 namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;
34
35 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver);
36
37 namespace extensions {
38
39 #if !defined(OS_ANDROID)
40
41 namespace helpers = web_navigation_api_helpers;
42 namespace keys = web_navigation_api_constants;
43 namespace web_navigation = api::web_navigation;
44
45 namespace {
46
47 typedef std::map<content::WebContents*, WebNavigationTabObserver*>
48     TabObserverMap;
49 static base::LazyInstance<TabObserverMap> g_tab_observer =
50     LAZY_INSTANCE_INITIALIZER;
51
52 }  // namespace
53
54 // WebNavigtionEventRouter -------------------------------------------
55
56 WebNavigationEventRouter::PendingWebContents::PendingWebContents()
57     : source_web_contents(NULL),
58       source_frame_id(0),
59       source_frame_is_main_frame(false),
60       target_web_contents(NULL),
61       target_url() {
62 }
63
64 WebNavigationEventRouter::PendingWebContents::PendingWebContents(
65     content::WebContents* source_web_contents,
66     int64 source_frame_id,
67     bool source_frame_is_main_frame,
68     content::WebContents* target_web_contents,
69     const GURL& target_url)
70     : source_web_contents(source_web_contents),
71       source_frame_id(source_frame_id),
72       source_frame_is_main_frame(source_frame_is_main_frame),
73       target_web_contents(target_web_contents),
74       target_url(target_url) {
75 }
76
77 WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}
78
79 WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile)
80     : profile_(profile) {
81   CHECK(registrar_.IsEmpty());
82   registrar_.Add(this,
83                  chrome::NOTIFICATION_RETARGETING,
84                  content::NotificationService::AllSources());
85   registrar_.Add(this,
86                  chrome::NOTIFICATION_TAB_ADDED,
87                  content::NotificationService::AllSources());
88   registrar_.Add(this,
89                  content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
90                  content::NotificationService::AllSources());
91
92   BrowserList::AddObserver(this);
93   for (chrome::BrowserIterator it; !it.done(); it.Next())
94     OnBrowserAdded(*it);
95 }
96
97 WebNavigationEventRouter::~WebNavigationEventRouter() {
98   for (chrome::BrowserIterator it; !it.done(); it.Next())
99     OnBrowserRemoved(*it);
100   BrowserList::RemoveObserver(this);
101 }
102
103 void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
104   if (!profile_->IsSameProfile(browser->profile()))
105     return;
106   browser->tab_strip_model()->AddObserver(this);
107 }
108
109 void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
110   if (!profile_->IsSameProfile(browser->profile()))
111     return;
112   browser->tab_strip_model()->RemoveObserver(this);
113 }
114
115 void WebNavigationEventRouter::TabReplacedAt(
116     TabStripModel* tab_strip_model,
117     content::WebContents* old_contents,
118     content::WebContents* new_contents,
119     int index) {
120   WebNavigationTabObserver* tab_observer =
121       WebNavigationTabObserver::Get(old_contents);
122   if (!tab_observer) {
123     // If you hit this DCHECK(), please add reproduction steps to
124     // http://crbug.com/109464.
125     DCHECK(GetViewType(old_contents) != VIEW_TYPE_TAB_CONTENTS);
126     return;
127   }
128   const FrameNavigationState& frame_navigation_state =
129       tab_observer->frame_navigation_state();
130
131   if (!frame_navigation_state.IsValidUrl(old_contents->GetURL()) ||
132       !frame_navigation_state.IsValidUrl(new_contents->GetURL()))
133     return;
134
135   helpers::DispatchOnTabReplaced(old_contents, profile_, new_contents);
136 }
137
138 void WebNavigationEventRouter::Observe(
139     int type,
140     const content::NotificationSource& source,
141     const content::NotificationDetails& details) {
142   switch (type) {
143     case chrome::NOTIFICATION_RETARGETING: {
144       Profile* profile = content::Source<Profile>(source).ptr();
145       if (profile->GetOriginalProfile() == profile_) {
146         Retargeting(
147             content::Details<const RetargetingDetails>(details).ptr());
148       }
149       break;
150     }
151
152     case chrome::NOTIFICATION_TAB_ADDED:
153       TabAdded(content::Details<content::WebContents>(details).ptr());
154       break;
155
156     case content::NOTIFICATION_WEB_CONTENTS_DESTROYED:
157       TabDestroyed(content::Source<content::WebContents>(source).ptr());
158       break;
159
160     default:
161       NOTREACHED();
162   }
163 }
164
165 void WebNavigationEventRouter::Retargeting(const RetargetingDetails* details) {
166   if (details->source_render_frame_id == 0)
167     return;
168   WebNavigationTabObserver* tab_observer =
169       WebNavigationTabObserver::Get(details->source_web_contents);
170   if (!tab_observer) {
171     // If you hit this DCHECK(), please add reproduction steps to
172     // http://crbug.com/109464.
173     DCHECK(GetViewType(details->source_web_contents) != VIEW_TYPE_TAB_CONTENTS);
174     return;
175   }
176   const FrameNavigationState& frame_navigation_state =
177       tab_observer->frame_navigation_state();
178
179   FrameNavigationState::FrameID frame_id(
180       details->source_render_frame_id,
181       details->source_web_contents->GetRenderViewHost());
182   if (!frame_navigation_state.CanSendEvents(frame_id))
183     return;
184
185   // If the WebContents isn't yet inserted into a tab strip, we need to delay
186   // the extension event until the WebContents is fully initialized.
187   if (details->not_yet_in_tabstrip) {
188     pending_web_contents_[details->target_web_contents] =
189         PendingWebContents(
190             details->source_web_contents,
191             details->source_render_frame_id,
192             frame_navigation_state.IsMainFrame(frame_id),
193             details->target_web_contents,
194             details->target_url);
195   } else {
196     helpers::DispatchOnCreatedNavigationTarget(
197         details->source_web_contents,
198         details->target_web_contents->GetBrowserContext(),
199         details->source_render_frame_id,
200         frame_navigation_state.IsMainFrame(frame_id),
201         details->target_web_contents,
202         details->target_url);
203   }
204 }
205
206 void WebNavigationEventRouter::TabAdded(content::WebContents* tab) {
207   std::map<content::WebContents*, PendingWebContents>::iterator iter =
208       pending_web_contents_.find(tab);
209   if (iter == pending_web_contents_.end())
210     return;
211
212   WebNavigationTabObserver* tab_observer =
213       WebNavigationTabObserver::Get(iter->second.source_web_contents);
214   if (!tab_observer) {
215     NOTREACHED();
216     return;
217   }
218   const FrameNavigationState& frame_navigation_state =
219       tab_observer->frame_navigation_state();
220
221   FrameNavigationState::FrameID frame_id(
222       iter->second.source_frame_id,
223       iter->second.source_web_contents->GetRenderViewHost());
224   if (frame_navigation_state.CanSendEvents(frame_id)) {
225     helpers::DispatchOnCreatedNavigationTarget(
226         iter->second.source_web_contents,
227         iter->second.target_web_contents->GetBrowserContext(),
228         iter->second.source_frame_id,
229         iter->second.source_frame_is_main_frame,
230         iter->second.target_web_contents,
231         iter->second.target_url);
232   }
233   pending_web_contents_.erase(iter);
234 }
235
236 void WebNavigationEventRouter::TabDestroyed(content::WebContents* tab) {
237   pending_web_contents_.erase(tab);
238   for (std::map<content::WebContents*, PendingWebContents>::iterator i =
239            pending_web_contents_.begin(); i != pending_web_contents_.end(); ) {
240     if (i->second.source_web_contents == tab)
241       pending_web_contents_.erase(i++);
242     else
243       ++i;
244   }
245 }
246
247 // WebNavigationTabObserver ------------------------------------------
248
249 WebNavigationTabObserver::WebNavigationTabObserver(
250     content::WebContents* web_contents)
251     : WebContentsObserver(web_contents),
252       render_view_host_(NULL),
253       pending_render_view_host_(NULL) {
254   g_tab_observer.Get().insert(TabObserverMap::value_type(web_contents, this));
255   registrar_.Add(this,
256                  content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
257                  content::NotificationService::AllSources());
258 }
259
260 WebNavigationTabObserver::~WebNavigationTabObserver() {}
261
262 // static
263 WebNavigationTabObserver* WebNavigationTabObserver::Get(
264     content::WebContents* web_contents) {
265   TabObserverMap::iterator i = g_tab_observer.Get().find(web_contents);
266   return i == g_tab_observer.Get().end() ? NULL : i->second;
267 }
268
269 content::RenderViewHost* WebNavigationTabObserver::GetRenderViewHostInProcess(
270     int process_id) const {
271   if (render_view_host_ &&
272       render_view_host_->GetProcess()->GetID() == process_id) {
273     return render_view_host_;
274   }
275   if (pending_render_view_host_ &&
276       pending_render_view_host_->GetProcess()->GetID() == process_id) {
277     return pending_render_view_host_;
278   }
279   return NULL;
280 }
281
282 void WebNavigationTabObserver::Observe(
283     int type,
284     const content::NotificationSource& source,
285     const content::NotificationDetails& details) {
286   switch (type) {
287     case content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW: {
288       // The RenderView is technically not yet deleted, but the RenderViewHost
289       // already starts to filter out some IPCs. In order to not get confused,
290       // we consider the RenderView dead already now.
291       RenderViewDeleted(content::Source<content::RenderViewHost>(source).ptr());
292       break;
293     }
294
295     default:
296       NOTREACHED();
297   }
298 }
299
300 void WebNavigationTabObserver::RenderViewDeleted(
301     content::RenderViewHost* render_view_host) {
302   if (render_view_host == render_view_host_) {
303     render_view_host_ = NULL;
304     if (pending_render_view_host_) {
305       render_view_host_ = pending_render_view_host_;
306       pending_render_view_host_ = NULL;
307     }
308   } else if (render_view_host == pending_render_view_host_) {
309     pending_render_view_host_ = NULL;
310   } else {
311     return;
312   }
313   SendErrorEvents(
314       web_contents(), render_view_host, FrameNavigationState::FrameID());
315 }
316
317 void WebNavigationTabObserver::AboutToNavigateRenderView(
318     content::RenderViewHost* render_view_host) {
319   if (!render_view_host_) {
320     render_view_host_ = render_view_host;
321   } else if (render_view_host != render_view_host_) {
322     if (pending_render_view_host_) {
323       SendErrorEvents(web_contents(),
324                       pending_render_view_host_,
325                       FrameNavigationState::FrameID());
326     }
327     pending_render_view_host_ = render_view_host;
328   }
329 }
330
331 void WebNavigationTabObserver::DidStartProvisionalLoadForFrame(
332     int64 frame_num,
333     int64 parent_frame_num,
334     bool is_main_frame,
335     const GURL& validated_url,
336     bool is_error_page,
337     bool is_iframe_srcdoc,
338     content::RenderViewHost* render_view_host) {
339   DVLOG(2) << "DidStartProvisionalLoad("
340            << "render_view_host=" << render_view_host
341            << ", frame_num=" << frame_num
342            << ", url=" << validated_url << ")";
343   if (!render_view_host_)
344     render_view_host_ = render_view_host;
345   if (render_view_host != render_view_host_ &&
346       render_view_host != pending_render_view_host_)
347     return;
348
349   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
350   FrameNavigationState::FrameID parent_frame_id(
351       parent_frame_num, render_view_host);
352
353   navigation_state_.TrackFrame(frame_id,
354                                parent_frame_id,
355                                validated_url,
356                                is_main_frame,
357                                is_error_page,
358                                is_iframe_srcdoc);
359
360   if (!navigation_state_.CanSendEvents(frame_id))
361     return;
362
363   helpers::DispatchOnBeforeNavigate(
364       web_contents(),
365       render_view_host->GetProcess()->GetID(),
366       frame_num,
367       is_main_frame,
368       parent_frame_num,
369       navigation_state_.IsMainFrame(parent_frame_id),
370       navigation_state_.GetUrl(frame_id));
371 }
372
373 void WebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
374     int64 frame_num,
375     const base::string16& frame_unique_name,
376     bool is_main_frame,
377     const GURL& url,
378     content::PageTransition transition_type,
379     content::RenderViewHost* render_view_host) {
380   DVLOG(2) << "DidCommitProvisionalLoad("
381            << "render_view_host=" << render_view_host
382            << ", frame_num=" << frame_num
383            << ", url=" << url << ")";
384   if (render_view_host != render_view_host_ &&
385       render_view_host != pending_render_view_host_)
386     return;
387   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
388
389   bool is_reference_fragment_navigation =
390       IsReferenceFragmentNavigation(frame_id, url);
391   bool is_history_state_modification =
392       navigation_state_.GetNavigationCommitted(frame_id);
393
394   if (is_main_frame && render_view_host_ == render_view_host) {
395     // Changing the reference fragment or the history state using
396     // history.pushState or history.replaceState does not cancel on-going
397     // iframe navigations.
398     if (!is_reference_fragment_navigation && !is_history_state_modification)
399       SendErrorEvents(web_contents(), render_view_host_, frame_id);
400     if (pending_render_view_host_) {
401       SendErrorEvents(web_contents(),
402                       pending_render_view_host_,
403                       FrameNavigationState::FrameID());
404       pending_render_view_host_ = NULL;
405     }
406   } else if (pending_render_view_host_ == render_view_host) {
407     SendErrorEvents(
408         web_contents(), render_view_host_, FrameNavigationState::FrameID());
409     render_view_host_ = pending_render_view_host_;
410     pending_render_view_host_ = NULL;
411   }
412
413   // Update the URL as it might have changed.
414   navigation_state_.UpdateFrame(frame_id, url);
415   navigation_state_.SetNavigationCommitted(frame_id);
416
417   if (!navigation_state_.CanSendEvents(frame_id))
418     return;
419
420   if (is_reference_fragment_navigation) {
421     helpers::DispatchOnCommitted(
422         web_navigation::OnReferenceFragmentUpdated::kEventName,
423         web_contents(),
424         frame_num,
425         is_main_frame,
426         navigation_state_.GetUrl(frame_id),
427         transition_type);
428   } else if (is_history_state_modification) {
429     helpers::DispatchOnCommitted(
430         web_navigation::OnHistoryStateUpdated::kEventName,
431         web_contents(),
432         frame_num,
433         is_main_frame,
434         navigation_state_.GetUrl(frame_id),
435         transition_type);
436   } else {
437     if (navigation_state_.GetIsServerRedirected(frame_id)) {
438       transition_type = static_cast<content::PageTransition>(
439           transition_type | content::PAGE_TRANSITION_SERVER_REDIRECT);
440     }
441     helpers::DispatchOnCommitted(
442         web_navigation::OnCommitted::kEventName,
443         web_contents(),
444         frame_num,
445         is_main_frame,
446         navigation_state_.GetUrl(frame_id),
447         transition_type);
448   }
449 }
450
451 void WebNavigationTabObserver::DidFailProvisionalLoad(
452     int64 frame_num,
453     const base::string16& frame_unique_id,
454     bool is_main_frame,
455     const GURL& validated_url,
456     int error_code,
457     const base::string16& error_description,
458     content::RenderViewHost* render_view_host) {
459   DVLOG(2) << "DidFailProvisionalLoad("
460            << "render_view_host=" << render_view_host
461            << ", frame_num=" << frame_num
462            << ", url=" << validated_url << ")";
463   if (render_view_host != render_view_host_ &&
464       render_view_host != pending_render_view_host_)
465     return;
466   bool stop_tracking_frames = false;
467   if (render_view_host == pending_render_view_host_) {
468     pending_render_view_host_ = NULL;
469     stop_tracking_frames = true;
470   }
471
472   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
473   if (navigation_state_.CanSendEvents(frame_id)) {
474     helpers::DispatchOnErrorOccurred(
475         web_contents(),
476         render_view_host->GetProcess()->GetID(),
477         navigation_state_.GetUrl(frame_id),
478         frame_num,
479         is_main_frame,
480         error_code);
481   }
482   navigation_state_.SetErrorOccurredInFrame(frame_id);
483   if (stop_tracking_frames) {
484     navigation_state_.StopTrackingFramesInRVH(render_view_host,
485                                               FrameNavigationState::FrameID());
486   }
487 }
488
489 void WebNavigationTabObserver::DocumentLoadedInFrame(
490     int64 frame_num,
491     content::RenderViewHost* render_view_host) {
492   DVLOG(2) << "DocumentLoadedInFrame("
493            << "render_view_host=" << render_view_host
494            << ", frame_num=" << frame_num << ")";
495   if (render_view_host != render_view_host_)
496     return;
497   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
498   if (!navigation_state_.CanSendEvents(frame_id))
499     return;
500   navigation_state_.SetParsingFinished(frame_id);
501   helpers::DispatchOnDOMContentLoaded(web_contents(),
502                                       navigation_state_.GetUrl(frame_id),
503                                       navigation_state_.IsMainFrame(frame_id),
504                                       frame_num);
505
506   if (!navigation_state_.GetNavigationCompleted(frame_id))
507     return;
508
509   // The load might already have finished by the time we finished parsing. For
510   // compatibility reasons, we artifically delay the load completed signal until
511   // after parsing was completed.
512   helpers::DispatchOnCompleted(web_contents(),
513                                navigation_state_.GetUrl(frame_id),
514                                navigation_state_.IsMainFrame(frame_id),
515                                frame_num);
516 }
517
518 void WebNavigationTabObserver::DidFinishLoad(
519     int64 frame_num,
520     const GURL& validated_url,
521     bool is_main_frame,
522     content::RenderViewHost* render_view_host) {
523   DVLOG(2) << "DidFinishLoad("
524            << "render_view_host=" << render_view_host
525            << ", frame_num=" << frame_num
526            << ", url=" << validated_url << ")";
527   if (render_view_host != render_view_host_)
528     return;
529   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
530   // When showing replacement content, we might get load signals for frames
531   // that weren't reguarly loaded.
532   if (!navigation_state_.IsValidFrame(frame_id))
533     return;
534   navigation_state_.SetNavigationCompleted(frame_id);
535   if (!navigation_state_.CanSendEvents(frame_id))
536     return;
537   DCHECK(
538       navigation_state_.GetUrl(frame_id) == validated_url ||
539       (navigation_state_.GetUrl(frame_id) == GURL(content::kAboutSrcDocURL) &&
540        validated_url == GURL(url::kAboutBlankURL)))
541       << "validated URL is " << validated_url << " but we expected "
542       << navigation_state_.GetUrl(frame_id);
543   DCHECK_EQ(navigation_state_.IsMainFrame(frame_id), is_main_frame);
544
545   // The load might already have finished by the time we finished parsing. For
546   // compatibility reasons, we artifically delay the load completed signal until
547   // after parsing was completed.
548   if (!navigation_state_.GetParsingFinished(frame_id))
549     return;
550   helpers::DispatchOnCompleted(web_contents(),
551                                navigation_state_.GetUrl(frame_id),
552                                is_main_frame,
553                                frame_num);
554 }
555
556 void WebNavigationTabObserver::DidFailLoad(
557     int64 frame_num,
558     const GURL& validated_url,
559     bool is_main_frame,
560     int error_code,
561     const base::string16& error_description,
562     content::RenderViewHost* render_view_host) {
563   DVLOG(2) << "DidFailLoad("
564            << "render_view_host=" << render_view_host
565            << ", frame_num=" << frame_num
566            << ", url=" << validated_url << ")";
567   if (render_view_host != render_view_host_)
568     return;
569   FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
570   // When showing replacement content, we might get load signals for frames
571   // that weren't reguarly loaded.
572   if (!navigation_state_.IsValidFrame(frame_id))
573     return;
574   if (navigation_state_.CanSendEvents(frame_id)) {
575     helpers::DispatchOnErrorOccurred(
576         web_contents(),
577         render_view_host->GetProcess()->GetID(),
578         navigation_state_.GetUrl(frame_id),
579         frame_num,
580         is_main_frame,
581         error_code);
582   }
583   navigation_state_.SetErrorOccurredInFrame(frame_id);
584 }
585
586 void WebNavigationTabObserver::DidGetRedirectForResourceRequest(
587     content::RenderViewHost* render_view_host,
588     const content::ResourceRedirectDetails& details) {
589   if (details.resource_type != ResourceType::MAIN_FRAME &&
590       details.resource_type != ResourceType::SUB_FRAME) {
591     return;
592   }
593   FrameNavigationState::FrameID frame_id(details.render_frame_id,
594                                          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() {
650   g_tab_observer.Get().erase(web_contents());
651   registrar_.RemoveAll();
652   SendErrorEvents(web_contents(), 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::Replacements<char> replacements;
688   replacements.ClearRef();
689   return existing_url.ReplaceComponents(replacements) ==
690       url.ReplaceComponents(replacements);
691 }
692
693 bool WebNavigationGetFrameFunction::RunSync() {
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::RunSync() {
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(content::BrowserContext* context)
802     : browser_context_(context) {
803   EventRouter* event_router = EventRouter::Get(browser_context_);
804   event_router->RegisterObserver(this,
805                                  web_navigation::OnBeforeNavigate::kEventName);
806   event_router->RegisterObserver(this, web_navigation::OnCommitted::kEventName);
807   event_router->RegisterObserver(this, web_navigation::OnCompleted::kEventName);
808   event_router->RegisterObserver(
809       this, web_navigation::OnCreatedNavigationTarget::kEventName);
810   event_router->RegisterObserver(
811       this, web_navigation::OnDOMContentLoaded::kEventName);
812   event_router->RegisterObserver(
813       this, web_navigation::OnHistoryStateUpdated::kEventName);
814   event_router->RegisterObserver(this,
815                                  web_navigation::OnErrorOccurred::kEventName);
816   event_router->RegisterObserver(
817       this, web_navigation::OnReferenceFragmentUpdated::kEventName);
818   event_router->RegisterObserver(this,
819                                  web_navigation::OnTabReplaced::kEventName);
820 }
821
822 WebNavigationAPI::~WebNavigationAPI() {
823 }
824
825 void WebNavigationAPI::Shutdown() {
826   EventRouter::Get(browser_context_)->UnregisterObserver(this);
827 }
828
829 static base::LazyInstance<BrowserContextKeyedAPIFactory<WebNavigationAPI> >
830     g_factory = LAZY_INSTANCE_INITIALIZER;
831
832 // static
833 BrowserContextKeyedAPIFactory<WebNavigationAPI>*
834 WebNavigationAPI::GetFactoryInstance() {
835   return g_factory.Pointer();
836 }
837
838 void WebNavigationAPI::OnListenerAdded(const EventListenerInfo& details) {
839   web_navigation_event_router_.reset(new WebNavigationEventRouter(
840       Profile::FromBrowserContext(browser_context_)));
841   EventRouter::Get(browser_context_)->UnregisterObserver(this);
842 }
843
844 #endif  // OS_ANDROID
845
846 }  // namespace extensions