web_navigation_api.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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
33namespace GetFrame = extensions::api::web_navigation::GetFrame;
34namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;
35
36DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver);
37
38namespace extensions {
39
40#if !defined(OS_ANDROID)
41
42namespace helpers = web_navigation_api_helpers;
43namespace keys = web_navigation_api_constants;
44namespace web_navigation = api::web_navigation;
45
46namespace {
47
48typedef std::map<content::WebContents*, WebNavigationTabObserver*>
49    TabObserverMap;
50static base::LazyInstance<TabObserverMap> g_tab_observer =
51    LAZY_INSTANCE_INITIALIZER;
52
53}  // namespace
54
55// WebNavigtionEventRouter -------------------------------------------
56
57WebNavigationEventRouter::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
65WebNavigationEventRouter::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
78WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}
79
80WebNavigationEventRouter::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
98WebNavigationEventRouter::~WebNavigationEventRouter() {
99  for (chrome::BrowserIterator it; !it.done(); it.Next())
100    OnBrowserRemoved(*it);
101  BrowserList::RemoveObserver(this);
102}
103
104void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
105  if (!profile_->IsSameProfile(browser->profile()))
106    return;
107  browser->tab_strip_model()->AddObserver(this);
108}
109
110void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
111  if (!profile_->IsSameProfile(browser->profile()))
112    return;
113  browser->tab_strip_model()->RemoveObserver(this);
114}
115
116void 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
139void 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
166void WebNavigationEventRouter::Retargeting(const RetargetingDetails* details) {
167  if (details->source_render_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_render_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_render_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_render_frame_id,
201        frame_navigation_state.IsMainFrame(frame_id),
202        details->target_web_contents,
203        details->target_url);
204  }
205}
206
207void 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
237void 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
250WebNavigationTabObserver::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
261WebNavigationTabObserver::~WebNavigationTabObserver() {}
262
263// static
264WebNavigationTabObserver* 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
270content::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
283void 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
301void 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
318void 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
332void 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
374void 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
452void 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
490void 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
519void 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
557void 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
587void 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.render_frame_id,
595                                         render_view_host);
596  navigation_state_.SetIsServerRedirected(frame_id);
597}
598
599void WebNavigationTabObserver::DidOpenRequestedURL(
600    content::WebContents* new_contents,
601    const GURL& url,
602    const content::Referrer& referrer,
603    WindowOpenDisposition disposition,
604    content::PageTransition transition,
605    int64 source_frame_num) {
606  FrameNavigationState::FrameID frame_id(source_frame_num, render_view_host_);
607  if (!navigation_state_.CanSendEvents(frame_id))
608    return;
609
610  // We only send the onCreatedNavigationTarget if we end up creating a new
611  // window.
612  if (disposition != SINGLETON_TAB &&
613      disposition != NEW_FOREGROUND_TAB &&
614      disposition != NEW_BACKGROUND_TAB &&
615      disposition != NEW_POPUP &&
616      disposition != NEW_WINDOW &&
617      disposition != OFF_THE_RECORD)
618    return;
619
620  helpers::DispatchOnCreatedNavigationTarget(
621      web_contents(),
622      new_contents->GetBrowserContext(),
623      source_frame_num,
624      navigation_state_.IsMainFrame(frame_id),
625      new_contents,
626      url);
627}
628
629void WebNavigationTabObserver::FrameDetached(
630    content::RenderViewHost* render_view_host,
631    int64 frame_num) {
632  if (render_view_host != render_view_host_ &&
633      render_view_host != pending_render_view_host_) {
634    return;
635  }
636  FrameNavigationState::FrameID frame_id(frame_num, render_view_host);
637  if (navigation_state_.CanSendEvents(frame_id) &&
638      !navigation_state_.GetNavigationCompleted(frame_id)) {
639    helpers::DispatchOnErrorOccurred(
640        web_contents(),
641        render_view_host->GetProcess()->GetID(),
642        navigation_state_.GetUrl(frame_id),
643        frame_num,
644        navigation_state_.IsMainFrame(frame_id),
645        net::ERR_ABORTED);
646  }
647  navigation_state_.FrameDetached(frame_id);
648}
649
650void WebNavigationTabObserver::WebContentsDestroyed(content::WebContents* tab) {
651  g_tab_observer.Get().erase(tab);
652  registrar_.RemoveAll();
653  SendErrorEvents(tab, NULL, FrameNavigationState::FrameID());
654}
655
656void WebNavigationTabObserver::SendErrorEvents(
657    content::WebContents* web_contents,
658    content::RenderViewHost* render_view_host,
659    FrameNavigationState::FrameID id_to_skip) {
660  for (FrameNavigationState::const_iterator frame = navigation_state_.begin();
661       frame != navigation_state_.end(); ++frame) {
662    if (!navigation_state_.GetNavigationCompleted(*frame) &&
663        navigation_state_.CanSendEvents(*frame) &&
664        *frame != id_to_skip &&
665        (!render_view_host || frame->render_view_host == render_view_host)) {
666      navigation_state_.SetErrorOccurredInFrame(*frame);
667      helpers::DispatchOnErrorOccurred(
668          web_contents,
669          frame->render_view_host->GetProcess()->GetID(),
670          navigation_state_.GetUrl(*frame),
671          frame->frame_num,
672          navigation_state_.IsMainFrame(*frame),
673          net::ERR_ABORTED);
674    }
675  }
676  if (render_view_host)
677    navigation_state_.StopTrackingFramesInRVH(render_view_host, id_to_skip);
678}
679
680// See also NavigationController::IsURLInPageNavigation.
681bool WebNavigationTabObserver::IsReferenceFragmentNavigation(
682    FrameNavigationState::FrameID frame_id,
683    const GURL& url) {
684  GURL existing_url = navigation_state_.GetUrl(frame_id);
685  if (existing_url == url)
686    return false;
687
688  url_canon::Replacements<char> replacements;
689  replacements.ClearRef();
690  return existing_url.ReplaceComponents(replacements) ==
691      url.ReplaceComponents(replacements);
692}
693
694bool WebNavigationGetFrameFunction::RunImpl() {
695  scoped_ptr<GetFrame::Params> params(GetFrame::Params::Create(*args_));
696  EXTENSION_FUNCTION_VALIDATE(params.get());
697  int tab_id = params->details.tab_id;
698  int frame_id = params->details.frame_id;
699  int process_id = params->details.process_id;
700
701  SetResult(base::Value::CreateNullValue());
702
703  content::WebContents* web_contents;
704  if (!ExtensionTabUtil::GetTabById(tab_id,
705                                    GetProfile(),
706                                    include_incognito(),
707                                    NULL,
708                                    NULL,
709                                    &web_contents,
710                                    NULL) ||
711      !web_contents) {
712    return true;
713  }
714
715  WebNavigationTabObserver* observer =
716      WebNavigationTabObserver::Get(web_contents);
717  DCHECK(observer);
718
719  const FrameNavigationState& frame_navigation_state =
720      observer->frame_navigation_state();
721
722  if (frame_id == 0)
723    frame_id = frame_navigation_state.GetMainFrameID().frame_num;
724
725  content::RenderViewHost* render_view_host =
726      observer->GetRenderViewHostInProcess(process_id);
727  if (!render_view_host)
728    return true;
729
730  FrameNavigationState::FrameID internal_frame_id(frame_id, render_view_host);
731  if (!frame_navigation_state.IsValidFrame(internal_frame_id))
732    return true;
733
734  GURL frame_url = frame_navigation_state.GetUrl(internal_frame_id);
735  if (!frame_navigation_state.IsValidUrl(frame_url))
736    return true;
737
738  GetFrame::Results::Details frame_details;
739  frame_details.url = frame_url.spec();
740  frame_details.error_occurred =
741      frame_navigation_state.GetErrorOccurredInFrame(internal_frame_id);
742  FrameNavigationState::FrameID parent_frame_id =
743      frame_navigation_state.GetParentFrameID(internal_frame_id);
744  frame_details.parent_frame_id = helpers::GetFrameId(
745      frame_navigation_state.IsMainFrame(parent_frame_id),
746      parent_frame_id.frame_num);
747  results_ = GetFrame::Results::Create(frame_details);
748  return true;
749}
750
751bool WebNavigationGetAllFramesFunction::RunImpl() {
752  scoped_ptr<GetAllFrames::Params> params(GetAllFrames::Params::Create(*args_));
753  EXTENSION_FUNCTION_VALIDATE(params.get());
754  int tab_id = params->details.tab_id;
755
756  SetResult(base::Value::CreateNullValue());
757
758  content::WebContents* web_contents;
759  if (!ExtensionTabUtil::GetTabById(tab_id,
760                                    GetProfile(),
761                                    include_incognito(),
762                                    NULL,
763                                    NULL,
764                                    &web_contents,
765                                    NULL) ||
766      !web_contents) {
767    return true;
768  }
769
770  WebNavigationTabObserver* observer =
771      WebNavigationTabObserver::Get(web_contents);
772  DCHECK(observer);
773
774  const FrameNavigationState& navigation_state =
775      observer->frame_navigation_state();
776
777  std::vector<linked_ptr<GetAllFrames::Results::DetailsType> > result_list;
778  for (FrameNavigationState::const_iterator it = navigation_state.begin();
779       it != navigation_state.end(); ++it) {
780    FrameNavigationState::FrameID frame_id = *it;
781    FrameNavigationState::FrameID parent_frame_id =
782        navigation_state.GetParentFrameID(frame_id);
783    GURL frame_url = navigation_state.GetUrl(frame_id);
784    if (!navigation_state.IsValidUrl(frame_url))
785      continue;
786    linked_ptr<GetAllFrames::Results::DetailsType> frame(
787        new GetAllFrames::Results::DetailsType());
788    frame->url = frame_url.spec();
789    frame->frame_id = helpers::GetFrameId(
790        navigation_state.IsMainFrame(frame_id), frame_id.frame_num);
791    frame->parent_frame_id = helpers::GetFrameId(
792        navigation_state.IsMainFrame(parent_frame_id),
793        parent_frame_id.frame_num);
794    frame->process_id = frame_id.render_view_host->GetProcess()->GetID();
795    frame->error_occurred = navigation_state.GetErrorOccurredInFrame(frame_id);
796    result_list.push_back(frame);
797  }
798  results_ = GetAllFrames::Results::Create(result_list);
799  return true;
800}
801
802WebNavigationAPI::WebNavigationAPI(content::BrowserContext* context)
803    : browser_context_(context) {
804  EventRouter* event_router =
805      ExtensionSystem::Get(browser_context_)->event_router();
806  event_router->RegisterObserver(this,
807                                 web_navigation::OnBeforeNavigate::kEventName);
808  event_router->RegisterObserver(this, web_navigation::OnCommitted::kEventName);
809  event_router->RegisterObserver(this, web_navigation::OnCompleted::kEventName);
810  event_router->RegisterObserver(
811      this, web_navigation::OnCreatedNavigationTarget::kEventName);
812  event_router->RegisterObserver(
813      this, web_navigation::OnDOMContentLoaded::kEventName);
814  event_router->RegisterObserver(
815      this, web_navigation::OnHistoryStateUpdated::kEventName);
816  event_router->RegisterObserver(this,
817                                 web_navigation::OnErrorOccurred::kEventName);
818  event_router->RegisterObserver(
819      this, web_navigation::OnReferenceFragmentUpdated::kEventName);
820  event_router->RegisterObserver(this,
821                                 web_navigation::OnTabReplaced::kEventName);
822}
823
824WebNavigationAPI::~WebNavigationAPI() {
825}
826
827void WebNavigationAPI::Shutdown() {
828  ExtensionSystem::Get(browser_context_)->event_router()->UnregisterObserver(
829      this);
830}
831
832static base::LazyInstance<BrowserContextKeyedAPIFactory<WebNavigationAPI> >
833    g_factory = LAZY_INSTANCE_INITIALIZER;
834
835// static
836BrowserContextKeyedAPIFactory<WebNavigationAPI>*
837WebNavigationAPI::GetFactoryInstance() {
838  return g_factory.Pointer();
839}
840
841void WebNavigationAPI::OnListenerAdded(const EventListenerInfo& details) {
842  web_navigation_event_router_.reset(new WebNavigationEventRouter(
843      Profile::FromBrowserContext(browser_context_)));
844  ExtensionSystem::Get(browser_context_)->event_router()->UnregisterObserver(
845      this);
846}
847
848#endif  // OS_ANDROID
849
850}  // namespace extensions
851