extension_webnavigation_api.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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/time.h"
11#include "base/values.h"
12#include "chrome/browser/extensions/extension_event_router.h"
13#include "chrome/browser/extensions/extension_tabs_module.h"
14#include "chrome/browser/extensions/extension_webnavigation_api_constants.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/tab_contents/navigation_controller.h"
17#include "chrome/browser/tab_contents/provisional_load_details.h"
18#include "chrome/browser/tab_contents/tab_contents.h"
19#include "chrome/common/notification_service.h"
20#include "chrome/common/render_messages_params.h"
21#include "net/base/net_errors.h"
22
23namespace keys = extension_webnavigation_api_constants;
24
25namespace {
26
27// Returns 0 if the navigation happens in the main frame, or the frame ID
28// modulo 32 bits otherwise.
29int GetFrameId(ProvisionalLoadDetails* details) {
30  return details->main_frame() ? 0 : static_cast<int>(details->frame_id());
31}
32
33// Returns |time| as milliseconds since the epoch.
34double MilliSecondsFromTime(const base::Time& time) {
35  return 1000 * time.ToDoubleT();
36}
37
38}  // namespace
39
40
41FrameNavigationState::FrameNavigationState() {
42}
43
44FrameNavigationState::~FrameNavigationState() {
45}
46
47bool FrameNavigationState::CanSendEvents(int64 frame_id) const {
48  FrameIdToStateMap::const_iterator frame_state =
49      frame_state_map_.find(frame_id);
50  return frame_state != frame_state_map_.end() &&
51      !frame_state->second.error_occurred;
52}
53
54void FrameNavigationState::TrackFrame(int64 frame_id,
55                                      const GURL& url,
56                                      bool is_main_frame,
57                                      bool is_error_page,
58                                      const TabContents* tab_contents) {
59  if (is_main_frame)
60    RemoveTabContentsState(tab_contents);
61  tab_contents_map_.insert(std::make_pair(tab_contents, frame_id));
62  FrameState& frame_state = frame_state_map_[frame_id];
63  frame_state.error_occurred = is_error_page;
64  frame_state.url = url;
65  frame_state.is_main_frame = is_main_frame;
66}
67
68GURL FrameNavigationState::GetUrl(int64 frame_id) const {
69  FrameIdToStateMap::const_iterator frame_state =
70      frame_state_map_.find(frame_id);
71  if (frame_state == frame_state_map_.end()) {
72    NOTREACHED();
73    return GURL();
74  }
75  return frame_state->second.url;
76}
77
78bool FrameNavigationState::IsMainFrame(int64 frame_id) const {
79  FrameIdToStateMap::const_iterator frame_state =
80      frame_state_map_.find(frame_id);
81  if (frame_state == frame_state_map_.end()) {
82    NOTREACHED();
83    return false;
84  }
85  return frame_state->second.is_main_frame;
86}
87
88void FrameNavigationState::ErrorOccurredInFrame(int64 frame_id) {
89  DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end());
90  frame_state_map_[frame_id].error_occurred = true;
91}
92
93void FrameNavigationState::RemoveTabContentsState(
94    const TabContents* tab_contents) {
95  typedef TabContentsToFrameIdMap::iterator FrameIdIterator;
96  std::pair<FrameIdIterator, FrameIdIterator> frame_ids =
97      tab_contents_map_.equal_range(tab_contents);
98  for (FrameIdIterator frame_id = frame_ids.first; frame_id != frame_ids.second;
99       ++frame_id) {
100    frame_state_map_.erase(frame_id->second);
101  }
102  tab_contents_map_.erase(tab_contents);
103}
104
105
106// static
107ExtensionWebNavigationEventRouter*
108ExtensionWebNavigationEventRouter::GetInstance() {
109  return Singleton<ExtensionWebNavigationEventRouter>::get();
110}
111
112void ExtensionWebNavigationEventRouter::Init() {
113  if (registrar_.IsEmpty()) {
114    registrar_.Add(this,
115                   NotificationType::FRAME_PROVISIONAL_LOAD_START,
116                   NotificationService::AllSources());
117    registrar_.Add(this,
118                   NotificationType::FRAME_PROVISIONAL_LOAD_COMMITTED,
119                   NotificationService::AllSources());
120    registrar_.Add(this,
121                   NotificationType::FRAME_DOM_CONTENT_LOADED,
122                   NotificationService::AllSources());
123    registrar_.Add(this,
124                   NotificationType::FRAME_DID_FINISH_LOAD,
125                   NotificationService::AllSources());
126    registrar_.Add(this,
127                   NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
128                   NotificationService::AllSources());
129    registrar_.Add(this,
130                   NotificationType::CREATING_NEW_WINDOW,
131                   NotificationService::AllSources());
132    registrar_.Add(this,
133                   NotificationType::TAB_CONTENTS_DESTROYED,
134                   NotificationService::AllSources());
135  }
136}
137
138void ExtensionWebNavigationEventRouter::Observe(
139    NotificationType type,
140    const NotificationSource& source,
141    const NotificationDetails& details) {
142  switch (type.value) {
143    case NotificationType::FRAME_PROVISIONAL_LOAD_START:
144      FrameProvisionalLoadStart(
145          Source<NavigationController>(source).ptr(),
146          Details<ProvisionalLoadDetails>(details).ptr());
147      break;
148    case NotificationType::FRAME_PROVISIONAL_LOAD_COMMITTED:
149      FrameProvisionalLoadCommitted(
150          Source<NavigationController>(source).ptr(),
151          Details<ProvisionalLoadDetails>(details).ptr());
152      break;
153    case NotificationType::FRAME_DOM_CONTENT_LOADED:
154      FrameDomContentLoaded(
155          Source<NavigationController>(source).ptr(),
156          *Details<int64>(details).ptr());
157      break;
158    case NotificationType::FRAME_DID_FINISH_LOAD:
159      FrameDidFinishLoad(
160          Source<NavigationController>(source).ptr(),
161          *Details<int64>(details).ptr());
162      break;
163    case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR:
164      FailProvisionalLoadWithError(
165          Source<NavigationController>(source).ptr(),
166          Details<ProvisionalLoadDetails>(details).ptr());
167      break;
168    case NotificationType::CREATING_NEW_WINDOW:
169      CreatingNewWindow(
170          Source<TabContents>(source).ptr(),
171          Details<const ViewHostMsg_CreateWindow_Params>(details).ptr());
172      break;
173    case NotificationType::TAB_CONTENTS_DESTROYED:
174      navigation_state_.RemoveTabContentsState(
175          Source<TabContents>(source).ptr());
176      break;
177
178    default:
179      NOTREACHED();
180  }
181}
182void ExtensionWebNavigationEventRouter::FrameProvisionalLoadStart(
183    NavigationController* controller,
184    ProvisionalLoadDetails* details) {
185  navigation_state_.TrackFrame(details->frame_id(),
186                               details->url(),
187                               details->main_frame(),
188                               details->is_error_page(),
189                               controller->tab_contents());
190  if (!navigation_state_.CanSendEvents(details->frame_id()))
191    return;
192  ListValue args;
193  DictionaryValue* dict = new DictionaryValue();
194  dict->SetInteger(keys::kTabIdKey,
195                   ExtensionTabUtil::GetTabId(controller->tab_contents()));
196  dict->SetString(keys::kUrlKey, details->url().spec());
197  dict->SetInteger(keys::kFrameIdKey, GetFrameId(details));
198  dict->SetInteger(keys::kRequestIdKey, 0);
199  dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
200  args.Append(dict);
201
202  std::string json_args;
203  base::JSONWriter::Write(&args, false, &json_args);
204  DispatchEvent(controller->profile(), keys::kOnBeforeNavigate, json_args);
205}
206
207void ExtensionWebNavigationEventRouter::FrameProvisionalLoadCommitted(
208    NavigationController* controller,
209    ProvisionalLoadDetails* details) {
210  if (!navigation_state_.CanSendEvents(details->frame_id()))
211    return;
212  ListValue args;
213  DictionaryValue* dict = new DictionaryValue();
214  dict->SetInteger(keys::kTabIdKey,
215                   ExtensionTabUtil::GetTabId(controller->tab_contents()));
216  dict->SetString(keys::kUrlKey, details->url().spec());
217  dict->SetInteger(keys::kFrameIdKey, GetFrameId(details));
218  dict->SetString(keys::kTransitionTypeKey,
219                  PageTransition::CoreTransitionString(
220                      details->transition_type()));
221  dict->SetString(keys::kTransitionQualifiersKey,
222                  PageTransition::QualifierString(
223                      details->transition_type()));
224  dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
225  args.Append(dict);
226
227  std::string json_args;
228  base::JSONWriter::Write(&args, false, &json_args);
229  DispatchEvent(controller->profile(), keys::kOnCommitted, json_args);
230}
231
232void ExtensionWebNavigationEventRouter::FrameDomContentLoaded(
233    NavigationController* controller,
234    int64 frame_id) {
235  if (!navigation_state_.CanSendEvents(frame_id))
236    return;
237  ListValue args;
238  DictionaryValue* dict = new DictionaryValue();
239  dict->SetInteger(keys::kTabIdKey,
240                   ExtensionTabUtil::GetTabId(controller->tab_contents()));
241  dict->SetString(keys::kUrlKey, navigation_state_.GetUrl(frame_id).spec());
242  dict->SetInteger(keys::kFrameIdKey,
243      navigation_state_.IsMainFrame(frame_id) ? 0 : static_cast<int>(frame_id));
244  dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
245  args.Append(dict);
246
247  std::string json_args;
248  base::JSONWriter::Write(&args, false, &json_args);
249  DispatchEvent(controller->profile(), keys::kOnDOMContentLoaded, json_args);
250}
251
252void ExtensionWebNavigationEventRouter::FrameDidFinishLoad(
253    NavigationController* controller,
254    int64 frame_id) {
255  if (!navigation_state_.CanSendEvents(frame_id))
256    return;
257  ListValue args;
258  DictionaryValue* dict = new DictionaryValue();
259  dict->SetInteger(keys::kTabIdKey,
260                   ExtensionTabUtil::GetTabId(controller->tab_contents()));
261  dict->SetString(keys::kUrlKey, navigation_state_.GetUrl(frame_id).spec());
262  dict->SetInteger(keys::kFrameIdKey,
263      navigation_state_.IsMainFrame(frame_id) ? 0 : static_cast<int>(frame_id));
264  dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
265  args.Append(dict);
266
267  std::string json_args;
268  base::JSONWriter::Write(&args, false, &json_args);
269  DispatchEvent(controller->profile(), keys::kOnCompleted, json_args);
270}
271
272void ExtensionWebNavigationEventRouter::FailProvisionalLoadWithError(
273    NavigationController* controller,
274    ProvisionalLoadDetails* details) {
275  if (!navigation_state_.CanSendEvents(details->frame_id()))
276    return;
277  ListValue args;
278  DictionaryValue* dict = new DictionaryValue();
279  dict->SetInteger(keys::kTabIdKey,
280                   ExtensionTabUtil::GetTabId(controller->tab_contents()));
281  dict->SetString(keys::kUrlKey, details->url().spec());
282  dict->SetInteger(keys::kFrameIdKey, GetFrameId(details));
283  dict->SetString(keys::kErrorKey,
284                  std::string(net::ErrorToString(details->error_code())));
285  dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
286  args.Append(dict);
287
288  std::string json_args;
289  base::JSONWriter::Write(&args, false, &json_args);
290  navigation_state_.ErrorOccurredInFrame(details->frame_id());
291  DispatchEvent(controller->profile(), keys::kOnErrorOccurred, json_args);
292}
293
294void ExtensionWebNavigationEventRouter::CreatingNewWindow(
295    TabContents* tab_contents,
296    const ViewHostMsg_CreateWindow_Params* details) {
297  ListValue args;
298  DictionaryValue* dict = new DictionaryValue();
299  dict->SetInteger(keys::kSourceTabIdKey,
300                   ExtensionTabUtil::GetTabId(tab_contents));
301  dict->SetString(keys::kSourceUrlKey, details->opener_url.spec());
302  dict->SetString(keys::kTargetUrlKey,
303                  details->target_url.possibly_invalid_spec());
304  dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now()));
305  args.Append(dict);
306
307  std::string json_args;
308  base::JSONWriter::Write(&args, false, &json_args);
309  DispatchEvent(tab_contents->profile(), keys::kOnBeforeRetarget, json_args);
310}
311
312void ExtensionWebNavigationEventRouter::DispatchEvent(
313    Profile* profile,
314    const char* event_name,
315    const std::string& json_args) {
316  if (profile && profile->GetExtensionEventRouter()) {
317    profile->GetExtensionEventRouter()->DispatchEventToRenderers(
318        event_name, json_args, profile, GURL());
319  }
320}
321