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