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 const std::string
& event_name
,
43 scoped_ptr
<base::ListValue
> args
,
45 EventFilteringInfo info
;
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());
60 int GetFrameId(content::RenderFrameHost
* frame_host
) {
63 return !frame_host
->GetParent() ? 0 : frame_host
->GetRoutingID();
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()));
84 DispatchEvent(web_contents
->GetBrowserContext(),
85 web_navigation::OnBeforeNavigate::kEventName
,
90 // Constructs and dispatches an onCommitted or onReferenceFragmentUpdated
92 void DispatchOnCommitted(const std::string
& event_name
,
93 content::WebContents
* web_contents
,
94 content::RenderFrameHost
* frame_host
,
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()));
124 DispatchEvent(web_contents
->GetBrowserContext(), event_name
, args
.Pass(),
128 // Constructs and dispatches an onDOMContentLoaded event.
129 void DispatchOnDOMContentLoaded(content::WebContents
* web_contents
,
130 content::RenderFrameHost
* frame_host
,
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()));
142 DispatchEvent(web_contents
->GetBrowserContext(),
143 web_navigation::OnDOMContentLoaded::kEventName
,
148 // Constructs and dispatches an onCompleted event.
149 void DispatchOnCompleted(content::WebContents
* web_contents
,
150 content::RenderFrameHost
* frame_host
,
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()));
162 DispatchEvent(web_contents
->GetBrowserContext(),
163 web_navigation::OnCompleted::kEventName
,
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
));
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()));
194 DispatchEvent(browser_context
,
195 web_navigation::OnCreatedNavigationTarget::kEventName
,
200 // Constructs and dispatches an onErrorOccurred event.
201 void DispatchOnErrorOccurred(content::WebContents
* web_contents
,
202 content::RenderFrameHost
* frame_host
,
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()));
216 DispatchEvent(web_contents
->GetBrowserContext(),
217 web_navigation::OnErrorOccurred::kEventName
,
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
));
232 ExtensionTabUtil::GetTabId(new_web_contents
));
233 dict
->SetDouble(keys::kTimeStampKey
, MilliSecondsFromTime(base::Time::Now()));
236 DispatchEvent(browser_context
,
237 web_navigation::OnTabReplaced::kEventName
,
242 } // namespace web_navigation_api_helpers
244 } // namespace extensions