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.
5 // Implements the Chrome Extensions WebNavigation API.
7 #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h"
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"
26 namespace extensions
{
28 namespace keys
= web_navigation_api_constants
;
29 namespace web_navigation
= api::web_navigation
;
31 namespace web_navigation_api_helpers
{
35 // Returns |time| as milliseconds since the epoch.
36 double MilliSecondsFromTime(const base::Time
& time
) {
37 return 1000 * time
.ToDoubleT();
40 // Dispatches events to the extension message service.
41 void DispatchEvent(content::BrowserContext
* browser_context
,
42 events::HistogramValue histogram_value
,
43 const std::string
& event_name
,
44 scoped_ptr
<base::ListValue
> args
,
46 EventFilteringInfo info
;
49 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
50 EventRouter
* event_router
= EventRouter::Get(profile
);
51 if (profile
&& event_router
) {
52 scoped_ptr
<Event
> event(
53 new Event(histogram_value
, event_name
, args
.Pass()));
54 event
->restrict_to_browser_context
= profile
;
55 event
->filter_info
= info
;
56 event_router
->BroadcastEvent(event
.Pass());
62 int GetFrameId(content::RenderFrameHost
* frame_host
) {
65 return !frame_host
->GetParent() ? 0 : frame_host
->GetRoutingID();
68 // Constructs and dispatches an onBeforeNavigate event.
69 // TODO(dcheng): Is the parent process ID needed here? http://crbug.com/393640
70 // Collisions are probably possible... but maybe this won't ever happen because
71 // of the SiteInstance grouping policies.
72 void DispatchOnBeforeNavigate(content::WebContents
* web_contents
,
73 content::RenderFrameHost
* frame_host
,
74 const GURL
& validated_url
) {
75 scoped_ptr
<base::ListValue
> args(new base::ListValue());
76 base::DictionaryValue
* dict
= new base::DictionaryValue();
77 dict
->SetInteger(keys::kTabIdKey
, ExtensionTabUtil::GetTabId(web_contents
));
78 dict
->SetString(keys::kUrlKey
, validated_url
.spec());
79 dict
->SetInteger(keys::kProcessIdKey
, frame_host
->GetProcess()->GetID());
80 dict
->SetInteger(keys::kFrameIdKey
, GetFrameId(frame_host
));
81 dict
->SetInteger(keys::kParentFrameIdKey
,
82 GetFrameId(frame_host
->GetParent()));
83 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
86 DispatchEvent(web_contents
->GetBrowserContext(),
87 events::WEB_NAVIGATION_ON_BEFORE_NAVIGATE
,
88 web_navigation::OnBeforeNavigate::kEventName
, args
.Pass(),
92 // Constructs and dispatches an onCommitted or onReferenceFragmentUpdated
94 void DispatchOnCommitted(events::HistogramValue histogram_value
,
95 const std::string
& event_name
,
96 content::WebContents
* web_contents
,
97 content::RenderFrameHost
* frame_host
,
99 ui::PageTransition transition_type
) {
100 scoped_ptr
<base::ListValue
> args(new base::ListValue());
101 base::DictionaryValue
* dict
= new base::DictionaryValue();
102 dict
->SetInteger(keys::kTabIdKey
, ExtensionTabUtil::GetTabId(web_contents
));
103 dict
->SetString(keys::kUrlKey
, url
.spec());
104 dict
->SetInteger(keys::kProcessIdKey
, frame_host
->GetProcess()->GetID());
105 dict
->SetInteger(keys::kFrameIdKey
, GetFrameId(frame_host
));
106 std::string transition_type_string
=
107 ui::PageTransitionGetCoreTransitionString(transition_type
);
108 // For webNavigation API backward compatibility, keep "start_page" even after
109 // renamed to "auto_toplevel".
110 if (ui::PageTransitionStripQualifier(transition_type
) ==
111 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
)
112 transition_type_string
= "start_page";
113 dict
->SetString(keys::kTransitionTypeKey
, transition_type_string
);
114 base::ListValue
* qualifiers
= new base::ListValue();
115 if (transition_type
& ui::PAGE_TRANSITION_CLIENT_REDIRECT
)
116 qualifiers
->Append(new base::StringValue("client_redirect"));
117 if (transition_type
& ui::PAGE_TRANSITION_SERVER_REDIRECT
)
118 qualifiers
->Append(new base::StringValue("server_redirect"));
119 if (transition_type
& ui::PAGE_TRANSITION_FORWARD_BACK
)
120 qualifiers
->Append(new base::StringValue("forward_back"));
121 if (transition_type
& ui::PAGE_TRANSITION_FROM_ADDRESS_BAR
)
122 qualifiers
->Append(new base::StringValue("from_address_bar"));
123 dict
->Set(keys::kTransitionQualifiersKey
, qualifiers
);
124 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
127 DispatchEvent(web_contents
->GetBrowserContext(), histogram_value
, event_name
,
131 // Constructs and dispatches an onDOMContentLoaded event.
132 void DispatchOnDOMContentLoaded(content::WebContents
* web_contents
,
133 content::RenderFrameHost
* frame_host
,
135 scoped_ptr
<base::ListValue
> args(new base::ListValue());
136 base::DictionaryValue
* dict
= new base::DictionaryValue();
137 dict
->SetInteger(keys::kTabIdKey
,
138 ExtensionTabUtil::GetTabId(web_contents
));
139 dict
->SetString(keys::kUrlKey
, url
.spec());
140 dict
->SetInteger(keys::kProcessIdKey
, frame_host
->GetProcess()->GetID());
141 dict
->SetInteger(keys::kFrameIdKey
, GetFrameId(frame_host
));
142 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
145 DispatchEvent(web_contents
->GetBrowserContext(),
146 events::WEB_NAVIGATION_ON_DOM_CONTENT_LOADED
,
147 web_navigation::OnDOMContentLoaded::kEventName
, args
.Pass(),
151 // Constructs and dispatches an onCompleted event.
152 void DispatchOnCompleted(content::WebContents
* web_contents
,
153 content::RenderFrameHost
* frame_host
,
155 scoped_ptr
<base::ListValue
> args(new base::ListValue());
156 base::DictionaryValue
* dict
= new base::DictionaryValue();
157 dict
->SetInteger(keys::kTabIdKey
,
158 ExtensionTabUtil::GetTabId(web_contents
));
159 dict
->SetString(keys::kUrlKey
, url
.spec());
160 dict
->SetInteger(keys::kProcessIdKey
, frame_host
->GetProcess()->GetID());
161 dict
->SetInteger(keys::kFrameIdKey
, GetFrameId(frame_host
));
162 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
165 DispatchEvent(web_contents
->GetBrowserContext(),
166 events::WEB_NAVIGATION_ON_COMPLETED
,
167 web_navigation::OnCompleted::kEventName
, args
.Pass(), url
);
170 // Constructs and dispatches an onCreatedNavigationTarget event.
171 void DispatchOnCreatedNavigationTarget(
172 content::WebContents
* web_contents
,
173 content::BrowserContext
* browser_context
,
174 content::RenderFrameHost
* source_frame_host
,
175 content::WebContents
* target_web_contents
,
176 const GURL
& target_url
) {
177 // Check that the tab is already inserted into a tab strip model. This code
178 // path is exercised by ExtensionApiTest.WebNavigationRequestOpenTab.
179 DCHECK(ExtensionTabUtil::GetTabById(
180 ExtensionTabUtil::GetTabId(target_web_contents
),
181 Profile::FromBrowserContext(target_web_contents
->GetBrowserContext()),
182 false, NULL
, NULL
, NULL
, NULL
));
184 scoped_ptr
<base::ListValue
> args(new base::ListValue());
185 base::DictionaryValue
* dict
= new base::DictionaryValue();
186 dict
->SetInteger(keys::kSourceTabIdKey
,
187 ExtensionTabUtil::GetTabId(web_contents
));
188 dict
->SetInteger(keys::kSourceProcessIdKey
,
189 source_frame_host
->GetProcess()->GetID());
190 dict
->SetInteger(keys::kSourceFrameIdKey
, GetFrameId(source_frame_host
));
191 dict
->SetString(keys::kUrlKey
, target_url
.possibly_invalid_spec());
192 dict
->SetInteger(keys::kTabIdKey
,
193 ExtensionTabUtil::GetTabId(target_web_contents
));
194 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
197 DispatchEvent(browser_context
,
198 events::WEB_NAVIGATION_ON_CREATED_NAVIGATION_TARGET
,
199 web_navigation::OnCreatedNavigationTarget::kEventName
,
200 args
.Pass(), target_url
);
203 // Constructs and dispatches an onErrorOccurred event.
204 void DispatchOnErrorOccurred(content::WebContents
* web_contents
,
205 content::RenderFrameHost
* frame_host
,
208 scoped_ptr
<base::ListValue
> args(new base::ListValue());
209 base::DictionaryValue
* dict
= new base::DictionaryValue();
210 dict
->SetInteger(keys::kTabIdKey
, ExtensionTabUtil::GetTabId(web_contents
));
211 dict
->SetString(keys::kUrlKey
, url
.spec());
212 dict
->SetInteger(keys::kProcessIdKey
, frame_host
->GetProcess()->GetID());
213 dict
->SetInteger(keys::kFrameIdKey
, GetFrameId(frame_host
));
214 dict
->SetString(keys::kErrorKey
, net::ErrorToString(error_code
));
215 dict
->SetDouble(keys::kTimeStampKey
,
216 MilliSecondsFromTime(base::Time::Now()));
219 DispatchEvent(web_contents
->GetBrowserContext(),
220 events::WEB_NAVIGATION_ON_ERROR_OCCURRED
,
221 web_navigation::OnErrorOccurred::kEventName
, args
.Pass(), url
);
224 // Constructs and dispatches an onTabReplaced event.
225 void DispatchOnTabReplaced(
226 content::WebContents
* old_web_contents
,
227 content::BrowserContext
* browser_context
,
228 content::WebContents
* new_web_contents
) {
229 scoped_ptr
<base::ListValue
> args(new base::ListValue());
230 base::DictionaryValue
* dict
= new base::DictionaryValue();
231 dict
->SetInteger(keys::kReplacedTabIdKey
,
232 ExtensionTabUtil::GetTabId(old_web_contents
));
235 ExtensionTabUtil::GetTabId(new_web_contents
));
236 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
239 DispatchEvent(browser_context
, events::WEB_NAVIGATION_ON_TAB_REPLACED
,
240 web_navigation::OnTabReplaced::kEventName
, args
.Pass(), GURL());
243 } // namespace web_navigation_api_helpers
245 } // namespace extensions