Home | History | Annotate | Download | only in web_navigation
      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 
     33 namespace GetFrame = extensions::api::web_navigation::GetFrame;
     34 namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames;
     35 
     36 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver);
     37 
     38 namespace extensions {
     39 
     40 #if !defined(OS_ANDROID)
     41 
     42 namespace helpers = web_navigation_api_helpers;
     43 namespace keys = web_navigation_api_constants;
     44 
     45 namespace {
     46 
     47 typedef std::map<content::WebContents*, WebNavigationTabObserver*>
     48     TabObserverMap;
     49 static base::LazyInstance<TabObserverMap> g_tab_observer =
     50     LAZY_INSTANCE_INITIALIZER;
     51 
     52 }  // namespace
     53 
     54 // WebNavigtionEventRouter -------------------------------------------
     55 
     56 WebNavigationEventRouter::PendingWebContents::PendingWebContents()
     57     : source_web_contents(NULL),
     58       source_frame_id(0),
     59       source_frame_is_main_frame(false),
     60       target_web_contents(NULL),
     61       target_url() {
     62 }
     63 
     64 WebNavigationEventRouter::PendingWebContents::PendingWebContents(
     65     content::WebContents* source_web_contents,
     66     int64 source_frame_id,
     67     bool source_frame_is_main_frame,
     68     content::WebContents* target_web_contents,
     69     const GURL& target_url)
     70     : source_web_contents(source_web_contents),
     71       source_frame_id(source_frame_id),
     72       source_frame_is_main_frame(source_frame_is_main_frame),
     73       target_web_contents(target_web_contents),
     74       target_url(target_url) {
     75 }
     76 
     77 WebNavigationEventRouter::PendingWebContents::~PendingWebContents() {}
     78 
     79 WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile)
     80     : profile_(profile) {
     81   CHECK(registrar_.IsEmpty());
     82   registrar_.Add(this,
     83                  chrome::NOTIFICATION_RETARGETING,
     84                  content::NotificationService::AllSources());
     85   registrar_.Add(this,
     86                  chrome::NOTIFICATION_TAB_ADDED,
     87                  content::NotificationService::AllSources());
     88   registrar_.Add(this,
     89                  content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
     90                  content::NotificationService::AllSources());
     91 
     92   BrowserList::AddObserver(this);
     93   for (chrome::BrowserIterator it; !it.done(); it.Next()) {
     94     OnBrowserAdded(*it);
     95   }
     96 }
     97 
     98 WebNavigationEventRouter::~WebNavigationEventRouter() {
     99   BrowserList::RemoveObserver(this);
    100 }
    101 
    102 void WebNavigationEventRouter::OnBrowserAdded(Browser* browser) {
    103   if (!profile_->IsSameProfile(browser->profile()))
    104     return;
    105   browser->tab_strip_model()->AddObserver(this);
    106 }
    107 
    108 void WebNavigationEventRouter::OnBrowserRemoved(Browser* browser) {
    109   if (!profile_->IsSameProfile(browser->profile()))
    110     return;
    111   browser->tab_strip_model()->RemoveObserver(this);
    112 }
    113 
    114 void 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 
    137 void 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 
    164 void 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 
    205 void 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 
    235 void 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 
    248 WebNavigationTabObserver::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 
    262 WebNavigationTabObserver::~WebNavigationTabObserver() {}
    263 
    264 // static
    265 WebNavigationTabObserver* 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 
    271 content::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 
    284 void 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 
    332 void 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 
    349 void 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 
    363 void 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 
    405 void 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 
    485 void 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 
    522 void 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 
    551 void 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 
    589 void 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 
    619 void 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 
    649 void 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 
    670 void WebNavigationTabObserver::WebContentsDestroyed(content::WebContents* tab) {
    671   g_tab_observer.Get().erase(tab);
    672   registrar_.RemoveAll();
    673   SendErrorEvents(tab, NULL, FrameNavigationState::FrameID());
    674 }
    675 
    676 void 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.
    701 bool 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 
    714 bool 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 
    770 bool 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 
    820 WebNavigationAPI::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 
    842 WebNavigationAPI::~WebNavigationAPI() {
    843 }
    844 
    845 void WebNavigationAPI::Shutdown() {
    846   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
    847 }
    848 
    849 static base::LazyInstance<ProfileKeyedAPIFactory<WebNavigationAPI> >
    850 g_factory = LAZY_INSTANCE_INITIALIZER;
    851 
    852 // static
    853 ProfileKeyedAPIFactory<WebNavigationAPI>*
    854 WebNavigationAPI::GetFactoryInstance() {
    855   return &g_factory.Get();
    856 }
    857 
    858 void 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