| OLD | NEW |
| (Empty) |
| 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. | |
| 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/lazy_instance.h" | |
| 11 #include "base/string_number_conversions.h" | |
| 12 #include "base/time.h" | |
| 13 #include "base/values.h" | |
| 14 #include "chrome/browser/extensions/extension_event_router.h" | |
| 15 #include "chrome/browser/extensions/extension_tab_util.h" | |
| 16 #include "chrome/browser/extensions/extension_webnavigation_api_constants.h" | |
| 17 #include "chrome/browser/profiles/profile.h" | |
| 18 #include "chrome/browser/tab_contents/retargeting_details.h" | |
| 19 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
| 20 #include "chrome/common/chrome_notification_types.h" | |
| 21 #include "chrome/common/url_constants.h" | |
| 22 #include "content/public/browser/resource_request_details.h" | |
| 23 #include "content/public/browser/navigation_details.h" | |
| 24 #include "content/public/browser/notification_service.h" | |
| 25 #include "content/public/browser/notification_types.h" | |
| 26 #include "content/public/browser/render_view_host.h" | |
| 27 #include "content/public/browser/render_view_host_delegate.h" | |
| 28 #include "content/public/browser/web_contents.h" | |
| 29 #include "net/base/net_errors.h" | |
| 30 | |
| 31 namespace keys = extension_webnavigation_api_constants; | |
| 32 | |
| 33 using content::BrowserContext; | |
| 34 using content::ResourceRedirectDetails; | |
| 35 using content::WebContents; | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 typedef std::map<WebContents*, ExtensionWebNavigationTabObserver*> | |
| 40 TabObserverMap; | |
| 41 static base::LazyInstance<TabObserverMap> g_tab_observer = | |
| 42 LAZY_INSTANCE_INITIALIZER; | |
| 43 | |
| 44 // URL schemes for which we'll send events. | |
| 45 const char* kValidSchemes[] = { | |
| 46 chrome::kHttpScheme, | |
| 47 chrome::kHttpsScheme, | |
| 48 chrome::kFileScheme, | |
| 49 chrome::kFtpScheme, | |
| 50 chrome::kJavaScriptScheme, | |
| 51 chrome::kDataScheme, | |
| 52 }; | |
| 53 | |
| 54 // Returns the frame ID as it will be passed to the extension: | |
| 55 // 0 if the navigation happens in the main frame, or the frame ID | |
| 56 // modulo 32 bits otherwise. | |
| 57 // Keep this in sync with the GetFrameId() function in | |
| 58 // extension_webrequest_api.cc. | |
| 59 int GetFrameId(bool is_main_frame, int64 frame_id) { | |
| 60 return is_main_frame ? 0 : static_cast<int>(frame_id); | |
| 61 } | |
| 62 | |
| 63 // Returns |time| as milliseconds since the epoch. | |
| 64 double MilliSecondsFromTime(const base::Time& time) { | |
| 65 return 1000 * time.ToDoubleT(); | |
| 66 } | |
| 67 | |
| 68 // Dispatches events to the extension message service. | |
| 69 void DispatchEvent(BrowserContext* browser_context, | |
| 70 const char* event_name, | |
| 71 const std::string& json_args) { | |
| 72 Profile* profile = Profile::FromBrowserContext(browser_context); | |
| 73 if (profile && profile->GetExtensionEventRouter()) { | |
| 74 profile->GetExtensionEventRouter()->DispatchEventToRenderers( | |
| 75 event_name, json_args, profile, GURL()); | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 // Constructs and dispatches an onBeforeNavigate event. | |
| 80 void DispatchOnBeforeNavigate(WebContents* web_contents, | |
| 81 int64 frame_id, | |
| 82 bool is_main_frame, | |
| 83 const GURL& validated_url) { | |
| 84 ListValue args; | |
| 85 DictionaryValue* dict = new DictionaryValue(); | |
| 86 dict->SetInteger(keys::kTabIdKey, ExtensionTabUtil::GetTabId(web_contents)); | |
| 87 dict->SetString(keys::kUrlKey, validated_url.spec()); | |
| 88 dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id)); | |
| 89 dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); | |
| 90 args.Append(dict); | |
| 91 | |
| 92 std::string json_args; | |
| 93 base::JSONWriter::Write(&args, &json_args); | |
| 94 DispatchEvent(web_contents->GetBrowserContext(), | |
| 95 keys::kOnBeforeNavigate, | |
| 96 json_args); | |
| 97 } | |
| 98 | |
| 99 // Constructs and dispatches an onCommitted or onReferenceFragmentUpdated | |
| 100 // event. | |
| 101 void DispatchOnCommitted(const char* event_name, | |
| 102 WebContents* web_contents, | |
| 103 int64 frame_id, | |
| 104 bool is_main_frame, | |
| 105 const GURL& url, | |
| 106 content::PageTransition transition_type) { | |
| 107 ListValue args; | |
| 108 DictionaryValue* dict = new DictionaryValue(); | |
| 109 dict->SetInteger(keys::kTabIdKey, ExtensionTabUtil::GetTabId(web_contents)); | |
| 110 dict->SetString(keys::kUrlKey, url.spec()); | |
| 111 dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id)); | |
| 112 dict->SetString( | |
| 113 keys::kTransitionTypeKey, | |
| 114 content::PageTransitionGetCoreTransitionString(transition_type)); | |
| 115 ListValue* qualifiers = new ListValue(); | |
| 116 if (transition_type & content::PAGE_TRANSITION_CLIENT_REDIRECT) | |
| 117 qualifiers->Append(Value::CreateStringValue("client_redirect")); | |
| 118 if (transition_type & content::PAGE_TRANSITION_SERVER_REDIRECT) | |
| 119 qualifiers->Append(Value::CreateStringValue("server_redirect")); | |
| 120 if (transition_type & content::PAGE_TRANSITION_FORWARD_BACK) | |
| 121 qualifiers->Append(Value::CreateStringValue("forward_back")); | |
| 122 if (transition_type & content::PAGE_TRANSITION_FROM_ADDRESS_BAR) | |
| 123 qualifiers->Append(Value::CreateStringValue("from_address_bar")); | |
| 124 dict->Set(keys::kTransitionQualifiersKey, qualifiers); | |
| 125 dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); | |
| 126 args.Append(dict); | |
| 127 | |
| 128 std::string json_args; | |
| 129 base::JSONWriter::Write(&args, &json_args); | |
| 130 DispatchEvent(web_contents->GetBrowserContext(), event_name, json_args); | |
| 131 } | |
| 132 | |
| 133 // Constructs and dispatches an onDOMContentLoaded event. | |
| 134 void DispatchOnDOMContentLoaded(WebContents* web_contents, | |
| 135 const GURL& url, | |
| 136 bool is_main_frame, | |
| 137 int64 frame_id) { | |
| 138 ListValue args; | |
| 139 DictionaryValue* dict = new DictionaryValue(); | |
| 140 dict->SetInteger(keys::kTabIdKey, | |
| 141 ExtensionTabUtil::GetTabId(web_contents)); | |
| 142 dict->SetString(keys::kUrlKey, url.spec()); | |
| 143 dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id)); | |
| 144 dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); | |
| 145 args.Append(dict); | |
| 146 | |
| 147 std::string json_args; | |
| 148 base::JSONWriter::Write(&args, &json_args); | |
| 149 DispatchEvent(web_contents->GetBrowserContext(), | |
| 150 keys::kOnDOMContentLoaded, | |
| 151 json_args); | |
| 152 } | |
| 153 | |
| 154 // Constructs and dispatches an onCompleted event. | |
| 155 void DispatchOnCompleted(WebContents* web_contents, | |
| 156 const GURL& url, | |
| 157 bool is_main_frame, | |
| 158 int64 frame_id) { | |
| 159 ListValue args; | |
| 160 DictionaryValue* dict = new DictionaryValue(); | |
| 161 dict->SetInteger(keys::kTabIdKey, | |
| 162 ExtensionTabUtil::GetTabId(web_contents)); | |
| 163 dict->SetString(keys::kUrlKey, url.spec()); | |
| 164 dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id)); | |
| 165 dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); | |
| 166 args.Append(dict); | |
| 167 | |
| 168 std::string json_args; | |
| 169 base::JSONWriter::Write(&args, &json_args); | |
| 170 DispatchEvent(web_contents->GetBrowserContext(), | |
| 171 keys::kOnCompleted, json_args); | |
| 172 } | |
| 173 | |
| 174 // Constructs and dispatches an onCreatedNavigationTarget event. | |
| 175 void DispatchOnCreatedNavigationTarget( | |
| 176 WebContents* web_contents, | |
| 177 BrowserContext* browser_context, | |
| 178 int64 source_frame_id, | |
| 179 bool source_frame_is_main_frame, | |
| 180 WebContents* target_web_contents, | |
| 181 const GURL& target_url) { | |
| 182 // Check that the tab is already inserted into a tab strip model. This code | |
| 183 // path is exercised by ExtensionApiTest.WebNavigationRequestOpenTab. | |
| 184 DCHECK(ExtensionTabUtil::GetTabById( | |
| 185 ExtensionTabUtil::GetTabId(target_web_contents), | |
| 186 Profile::FromBrowserContext(target_web_contents->GetBrowserContext()), | |
| 187 false, NULL, NULL, NULL, NULL)); | |
| 188 | |
| 189 ListValue args; | |
| 190 DictionaryValue* dict = new DictionaryValue(); | |
| 191 dict->SetInteger(keys::kSourceTabIdKey, | |
| 192 ExtensionTabUtil::GetTabId(web_contents)); | |
| 193 dict->SetInteger(keys::kSourceFrameIdKey, | |
| 194 GetFrameId(source_frame_is_main_frame, source_frame_id)); | |
| 195 dict->SetString(keys::kUrlKey, target_url.possibly_invalid_spec()); | |
| 196 dict->SetInteger(keys::kTabIdKey, | |
| 197 ExtensionTabUtil::GetTabId(target_web_contents)); | |
| 198 dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); | |
| 199 args.Append(dict); | |
| 200 | |
| 201 std::string json_args; | |
| 202 base::JSONWriter::Write(&args, &json_args); | |
| 203 DispatchEvent( | |
| 204 browser_context, keys::kOnCreatedNavigationTarget, json_args); | |
| 205 } | |
| 206 | |
| 207 // Constructs and dispatches an onErrorOccurred event. | |
| 208 void DispatchOnErrorOccurred(WebContents* web_contents, | |
| 209 const GURL& url, | |
| 210 int64 frame_id, | |
| 211 bool is_main_frame, | |
| 212 int error_code) { | |
| 213 ListValue args; | |
| 214 DictionaryValue* dict = new DictionaryValue(); | |
| 215 dict->SetInteger(keys::kTabIdKey, ExtensionTabUtil::GetTabId(web_contents)); | |
| 216 dict->SetString(keys::kUrlKey, url.spec()); | |
| 217 dict->SetInteger(keys::kFrameIdKey, GetFrameId(is_main_frame, frame_id)); | |
| 218 dict->SetString(keys::kErrorKey, net::ErrorToString(error_code)); | |
| 219 dict->SetDouble(keys::kTimeStampKey, MilliSecondsFromTime(base::Time::Now())); | |
| 220 args.Append(dict); | |
| 221 | |
| 222 std::string json_args; | |
| 223 base::JSONWriter::Write(&args, &json_args); | |
| 224 DispatchEvent(web_contents->GetBrowserContext(), | |
| 225 keys::kOnErrorOccurred, | |
| 226 json_args); | |
| 227 } | |
| 228 | |
| 229 } // namespace | |
| 230 | |
| 231 | |
| 232 // FrameNavigationState ------------------------------------------------------- | |
| 233 | |
| 234 // static | |
| 235 bool FrameNavigationState::allow_extension_scheme_ = false; | |
| 236 | |
| 237 FrameNavigationState::FrameNavigationState() | |
| 238 : main_frame_id_(-1) { | |
| 239 } | |
| 240 | |
| 241 FrameNavigationState::~FrameNavigationState() {} | |
| 242 | |
| 243 bool FrameNavigationState::CanSendEvents(int64 frame_id) const { | |
| 244 FrameIdToStateMap::const_iterator frame_state = | |
| 245 frame_state_map_.find(frame_id); | |
| 246 if (frame_state == frame_state_map_.end() || | |
| 247 frame_state->second.error_occurred) { | |
| 248 return false; | |
| 249 } | |
| 250 return IsValidUrl(frame_state->second.url); | |
| 251 } | |
| 252 | |
| 253 bool FrameNavigationState::IsValidUrl(const GURL& url) const { | |
| 254 for (unsigned i = 0; i < arraysize(kValidSchemes); ++i) { | |
| 255 if (url.scheme() == kValidSchemes[i]) | |
| 256 return true; | |
| 257 } | |
| 258 // Allow about:blank. | |
| 259 if (url.spec() == chrome::kAboutBlankURL) | |
| 260 return true; | |
| 261 if (allow_extension_scheme_ && url.scheme() == chrome::kExtensionScheme) | |
| 262 return true; | |
| 263 return false; | |
| 264 } | |
| 265 | |
| 266 void FrameNavigationState::TrackFrame(int64 frame_id, | |
| 267 const GURL& url, | |
| 268 bool is_main_frame, | |
| 269 bool is_error_page) { | |
| 270 if (is_main_frame) { | |
| 271 frame_state_map_.clear(); | |
| 272 frame_ids_.clear(); | |
| 273 } | |
| 274 FrameState& frame_state = frame_state_map_[frame_id]; | |
| 275 frame_state.error_occurred = is_error_page; | |
| 276 frame_state.url = url; | |
| 277 frame_state.is_main_frame = is_main_frame; | |
| 278 frame_state.is_navigating = true; | |
| 279 frame_state.is_committed = false; | |
| 280 frame_state.is_server_redirected = false; | |
| 281 if (is_main_frame) { | |
| 282 main_frame_id_ = frame_id; | |
| 283 } | |
| 284 frame_ids_.insert(frame_id); | |
| 285 } | |
| 286 | |
| 287 void FrameNavigationState::UpdateFrame(int64 frame_id, const GURL& url) { | |
| 288 FrameIdToStateMap::iterator frame_state = frame_state_map_.find(frame_id); | |
| 289 if (frame_state == frame_state_map_.end()) { | |
| 290 NOTREACHED(); | |
| 291 return; | |
| 292 } | |
| 293 frame_state->second.url = url; | |
| 294 } | |
| 295 | |
| 296 bool FrameNavigationState::IsValidFrame(int64 frame_id) const { | |
| 297 FrameIdToStateMap::const_iterator frame_state = | |
| 298 frame_state_map_.find(frame_id); | |
| 299 return (frame_state != frame_state_map_.end()); | |
| 300 } | |
| 301 | |
| 302 GURL FrameNavigationState::GetUrl(int64 frame_id) const { | |
| 303 FrameIdToStateMap::const_iterator frame_state = | |
| 304 frame_state_map_.find(frame_id); | |
| 305 if (frame_state == frame_state_map_.end()) { | |
| 306 NOTREACHED(); | |
| 307 return GURL(); | |
| 308 } | |
| 309 return frame_state->second.url; | |
| 310 } | |
| 311 | |
| 312 bool FrameNavigationState::IsMainFrame(int64 frame_id) const { | |
| 313 return main_frame_id_ != -1 && main_frame_id_ == frame_id; | |
| 314 } | |
| 315 | |
| 316 int64 FrameNavigationState::GetMainFrameID() const { | |
| 317 return main_frame_id_; | |
| 318 } | |
| 319 | |
| 320 void FrameNavigationState::SetErrorOccurredInFrame(int64 frame_id) { | |
| 321 DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end()); | |
| 322 frame_state_map_[frame_id].error_occurred = true; | |
| 323 } | |
| 324 | |
| 325 bool FrameNavigationState::GetErrorOccurredInFrame(int64 frame_id) const { | |
| 326 FrameIdToStateMap::const_iterator frame_state = | |
| 327 frame_state_map_.find(frame_id); | |
| 328 return (frame_state == frame_state_map_.end() || | |
| 329 frame_state->second.error_occurred); | |
| 330 } | |
| 331 | |
| 332 void FrameNavigationState::SetNavigationCompleted(int64 frame_id) { | |
| 333 DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end()); | |
| 334 frame_state_map_[frame_id].is_navigating = false; | |
| 335 } | |
| 336 | |
| 337 bool FrameNavigationState::GetNavigationCompleted(int64 frame_id) const { | |
| 338 FrameIdToStateMap::const_iterator frame_state = | |
| 339 frame_state_map_.find(frame_id); | |
| 340 return (frame_state == frame_state_map_.end() || | |
| 341 !frame_state->second.is_navigating); | |
| 342 } | |
| 343 | |
| 344 void FrameNavigationState::SetNavigationCommitted(int64 frame_id) { | |
| 345 DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end()); | |
| 346 frame_state_map_[frame_id].is_committed = true; | |
| 347 } | |
| 348 | |
| 349 bool FrameNavigationState::GetNavigationCommitted(int64 frame_id) const { | |
| 350 FrameIdToStateMap::const_iterator frame_state = | |
| 351 frame_state_map_.find(frame_id); | |
| 352 return (frame_state != frame_state_map_.end() && | |
| 353 frame_state->second.is_committed); | |
| 354 } | |
| 355 | |
| 356 void FrameNavigationState::SetIsServerRedirected(int64 frame_id) { | |
| 357 DCHECK(frame_state_map_.find(frame_id) != frame_state_map_.end()); | |
| 358 frame_state_map_[frame_id].is_server_redirected = true; | |
| 359 } | |
| 360 | |
| 361 bool FrameNavigationState::GetIsServerRedirected(int64 frame_id) const { | |
| 362 FrameIdToStateMap::const_iterator frame_state = | |
| 363 frame_state_map_.find(frame_id); | |
| 364 return (frame_state != frame_state_map_.end() && | |
| 365 frame_state->second.is_server_redirected); | |
| 366 } | |
| 367 | |
| 368 | |
| 369 // ExtensionWebNavigtionEventRouter ------------------------------------------- | |
| 370 | |
| 371 ExtensionWebNavigationEventRouter::PendingWebContents::PendingWebContents() | |
| 372 : source_web_contents(NULL), | |
| 373 source_frame_id(0), | |
| 374 source_frame_is_main_frame(false), | |
| 375 target_web_contents(NULL), | |
| 376 target_url() { | |
| 377 } | |
| 378 | |
| 379 ExtensionWebNavigationEventRouter::PendingWebContents::PendingWebContents( | |
| 380 WebContents* source_web_contents, | |
| 381 int64 source_frame_id, | |
| 382 bool source_frame_is_main_frame, | |
| 383 WebContents* target_web_contents, | |
| 384 const GURL& target_url) | |
| 385 : source_web_contents(source_web_contents), | |
| 386 source_frame_id(source_frame_id), | |
| 387 source_frame_is_main_frame(source_frame_is_main_frame), | |
| 388 target_web_contents(target_web_contents), | |
| 389 target_url(target_url) { | |
| 390 } | |
| 391 | |
| 392 ExtensionWebNavigationEventRouter::PendingWebContents::~PendingWebContents() {} | |
| 393 | |
| 394 ExtensionWebNavigationEventRouter::ExtensionWebNavigationEventRouter( | |
| 395 Profile* profile) : profile_(profile) {} | |
| 396 | |
| 397 ExtensionWebNavigationEventRouter::~ExtensionWebNavigationEventRouter() {} | |
| 398 | |
| 399 void ExtensionWebNavigationEventRouter::Init() { | |
| 400 if (registrar_.IsEmpty()) { | |
| 401 registrar_.Add(this, | |
| 402 chrome::NOTIFICATION_RETARGETING, | |
| 403 content::NotificationService::AllSources()); | |
| 404 registrar_.Add(this, | |
| 405 content::NOTIFICATION_TAB_ADDED, | |
| 406 content::NotificationService::AllSources()); | |
| 407 registrar_.Add(this, | |
| 408 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
| 409 content::NotificationService::AllSources()); | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 void ExtensionWebNavigationEventRouter::Observe( | |
| 414 int type, | |
| 415 const content::NotificationSource& source, | |
| 416 const content::NotificationDetails& details) { | |
| 417 switch (type) { | |
| 418 case chrome::NOTIFICATION_RETARGETING: { | |
| 419 Profile* profile = content::Source<Profile>(source).ptr(); | |
| 420 if (profile->GetOriginalProfile() == profile_) { | |
| 421 Retargeting( | |
| 422 content::Details<const RetargetingDetails>(details).ptr()); | |
| 423 } | |
| 424 break; | |
| 425 } | |
| 426 | |
| 427 case content::NOTIFICATION_TAB_ADDED: | |
| 428 TabAdded(content::Details<WebContents>(details).ptr()); | |
| 429 break; | |
| 430 | |
| 431 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: | |
| 432 TabDestroyed(content::Source<WebContents>(source).ptr()); | |
| 433 break; | |
| 434 | |
| 435 default: | |
| 436 NOTREACHED(); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 void ExtensionWebNavigationEventRouter::Retargeting( | |
| 441 const RetargetingDetails* details) { | |
| 442 if (details->source_frame_id == 0) | |
| 443 return; | |
| 444 ExtensionWebNavigationTabObserver* tab_observer = | |
| 445 ExtensionWebNavigationTabObserver::Get(details->source_web_contents); | |
| 446 if (!tab_observer) { | |
| 447 // If you hit this DCHECK(), please add reproduction steps to | |
| 448 // http://crbug.com/109464. | |
| 449 DCHECK(details->source_web_contents->GetViewType() != | |
| 450 content::VIEW_TYPE_TAB_CONTENTS); | |
| 451 return; | |
| 452 } | |
| 453 const FrameNavigationState& frame_navigation_state = | |
| 454 tab_observer->frame_navigation_state(); | |
| 455 | |
| 456 if (!frame_navigation_state.CanSendEvents(details->source_frame_id)) | |
| 457 return; | |
| 458 | |
| 459 // If the WebContents was created as a response to an IPC from a renderer | |
| 460 // (and therefore doesn't yet have a wrapper), or if it isn't yet inserted | |
| 461 // into a tab strip, we need to delay the extension event until the | |
| 462 // WebContents is fully initialized. | |
| 463 if ((TabContentsWrapper::GetCurrentWrapperForContents( | |
| 464 details->target_web_contents) == NULL) || | |
| 465 details->not_yet_in_tabstrip) { | |
| 466 pending_web_contents_[details->target_web_contents] = | |
| 467 PendingWebContents( | |
| 468 details->source_web_contents, | |
| 469 details->source_frame_id, | |
| 470 frame_navigation_state.IsMainFrame(details->source_frame_id), | |
| 471 details->target_web_contents, | |
| 472 details->target_url); | |
| 473 } else { | |
| 474 DispatchOnCreatedNavigationTarget( | |
| 475 details->source_web_contents, | |
| 476 details->target_web_contents->GetBrowserContext(), | |
| 477 details->source_frame_id, | |
| 478 frame_navigation_state.IsMainFrame(details->source_frame_id), | |
| 479 details->target_web_contents, | |
| 480 details->target_url); | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 void ExtensionWebNavigationEventRouter::TabAdded(WebContents* tab) { | |
| 485 std::map<WebContents*, PendingWebContents>::iterator iter = | |
| 486 pending_web_contents_.find(tab); | |
| 487 if (iter == pending_web_contents_.end()) | |
| 488 return; | |
| 489 | |
| 490 DispatchOnCreatedNavigationTarget( | |
| 491 iter->second.source_web_contents, | |
| 492 iter->second.target_web_contents->GetBrowserContext(), | |
| 493 iter->second.source_frame_id, | |
| 494 iter->second.source_frame_is_main_frame, | |
| 495 iter->second.target_web_contents, | |
| 496 iter->second.target_url); | |
| 497 pending_web_contents_.erase(iter); | |
| 498 } | |
| 499 | |
| 500 void ExtensionWebNavigationEventRouter::TabDestroyed(WebContents* tab) { | |
| 501 pending_web_contents_.erase(tab); | |
| 502 for (std::map<WebContents*, PendingWebContents>::iterator i = | |
| 503 pending_web_contents_.begin(); i != pending_web_contents_.end(); ) { | |
| 504 if (i->second.source_web_contents == tab) | |
| 505 pending_web_contents_.erase(i++); | |
| 506 else | |
| 507 ++i; | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 // ExtensionWebNavigationTabObserver ------------------------------------------ | |
| 512 | |
| 513 ExtensionWebNavigationTabObserver::ExtensionWebNavigationTabObserver( | |
| 514 WebContents* web_contents) | |
| 515 : WebContentsObserver(web_contents) { | |
| 516 g_tab_observer.Get().insert(TabObserverMap::value_type(web_contents, this)); | |
| 517 registrar_.Add(this, | |
| 518 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, | |
| 519 content::Source<WebContents>(web_contents)); | |
| 520 } | |
| 521 | |
| 522 ExtensionWebNavigationTabObserver::~ExtensionWebNavigationTabObserver() {} | |
| 523 | |
| 524 // static | |
| 525 ExtensionWebNavigationTabObserver* ExtensionWebNavigationTabObserver::Get( | |
| 526 WebContents* web_contents) { | |
| 527 TabObserverMap::iterator i = g_tab_observer.Get().find(web_contents); | |
| 528 return i == g_tab_observer.Get().end() ? NULL : i->second; | |
| 529 } | |
| 530 | |
| 531 void ExtensionWebNavigationTabObserver::Observe( | |
| 532 int type, | |
| 533 const content::NotificationSource& source, | |
| 534 const content::NotificationDetails& details) { | |
| 535 switch (type) { | |
| 536 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { | |
| 537 ResourceRedirectDetails* resource_redirect_details = | |
| 538 content::Details<ResourceRedirectDetails>(details).ptr(); | |
| 539 ResourceType::Type resource_type = | |
| 540 resource_redirect_details->resource_type; | |
| 541 if (resource_type == ResourceType::MAIN_FRAME || | |
| 542 resource_type == ResourceType::SUB_FRAME) { | |
| 543 int64 frame_id = resource_redirect_details->frame_id; | |
| 544 if (!navigation_state_.CanSendEvents(frame_id)) | |
| 545 return; | |
| 546 navigation_state_.SetIsServerRedirected(frame_id); | |
| 547 } | |
| 548 break; | |
| 549 } | |
| 550 | |
| 551 default: | |
| 552 NOTREACHED(); | |
| 553 } | |
| 554 } | |
| 555 | |
| 556 void ExtensionWebNavigationTabObserver::DidStartProvisionalLoadForFrame( | |
| 557 int64 frame_id, | |
| 558 bool is_main_frame, | |
| 559 const GURL& validated_url, | |
| 560 bool is_error_page, | |
| 561 content::RenderViewHost* render_view_host) { | |
| 562 // Ignore navigations of sub frames, if the main frame isn't committed yet. | |
| 563 // This might happen if a sub frame triggers a navigation for both the main | |
| 564 // frame and itself. Since the sub frame is about to be deleted, and there's | |
| 565 // no way for an extension to tell that these navigations belong to an old | |
| 566 // frame, we just suppress the events here. | |
| 567 int64 main_frame_id = navigation_state_.GetMainFrameID(); | |
| 568 if (!is_main_frame && | |
| 569 !navigation_state_.GetNavigationCommitted(main_frame_id)) { | |
| 570 return; | |
| 571 } | |
| 572 | |
| 573 navigation_state_.TrackFrame(frame_id, | |
| 574 validated_url, | |
| 575 is_main_frame, | |
| 576 is_error_page); | |
| 577 if (!navigation_state_.CanSendEvents(frame_id)) | |
| 578 return; | |
| 579 DispatchOnBeforeNavigate( | |
| 580 web_contents(), frame_id, is_main_frame, validated_url); | |
| 581 } | |
| 582 | |
| 583 void ExtensionWebNavigationTabObserver::DidCommitProvisionalLoadForFrame( | |
| 584 int64 frame_id, | |
| 585 bool is_main_frame, | |
| 586 const GURL& url, | |
| 587 content::PageTransition transition_type) { | |
| 588 if (!navigation_state_.CanSendEvents(frame_id)) | |
| 589 return; | |
| 590 | |
| 591 bool is_reference_fragment_navigation = | |
| 592 IsReferenceFragmentNavigation(frame_id, url); | |
| 593 | |
| 594 // Update the URL as it might have changed. | |
| 595 navigation_state_.UpdateFrame(frame_id, url); | |
| 596 navigation_state_.SetNavigationCommitted(frame_id); | |
| 597 | |
| 598 if (is_reference_fragment_navigation) { | |
| 599 DispatchOnCommitted( | |
| 600 keys::kOnReferenceFragmentUpdated, | |
| 601 web_contents(), | |
| 602 frame_id, | |
| 603 is_main_frame, | |
| 604 url, | |
| 605 transition_type); | |
| 606 navigation_state_.SetNavigationCompleted(frame_id); | |
| 607 } else { | |
| 608 if (navigation_state_.GetIsServerRedirected(frame_id)) { | |
| 609 transition_type = static_cast<content::PageTransition>( | |
| 610 transition_type | content::PAGE_TRANSITION_SERVER_REDIRECT); | |
| 611 } | |
| 612 DispatchOnCommitted( | |
| 613 keys::kOnCommitted, | |
| 614 web_contents(), | |
| 615 frame_id, | |
| 616 is_main_frame, | |
| 617 url, | |
| 618 transition_type); | |
| 619 } | |
| 620 } | |
| 621 | |
| 622 void ExtensionWebNavigationTabObserver::DidFailProvisionalLoad( | |
| 623 int64 frame_id, | |
| 624 bool is_main_frame, | |
| 625 const GURL& validated_url, | |
| 626 int error_code, | |
| 627 const string16& error_description) { | |
| 628 if (!navigation_state_.CanSendEvents(frame_id)) | |
| 629 return; | |
| 630 navigation_state_.SetErrorOccurredInFrame(frame_id); | |
| 631 DispatchOnErrorOccurred( | |
| 632 web_contents(), validated_url, frame_id, is_main_frame, error_code); | |
| 633 } | |
| 634 | |
| 635 void ExtensionWebNavigationTabObserver::DocumentLoadedInFrame( | |
| 636 int64 frame_id) { | |
| 637 if (!navigation_state_.CanSendEvents(frame_id)) | |
| 638 return; | |
| 639 DispatchOnDOMContentLoaded(web_contents(), | |
| 640 navigation_state_.GetUrl(frame_id), | |
| 641 navigation_state_.IsMainFrame(frame_id), | |
| 642 frame_id); | |
| 643 } | |
| 644 | |
| 645 void ExtensionWebNavigationTabObserver::DidFinishLoad( | |
| 646 int64 frame_id, | |
| 647 const GURL& validated_url, | |
| 648 bool is_main_frame) { | |
| 649 if (!navigation_state_.CanSendEvents(frame_id)) | |
| 650 return; | |
| 651 navigation_state_.SetNavigationCompleted(frame_id); | |
| 652 DCHECK_EQ(navigation_state_.GetUrl(frame_id), validated_url); | |
| 653 DCHECK_EQ(navigation_state_.IsMainFrame(frame_id), is_main_frame); | |
| 654 DispatchOnCompleted(web_contents(), | |
| 655 validated_url, | |
| 656 is_main_frame, | |
| 657 frame_id); | |
| 658 } | |
| 659 | |
| 660 void ExtensionWebNavigationTabObserver::DidOpenRequestedURL( | |
| 661 WebContents* new_contents, | |
| 662 const GURL& url, | |
| 663 const content::Referrer& referrer, | |
| 664 WindowOpenDisposition disposition, | |
| 665 content::PageTransition transition, | |
| 666 int64 source_frame_id) { | |
| 667 if (!navigation_state_.CanSendEvents(source_frame_id)) | |
| 668 return; | |
| 669 | |
| 670 // We only send the onCreatedNavigationTarget if we end up creating a new | |
| 671 // window. | |
| 672 if (disposition != SINGLETON_TAB && | |
| 673 disposition != NEW_FOREGROUND_TAB && | |
| 674 disposition != NEW_BACKGROUND_TAB && | |
| 675 disposition != NEW_POPUP && | |
| 676 disposition != NEW_WINDOW && | |
| 677 disposition != OFF_THE_RECORD) | |
| 678 return; | |
| 679 | |
| 680 DispatchOnCreatedNavigationTarget( | |
| 681 web_contents(), | |
| 682 new_contents->GetBrowserContext(), | |
| 683 source_frame_id, | |
| 684 navigation_state_.IsMainFrame(source_frame_id), | |
| 685 new_contents, | |
| 686 url); | |
| 687 } | |
| 688 | |
| 689 void ExtensionWebNavigationTabObserver::WebContentsDestroyed(WebContents* tab) { | |
| 690 g_tab_observer.Get().erase(tab); | |
| 691 for (FrameNavigationState::const_iterator frame = navigation_state_.begin(); | |
| 692 frame != navigation_state_.end(); ++frame) { | |
| 693 if (!navigation_state_.GetNavigationCompleted(*frame) && | |
| 694 navigation_state_.CanSendEvents(*frame)) { | |
| 695 DispatchOnErrorOccurred( | |
| 696 tab, | |
| 697 navigation_state_.GetUrl(*frame), | |
| 698 *frame, | |
| 699 navigation_state_.IsMainFrame(*frame), | |
| 700 net::ERR_ABORTED); | |
| 701 } | |
| 702 } | |
| 703 } | |
| 704 | |
| 705 // See also NavigationController::IsURLInPageNavigation. | |
| 706 bool ExtensionWebNavigationTabObserver::IsReferenceFragmentNavigation( | |
| 707 int64 frame_id, | |
| 708 const GURL& url) { | |
| 709 GURL existing_url = navigation_state_.GetUrl(frame_id); | |
| 710 if (existing_url == url) | |
| 711 return false; | |
| 712 | |
| 713 url_canon::Replacements<char> replacements; | |
| 714 replacements.ClearRef(); | |
| 715 return existing_url.ReplaceComponents(replacements) == | |
| 716 url.ReplaceComponents(replacements); | |
| 717 } | |
| 718 | |
| 719 bool GetFrameFunction::RunImpl() { | |
| 720 DictionaryValue* details; | |
| 721 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details)); | |
| 722 DCHECK(details); | |
| 723 | |
| 724 int tab_id; | |
| 725 int frame_id; | |
| 726 EXTENSION_FUNCTION_VALIDATE(details->GetInteger(keys::kTabIdKey, &tab_id)); | |
| 727 EXTENSION_FUNCTION_VALIDATE( | |
| 728 details->GetInteger(keys::kFrameIdKey, &frame_id)); | |
| 729 | |
| 730 result_.reset(Value::CreateNullValue()); | |
| 731 | |
| 732 TabContentsWrapper* wrapper; | |
| 733 if (!ExtensionTabUtil::GetTabById( | |
| 734 tab_id, profile(), include_incognito(), NULL, NULL, &wrapper, NULL) || | |
| 735 !wrapper) { | |
| 736 return true; | |
| 737 } | |
| 738 | |
| 739 WebContents* web_contents = wrapper->web_contents(); | |
| 740 ExtensionWebNavigationTabObserver* observer = | |
| 741 ExtensionWebNavigationTabObserver::Get(web_contents); | |
| 742 DCHECK(observer); | |
| 743 | |
| 744 const FrameNavigationState& frame_navigation_state = | |
| 745 observer->frame_navigation_state(); | |
| 746 | |
| 747 if (frame_id == 0) | |
| 748 frame_id = frame_navigation_state.GetMainFrameID(); | |
| 749 if (!frame_navigation_state.IsValidFrame(frame_id)) | |
| 750 return true; | |
| 751 | |
| 752 GURL frame_url = frame_navigation_state.GetUrl(frame_id); | |
| 753 if (!frame_navigation_state.IsValidUrl(frame_url)) | |
| 754 return true; | |
| 755 | |
| 756 DictionaryValue* resultDict = new DictionaryValue(); | |
| 757 resultDict->SetString(keys::kUrlKey, frame_url.spec()); | |
| 758 resultDict->SetBoolean( | |
| 759 keys::kErrorOccurredKey, | |
| 760 frame_navigation_state.GetErrorOccurredInFrame(frame_id)); | |
| 761 result_.reset(resultDict); | |
| 762 return true; | |
| 763 } | |
| 764 | |
| 765 bool GetAllFramesFunction::RunImpl() { | |
| 766 DictionaryValue* details; | |
| 767 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details)); | |
| 768 DCHECK(details); | |
| 769 | |
| 770 int tab_id; | |
| 771 EXTENSION_FUNCTION_VALIDATE(details->GetInteger(keys::kTabIdKey, &tab_id)); | |
| 772 | |
| 773 result_.reset(Value::CreateNullValue()); | |
| 774 | |
| 775 TabContentsWrapper* wrapper; | |
| 776 if (!ExtensionTabUtil::GetTabById( | |
| 777 tab_id, profile(), include_incognito(), NULL, NULL, &wrapper, NULL) || | |
| 778 !wrapper) { | |
| 779 return true; | |
| 780 } | |
| 781 | |
| 782 WebContents* web_contents = wrapper->web_contents(); | |
| 783 ExtensionWebNavigationTabObserver* observer = | |
| 784 ExtensionWebNavigationTabObserver::Get(web_contents); | |
| 785 DCHECK(observer); | |
| 786 | |
| 787 const FrameNavigationState& navigation_state = | |
| 788 observer->frame_navigation_state(); | |
| 789 | |
| 790 ListValue* resultList = new ListValue(); | |
| 791 for (FrameNavigationState::const_iterator frame = navigation_state.begin(); | |
| 792 frame != navigation_state.end(); ++frame) { | |
| 793 GURL frame_url = navigation_state.GetUrl(*frame); | |
| 794 if (!navigation_state.IsValidUrl(frame_url)) | |
| 795 continue; | |
| 796 DictionaryValue* frameDict = new DictionaryValue(); | |
| 797 frameDict->SetString(keys::kUrlKey, frame_url.spec()); | |
| 798 frameDict->SetInteger( | |
| 799 keys::kFrameIdKey, | |
| 800 GetFrameId(navigation_state.IsMainFrame(*frame), *frame)); | |
| 801 frameDict->SetBoolean( | |
| 802 keys::kErrorOccurredKey, | |
| 803 navigation_state.GetErrorOccurredInFrame(*frame)); | |
| 804 resultList->Append(frameDict); | |
| 805 } | |
| 806 result_.reset(resultList); | |
| 807 return true; | |
| 808 } | |
| OLD | NEW |