• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/extension_webnavigation_api.h"
8 
9 #include "base/json/json_writer.h"
10 #include "base/string_number_conversions.h"
11 #include "base/time.h"
12 #include "base/values.h"
13 #include "chrome/browser/extensions/extension_event_router.h"
14 #include "chrome/browser/extensions/extension_tabs_module.h"
15 #include "chrome/browser/extensions/extension_webnavigation_api_constants.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/url_constants.h"
18 #include "content/browser/tab_contents/tab_contents.h"
19 #include "content/common/notification_service.h"
20 #include "content/common/view_messages.h"
21 #include "net/base/net_errors.h"
22 
23 namespace keys = extension_webnavigation_api_constants;
24 
25 namespace {
26 
27 // URL schemes for which we'll send events.
28 const char* kValidSchemes[] = {
29   chrome::kHttpScheme,
30   chrome::kHttpsScheme,
31   chrome::kFileScheme,
32   chrome::kFtpScheme,
33 };
34 
35 // Returns 0 if the navigation happens in the main frame, or the frame ID
36 // modulo 32 bits otherwise.
GetFrameId(bool is_main_frame,int64 frame_id)37 int GetFrameId(bool is_main_frame, int64 frame_id) {
38   return is_main_frame ? 0 : static_cast<int>(frame_id);
39 }
40 
41 // Returns |time| as milliseconds since the epoch.
MilliSecondsFromTime(const base::Time & time)42 double MilliSecondsFromTime(const base::Time& time) {
43   return 1000 * time.ToDoubleT();
44 }
45 
46 // Dispatches events to the extension message service.
DispatchEvent(Profile * profile,const char * event_name,const std::string & json_args)47 void DispatchEvent(Profile* profile,
48                    const char* event_name,
49                    const std::string& json_args) {
50   if (profile && profile->GetExtensionEventRouter()) {
51     profile->GetExtensionEventRouter()->DispatchEventToRenderers(
52         event_name, json_args, profile, GURL());
53   }
54 }
55 
56 // Constructs and dispatches an onBeforeNavigate event.
DispatchOnBeforeNavigate(TabContents * tab_contents,int64 frame_id,bool is_main_frame,const GURL & validated_url,uint64 request_id)57 void DispatchOnBeforeNavigate(TabContents* tab_contents,
58                               int64 frame_id,
59                               bool is_main_frame,
60                               const GURL& validated_url,
61                               uint64 request_id) {
62   ListValue args;
63   DictionaryValue* dict = new DictionaryValue();
64   dict->SetInteger(keys::kTabIdKey,
65                    ExtensionTabUtil::GetTabId(tab_contents));
66   dict->SetString(keys::kUrlKey, validated_url.spec());
67   dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id));
68   dict->SetString(keys::kRequestIdKey,
69                   base::Uint64ToString(request_id));
70   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
71   args.Append(dict);
72 
73   std::string json_args;
74   base::JSONWriter::Write(&args, false, &json_args);
75   DispatchEvent(tab_contents->profile(), keys::kOnBeforeNavigate, json_args);
76 }
77 
78 // Constructs and dispatches an onCommitted event.
DispatchOnCommitted(TabContents * tab_contents,int64 frame_id,bool is_main_frame,const GURL & url,PageTransition::Type transition_type)79 void DispatchOnCommitted(TabContents* tab_contents,
80                          int64 frame_id,
81                          bool is_main_frame,
82                          const GURL& url,
83                          PageTransition::Type transition_type) {
84   ListValue args;
85   DictionaryValue* dict = new DictionaryValue();
86   dict->SetInteger(keys::kTabIdKey,
87                    ExtensionTabUtil::GetTabId(tab_contents));
88   dict->SetString(keys::kUrlKey, url.spec());
89   dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id));
90   dict->SetString(keys::kTransitionTypeKey,
91                   PageTransition::CoreTransitionString(transition_type));
92   ListValue* qualifiers = new ListValue();
93   if (transition_type & PageTransition::CLIENT_REDIRECT)
94     qualifiers->Append(Value::CreateStringValue("client_redirect"));
95   if (transition_type & PageTransition::SERVER_REDIRECT)
96     qualifiers->Append(Value::CreateStringValue("server_redirect"));
97   if (transition_type & PageTransition::FORWARD_BACK)
98     qualifiers->Append(Value::CreateStringValue("forward_back"));
99   dict->Set(keys::kTransitionQualifiersKey, qualifiers);
100   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
101   args.Append(dict);
102 
103   std::string json_args;
104   base::JSONWriter::Write(&args, false, &json_args);
105   DispatchEvent(tab_contents->profile(), keys::kOnCommitted, json_args);
106 }
107 
108 // Constructs and dispatches an onDOMContentLoaded event.
DispatchOnDOMContentLoaded(TabContents * tab_contents,const GURL & url,bool is_main_frame,int64 frame_id)109 void DispatchOnDOMContentLoaded(TabContents* tab_contents,
110                                 const GURL& url,
111                                 bool is_main_frame,
112                                 int64 frame_id) {
113   ListValue args;
114   DictionaryValue* dict = new DictionaryValue();
115   dict->SetInteger(keys::kTabIdKey,
116                    ExtensionTabUtil::GetTabId(tab_contents));
117   dict->SetString(keys::kUrlKey, url.spec());
118   dict->SetInteger(keys::kFrameIdKey,
119       is_main_frame ? 0 : static_cast<int>(frame_id));
120   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
121   args.Append(dict);
122 
123   std::string json_args;
124   base::JSONWriter::Write(&args, false, &json_args);
125   DispatchEvent(tab_contents->profile(), keys::kOnDOMContentLoaded, json_args);
126 }
127 
128 // Constructs and dispatches an onCompleted event.
DispatchOnCompleted(TabContents * tab_contents,const GURL & url,bool is_main_frame,int64 frame_id)129 void DispatchOnCompleted(TabContents* tab_contents,
130                          const GURL& url,
131                          bool is_main_frame,
132                          int64 frame_id) {
133   ListValue args;
134   DictionaryValue* dict = new DictionaryValue();
135   dict->SetInteger(keys::kTabIdKey,
136                    ExtensionTabUtil::GetTabId(tab_contents));
137   dict->SetString(keys::kUrlKey, url.spec());
138   dict->SetInteger(keys::kFrameIdKey,
139       is_main_frame ? 0 : static_cast<int>(frame_id));
140   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
141   args.Append(dict);
142 
143   std::string json_args;
144   base::JSONWriter::Write(&args, false, &json_args);
145   DispatchEvent(tab_contents->profile(), keys::kOnCompleted, json_args);
146 }
147 
148 // Constructs and dispatches an onBeforeRetarget event.
DispatchOnBeforeRetarget(TabContents * tab_contents,Profile * profile,const GURL & opener_url,const GURL & target_url)149 void DispatchOnBeforeRetarget(TabContents* tab_contents,
150                               Profile* profile,
151                               const GURL& opener_url,
152                               const GURL& target_url) {
153   ListValue args;
154   DictionaryValue* dict = new DictionaryValue();
155   dict->SetInteger(keys::kSourceTabIdKey,
156                    ExtensionTabUtil::GetTabId(tab_contents));
157   dict->SetString(keys::kSourceUrlKey, opener_url.spec());
158   dict->SetString(keys::kUrlKey, target_url.possibly_invalid_spec());
159   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
160   args.Append(dict);
161 
162   std::string json_args;
163   base::JSONWriter::Write(&args, false, &json_args);
164   DispatchEvent(profile, keys::kOnBeforeRetarget, json_args);
165 }
166 
167 }  // namespace
168 
169 
170 // FrameNavigationState -------------------------------------------------------
171 
172 // static
173 bool FrameNavigationState::allow_extension_scheme_ = false;
174 
FrameNavigationState()175 FrameNavigationState::FrameNavigationState() {}
176 
~FrameNavigationState()177 FrameNavigationState::~FrameNavigationState() {}
178 
CanSendEvents(int64 frame_id) const179 bool FrameNavigationState::CanSendEvents(int64 frame_id) const {
180   FrameIdToStateMap::const_iterator frame_state =
181       frame_state_map_.find(frame_id);
182   if (frame_state == frame_state_map_.end() ||
183       frame_state->second.error_occurred) {
184     return false;
185   }
186   const std::string& scheme = frame_state->second.url.scheme();
187   for (unsigned i = 0; i < arraysize(kValidSchemes); ++i) {
188     if (scheme == kValidSchemes[i])
189       return true;
190   }
191   if (allow_extension_scheme_ && scheme == chrome::kExtensionScheme)
192     return true;
193   return false;
194 }
195 
TrackFrame(int64 frame_id,const GURL & url,bool is_main_frame,bool is_error_page,const TabContents * tab_contents)196 void FrameNavigationState::TrackFrame(int64 frame_id,
197                                       const GURL& url,
198                                       bool is_main_frame,
199                                       bool is_error_page,
200                                       const TabContents* tab_contents) {
201   if (is_main_frame)
202     RemoveTabContentsState(tab_contents);
203   tab_contents_map_.insert(std::make_pair(tab_contents, frame_id));
204   FrameState& frame_state = frame_state_map_[frame_id];
205   frame_state.error_occurred = is_error_page;
206   frame_state.url = url;
207   frame_state.is_main_frame = is_main_frame;
208 }
209 
GetUrl(int64 frame_id) const210 GURL FrameNavigationState::GetUrl(int64 frame_id) const {
211   FrameIdToStateMap::const_iterator frame_state =
212       frame_state_map_.find(frame_id);
213   if (frame_state == frame_state_map_.end()) {
214     NOTREACHED();
215     return GURL();
216   }
217   return frame_state->second.url;
218 }
219 
IsMainFrame(int64 frame_id) const220 bool FrameNavigationState::IsMainFrame(int64 frame_id) const {
221   FrameIdToStateMap::const_iterator frame_state =
222       frame_state_map_.find(frame_id);
223   if (frame_state == frame_state_map_.end()) {
224     NOTREACHED();
225     return false;
226   }
227   return frame_state->second.is_main_frame;
228 }
229 
ErrorOccurredInFrame(int64 frame_id)230 void FrameNavigationState::ErrorOccurredInFrame(int64 frame_id) {
231   DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end());
232   frame_state_map_[frame_id].error_occurred = true;
233 }
234 
RemoveTabContentsState(const TabContents * tab_contents)235 void FrameNavigationState::RemoveTabContentsState(
236     const TabContents* tab_contents) {
237   typedef TabContentsToFrameIdMap::iterator FrameIdIterator;
238   std::pair<FrameIdIterator, FrameIdIterator> frame_ids =
239       tab_contents_map_.equal_range(tab_contents);
240   for (FrameIdIterator frame_id = frame_ids.first; frame_id != frame_ids.second;
241        ++frame_id) {
242     frame_state_map_.erase(frame_id->second);
243   }
244   tab_contents_map_.erase(tab_contents);
245 }
246 
247 
248 // ExtensionWebNavigtionEventRouter -------------------------------------------
249 
ExtensionWebNavigationEventRouter()250 ExtensionWebNavigationEventRouter::ExtensionWebNavigationEventRouter() {}
251 
~ExtensionWebNavigationEventRouter()252 ExtensionWebNavigationEventRouter::~ExtensionWebNavigationEventRouter() {}
253 
254 // static
255 ExtensionWebNavigationEventRouter*
GetInstance()256 ExtensionWebNavigationEventRouter::GetInstance() {
257   return Singleton<ExtensionWebNavigationEventRouter>::get();
258 }
259 
Init()260 void ExtensionWebNavigationEventRouter::Init() {
261   if (registrar_.IsEmpty()) {
262     registrar_.Add(this,
263                    NotificationType::CREATING_NEW_WINDOW,
264                    NotificationService::AllSources());
265   }
266 }
267 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)268 void ExtensionWebNavigationEventRouter::Observe(
269     NotificationType type,
270     const NotificationSource& source,
271     const NotificationDetails& details) {
272   switch (type.value) {
273     case NotificationType::CREATING_NEW_WINDOW:
274       CreatingNewWindow(
275           Source<TabContents>(source).ptr(),
276           Details<const ViewHostMsg_CreateWindow_Params>(details).ptr());
277       break;
278 
279     default:
280       NOTREACHED();
281   }
282 }
283 
CreatingNewWindow(TabContents * tab_contents,const ViewHostMsg_CreateWindow_Params * details)284 void ExtensionWebNavigationEventRouter::CreatingNewWindow(
285     TabContents* tab_contents,
286     const ViewHostMsg_CreateWindow_Params* details) {
287   DispatchOnBeforeRetarget(tab_contents,
288                            tab_contents->profile(),
289                            details->opener_url,
290                            details->target_url);
291 }
292 
293 
294 // ExtensionWebNavigationTabObserver ------------------------------------------
295 
ExtensionWebNavigationTabObserver(TabContents * tab_contents)296 ExtensionWebNavigationTabObserver::ExtensionWebNavigationTabObserver(
297     TabContents* tab_contents)
298     : TabContentsObserver(tab_contents) {}
299 
~ExtensionWebNavigationTabObserver()300 ExtensionWebNavigationTabObserver::~ExtensionWebNavigationTabObserver() {}
301 
DidStartProvisionalLoadForFrame(int64 frame_id,bool is_main_frame,const GURL & validated_url,bool is_error_page)302 void ExtensionWebNavigationTabObserver::DidStartProvisionalLoadForFrame(
303     int64 frame_id,
304     bool is_main_frame,
305     const GURL& validated_url,
306     bool is_error_page) {
307   navigation_state_.TrackFrame(frame_id,
308                                validated_url,
309                                is_main_frame,
310                                is_error_page,
311                                tab_contents());
312   if (!navigation_state_.CanSendEvents(frame_id))
313     return;
314   DispatchOnBeforeNavigate(
315       tab_contents(), frame_id, is_main_frame, validated_url, 0);
316 }
317 
DidCommitProvisionalLoadForFrame(int64 frame_id,bool is_main_frame,const GURL & url,PageTransition::Type transition_type)318 void ExtensionWebNavigationTabObserver::DidCommitProvisionalLoadForFrame(
319     int64 frame_id,
320     bool is_main_frame,
321     const GURL& url,
322     PageTransition::Type transition_type) {
323   if (!navigation_state_.CanSendEvents(frame_id))
324     return;
325   // On reference fragment navigations, only a new navigation state is
326   // committed. We need to catch this case and generate a full sequence
327   // of events.
328   if (IsReferenceFragmentNavigation(frame_id, url)) {
329     NavigatedReferenceFragment(frame_id, is_main_frame, url, transition_type);
330     return;
331   }
332   DispatchOnCommitted(
333       tab_contents(), frame_id, is_main_frame, url, transition_type);
334 }
335 
DidFailProvisionalLoad(int64 frame_id,bool is_main_frame,const GURL & validated_url,int error_code)336 void ExtensionWebNavigationTabObserver::DidFailProvisionalLoad(
337     int64 frame_id,
338     bool is_main_frame,
339     const GURL& validated_url,
340     int error_code) {
341   if (!navigation_state_.CanSendEvents(frame_id))
342     return;
343   ListValue args;
344   DictionaryValue* dict = new DictionaryValue();
345   dict->SetInteger(keys::kTabIdKey,
346                    ExtensionTabUtil::GetTabId(tab_contents()));
347   dict->SetString(keys::kUrlKey, validated_url.spec());
348   dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id));
349   dict->SetString(keys::kErrorKey,
350                   std::string(net::ErrorToString(error_code)));
351   dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
352   args.Append(dict);
353 
354   std::string json_args;
355   base::JSONWriter::Write(&args, false, &json_args);
356   navigation_state_.ErrorOccurredInFrame(frame_id);
357   DispatchEvent(tab_contents()->profile(), keys::kOnErrorOccurred, json_args);
358 }
359 
DocumentLoadedInFrame(int64 frame_id)360 void ExtensionWebNavigationTabObserver::DocumentLoadedInFrame(
361     int64 frame_id) {
362   if (!navigation_state_.CanSendEvents(frame_id))
363     return;
364   DispatchOnDOMContentLoaded(tab_contents(),
365                              navigation_state_.GetUrl(frame_id),
366                              navigation_state_.IsMainFrame(frame_id),
367                              frame_id);
368 }
369 
DidFinishLoad(int64 frame_id)370 void ExtensionWebNavigationTabObserver::DidFinishLoad(
371     int64 frame_id) {
372   if (!navigation_state_.CanSendEvents(frame_id))
373     return;
374   DispatchOnCompleted(tab_contents(),
375                       navigation_state_.GetUrl(frame_id),
376                       navigation_state_.IsMainFrame(frame_id),
377                       frame_id);
378 }
379 
TabContentsDestroyed(TabContents * tab)380 void ExtensionWebNavigationTabObserver::TabContentsDestroyed(
381     TabContents* tab) {
382   navigation_state_.RemoveTabContentsState(tab);
383 }
384 
DidOpenURL(const GURL & url,const GURL & referrer,WindowOpenDisposition disposition,PageTransition::Type transition)385 void ExtensionWebNavigationTabObserver::DidOpenURL(
386     const GURL& url,
387     const GURL& referrer,
388     WindowOpenDisposition disposition,
389     PageTransition::Type transition) {
390   if (disposition != NEW_FOREGROUND_TAB &&
391       disposition != NEW_BACKGROUND_TAB &&
392       disposition != NEW_WINDOW &&
393       disposition != OFF_THE_RECORD) {
394     return;
395   }
396   Profile* profile = tab_contents()->profile();
397   if (disposition == OFF_THE_RECORD) {
398     if (!profile->HasOffTheRecordProfile()) {
399       NOTREACHED();
400       return;
401     }
402     profile = profile->GetOffTheRecordProfile();
403   }
404   DispatchOnBeforeRetarget(tab_contents(),
405                            profile,
406                            tab_contents()->GetURL(),
407                            url);
408 }
409 
410 // See also NavigationController::IsURLInPageNavigation.
IsReferenceFragmentNavigation(int64 frame_id,const GURL & url)411 bool ExtensionWebNavigationTabObserver::IsReferenceFragmentNavigation(
412     int64 frame_id,
413     const GURL& url) {
414   GURL existing_url = navigation_state_.GetUrl(frame_id);
415   if (existing_url == url)
416     return false;
417 
418   url_canon::Replacements<char> replacements;
419   replacements.ClearRef();
420   return existing_url.ReplaceComponents(replacements) ==
421       url.ReplaceComponents(replacements);
422 }
423 
NavigatedReferenceFragment(int64 frame_id,bool is_main_frame,const GURL & url,PageTransition::Type transition_type)424 void ExtensionWebNavigationTabObserver::NavigatedReferenceFragment(
425     int64 frame_id,
426     bool is_main_frame,
427     const GURL& url,
428     PageTransition::Type transition_type) {
429   navigation_state_.TrackFrame(frame_id,
430                                url,
431                                is_main_frame,
432                                false,
433                                tab_contents());
434 
435   DispatchOnBeforeNavigate(tab_contents(),
436                            frame_id,
437                            is_main_frame,
438                            url,
439                            0);
440   DispatchOnCommitted(tab_contents(),
441                       frame_id,
442                       is_main_frame,
443                       url,
444                       transition_type);
445   DispatchOnDOMContentLoaded(tab_contents(),
446                              url,
447                              is_main_frame,
448                              frame_id);
449   DispatchOnCompleted(tab_contents(),
450                       url,
451                       is_main_frame,
452                       frame_id);
453 }
454