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