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_helpers.h"
      8 
      9 #include "base/json/json_writer.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/time/time.h"
     12 #include "base/values.h"
     13 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_constants.h"
     14 #include "chrome/browser/extensions/extension_tab_util.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/common/extensions/api/web_navigation.h"
     17 #include "content/public/browser/render_frame_host.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "content/public/browser/web_contents.h"
     21 #include "extensions/browser/event_router.h"
     22 #include "extensions/common/event_filtering_info.h"
     23 #include "net/base/net_errors.h"
     24 #include "ui/base/page_transition_types.h"
     25 
     26 namespace extensions {
     27 
     28 namespace keys = web_navigation_api_constants;
     29 namespace web_navigation = api::web_navigation;
     30 
     31 namespace web_navigation_api_helpers {
     32 
     33 namespace {
     34 
     35 // Returns |time| as milliseconds since the epoch.
     36 double MilliSecondsFromTime(const base::Time& time) {
     37   return 1000 * time.ToDoubleT();
     38 }
     39 
     40 // Dispatches events to the extension message service.
     41 void DispatchEvent(content::BrowserContext* browser_context,
     42                    const std::string& event_name,
     43                    scoped_ptr<base::ListValue> args,
     44                    const GURL& url) {
     45   EventFilteringInfo info;
     46   info.SetURL(url);
     47 
     48   Profile* profile = Profile::FromBrowserContext(browser_context);
     49   EventRouter* event_router = EventRouter::Get(profile);
     50   if (profile && event_router) {
     51     scoped_ptr<Event> event(new Event(event_name, args.Pass()));
     52     event->restrict_to_browser_context = profile;
     53     event->filter_info = info;
     54     event_router->BroadcastEvent(event.Pass());
     55   }
     56 }
     57 
     58 }  // namespace
     59 
     60 int GetFrameId(content::RenderFrameHost* frame_host) {
     61   if (!frame_host)
     62     return -1;
     63   return !frame_host->GetParent() ? 0 : frame_host->GetRoutingID();
     64 }
     65 
     66 // Constructs and dispatches an onBeforeNavigate event.
     67 // TODO(dcheng): Is the parent process ID needed here? http://crbug.com/393640
     68 // Collisions are probably possible... but maybe this won't ever happen because
     69 // of the SiteInstance grouping policies.
     70 void DispatchOnBeforeNavigate(content::WebContents* web_contents,
     71                               content::RenderFrameHost* frame_host,
     72                               const GURL& validated_url) {
     73   scoped_ptr<base::ListValue> args(new base::ListValue());
     74   base::DictionaryValue* dict = new base::DictionaryValue();
     75   dict->SetInteger(keys::kTabIdKey, ExtensionTabUtil::GetTabId(web_contents));
     76   dict->SetString(keys::kUrlKey, validated_url.spec());
     77   dict->SetInteger(keys::kProcessIdKey, frame_host->GetProcess()->GetID());
     78   dict->SetInteger(keys::kFrameIdKey, GetFrameId(frame_host));
     79   dict->SetInteger(keys::kParentFrameIdKey,
     80                    GetFrameId(frame_host->GetParent()));
     81   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
     82   args->Append(dict);
     83 
     84   DispatchEvent(web_contents->GetBrowserContext(),
     85                 web_navigation::OnBeforeNavigate::kEventName,
     86                 args.Pass(),
     87                 validated_url);
     88 }
     89 
     90 // Constructs and dispatches an onCommitted or onReferenceFragmentUpdated
     91 // event.
     92 void DispatchOnCommitted(const std::string& event_name,
     93                          content::WebContents* web_contents,
     94                          content::RenderFrameHost* frame_host,
     95                          const GURL& url,
     96                          ui::PageTransition transition_type) {
     97   scoped_ptr<base::ListValue> args(new base::ListValue());
     98   base::DictionaryValue* dict = new base::DictionaryValue();
     99   dict->SetInteger(keys::kTabIdKey, ExtensionTabUtil::GetTabId(web_contents));
    100   dict->SetString(keys::kUrlKey, url.spec());
    101   dict->SetInteger(keys::kProcessIdKey, frame_host->GetProcess()->GetID());
    102   dict->SetInteger(keys::kFrameIdKey, GetFrameId(frame_host));
    103   std::string transition_type_string =
    104       ui::PageTransitionGetCoreTransitionString(transition_type);
    105   // For webNavigation API backward compatibility, keep "start_page" even after
    106   // renamed to "auto_toplevel".
    107   if (ui::PageTransitionStripQualifier(transition_type) ==
    108           ui::PAGE_TRANSITION_AUTO_TOPLEVEL)
    109     transition_type_string = "start_page";
    110   dict->SetString(keys::kTransitionTypeKey, transition_type_string);
    111   base::ListValue* qualifiers = new base::ListValue();
    112   if (transition_type & ui::PAGE_TRANSITION_CLIENT_REDIRECT)
    113     qualifiers->Append(new base::StringValue("client_redirect"));
    114   if (transition_type & ui::PAGE_TRANSITION_SERVER_REDIRECT)
    115     qualifiers->Append(new base::StringValue("server_redirect"));
    116   if (transition_type & ui::PAGE_TRANSITION_FORWARD_BACK)
    117     qualifiers->Append(new base::StringValue("forward_back"));
    118   if (transition_type & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)
    119     qualifiers->Append(new base::StringValue("from_address_bar"));
    120   dict->Set(keys::kTransitionQualifiersKey, qualifiers);
    121   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
    122   args->Append(dict);
    123 
    124   DispatchEvent(web_contents->GetBrowserContext(), event_name, args.Pass(),
    125                 url);
    126 }
    127 
    128 // Constructs and dispatches an onDOMContentLoaded event.
    129 void DispatchOnDOMContentLoaded(content::WebContents* web_contents,
    130                                 content::RenderFrameHost* frame_host,
    131                                 const GURL& url) {
    132   scoped_ptr<base::ListValue> args(new base::ListValue());
    133   base::DictionaryValue* dict = new base::DictionaryValue();
    134   dict->SetInteger(keys::kTabIdKey,
    135                    ExtensionTabUtil::GetTabId(web_contents));
    136   dict->SetString(keys::kUrlKey, url.spec());
    137   dict->SetInteger(keys::kProcessIdKey, frame_host->GetProcess()->GetID());
    138   dict->SetInteger(keys::kFrameIdKey, GetFrameId(frame_host));
    139   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
    140   args->Append(dict);
    141 
    142   DispatchEvent(web_contents->GetBrowserContext(),
    143                 web_navigation::OnDOMContentLoaded::kEventName,
    144                 args.Pass(),
    145                 url);
    146 }
    147 
    148 // Constructs and dispatches an onCompleted event.
    149 void DispatchOnCompleted(content::WebContents* web_contents,
    150                          content::RenderFrameHost* frame_host,
    151                          const GURL& url) {
    152   scoped_ptr<base::ListValue> args(new base::ListValue());
    153   base::DictionaryValue* dict = new base::DictionaryValue();
    154   dict->SetInteger(keys::kTabIdKey,
    155                    ExtensionTabUtil::GetTabId(web_contents));
    156   dict->SetString(keys::kUrlKey, url.spec());
    157   dict->SetInteger(keys::kProcessIdKey, frame_host->GetProcess()->GetID());
    158   dict->SetInteger(keys::kFrameIdKey, GetFrameId(frame_host));
    159   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
    160   args->Append(dict);
    161 
    162   DispatchEvent(web_contents->GetBrowserContext(),
    163                 web_navigation::OnCompleted::kEventName,
    164                 args.Pass(), url);
    165 }
    166 
    167 // Constructs and dispatches an onCreatedNavigationTarget event.
    168 void DispatchOnCreatedNavigationTarget(
    169     content::WebContents* web_contents,
    170     content::BrowserContext* browser_context,
    171     content::RenderFrameHost* source_frame_host,
    172     content::WebContents* target_web_contents,
    173     const GURL& target_url) {
    174   // Check that the tab is already inserted into a tab strip model. This code
    175   // path is exercised by ExtensionApiTest.WebNavigationRequestOpenTab.
    176   DCHECK(ExtensionTabUtil::GetTabById(
    177       ExtensionTabUtil::GetTabId(target_web_contents),
    178       Profile::FromBrowserContext(target_web_contents->GetBrowserContext()),
    179       false, NULL, NULL, NULL, NULL));
    180 
    181   scoped_ptr<base::ListValue> args(new base::ListValue());
    182   base::DictionaryValue* dict = new base::DictionaryValue();
    183   dict->SetInteger(keys::kSourceTabIdKey,
    184                    ExtensionTabUtil::GetTabId(web_contents));
    185   dict->SetInteger(keys::kSourceProcessIdKey,
    186                    source_frame_host->GetProcess()->GetID());
    187   dict->SetInteger(keys::kSourceFrameIdKey, GetFrameId(source_frame_host));
    188   dict->SetString(keys::kUrlKey, target_url.possibly_invalid_spec());
    189   dict->SetInteger(keys::kTabIdKey,
    190                    ExtensionTabUtil::GetTabId(target_web_contents));
    191   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
    192   args->Append(dict);
    193 
    194   DispatchEvent(browser_context,
    195                 web_navigation::OnCreatedNavigationTarget::kEventName,
    196                 args.Pass(),
    197                 target_url);
    198 }
    199 
    200 // Constructs and dispatches an onErrorOccurred event.
    201 void DispatchOnErrorOccurred(content::WebContents* web_contents,
    202                              content::RenderFrameHost* frame_host,
    203                              const GURL& url,
    204                              int error_code) {
    205   scoped_ptr<base::ListValue> args(new base::ListValue());
    206   base::DictionaryValue* dict = new base::DictionaryValue();
    207   dict->SetInteger(keys::kTabIdKey, ExtensionTabUtil::GetTabId(web_contents));
    208   dict->SetString(keys::kUrlKey, url.spec());
    209   dict->SetInteger(keys::kProcessIdKey, frame_host->GetProcess()->GetID());
    210   dict->SetInteger(keys::kFrameIdKey, GetFrameId(frame_host));
    211   dict->SetString(keys::kErrorKey, net::ErrorToString(error_code));
    212   dict->SetDouble(keys::kTimeStampKey,
    213       MilliSecondsFromTime(base::Time::Now()));
    214   args->Append(dict);
    215 
    216   DispatchEvent(web_contents->GetBrowserContext(),
    217                 web_navigation::OnErrorOccurred::kEventName,
    218                 args.Pass(), url);
    219 }
    220 
    221 // Constructs and dispatches an onTabReplaced event.
    222 void DispatchOnTabReplaced(
    223     content::WebContents* old_web_contents,
    224     content::BrowserContext* browser_context,
    225     content::WebContents* new_web_contents) {
    226   scoped_ptr<base::ListValue> args(new base::ListValue());
    227   base::DictionaryValue* dict = new base::DictionaryValue();
    228   dict->SetInteger(keys::kReplacedTabIdKey,
    229                    ExtensionTabUtil::GetTabId(old_web_contents));
    230   dict->SetInteger(
    231       keys::kTabIdKey,
    232       ExtensionTabUtil::GetTabId(new_web_contents));
    233   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
    234   args->Append(dict);
    235 
    236   DispatchEvent(browser_context,
    237                 web_navigation::OnTabReplaced::kEventName,
    238                 args.Pass(),
    239                 GURL());
    240 }
    241 
    242 }  // namespace web_navigation_api_helpers
    243 
    244 }  // namespace extensions
    245