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