| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 #include "extensions/browser/guest_view/web_view/web_view_guest.h" | |
| 6 | |
| 7 #include "base/message_loop/message_loop.h" | |
| 8 #include "base/strings/stringprintf.h" | |
| 9 #include "base/strings/utf_string_conversions.h" | |
| 10 #include "chrome/browser/extensions/api/web_view/web_view_internal_api.h" | |
| 11 #include "content/public/browser/browser_context.h" | |
| 12 #include "content/public/browser/browser_thread.h" | |
| 13 #include "content/public/browser/child_process_security_policy.h" | |
| 14 #include "content/public/browser/native_web_keyboard_event.h" | |
| 15 #include "content/public/browser/navigation_entry.h" | |
| 16 #include "content/public/browser/notification_details.h" | |
| 17 #include "content/public/browser/notification_service.h" | |
| 18 #include "content/public/browser/notification_source.h" | |
| 19 #include "content/public/browser/notification_types.h" | |
| 20 #include "content/public/browser/render_process_host.h" | |
| 21 #include "content/public/browser/render_view_host.h" | |
| 22 #include "content/public/browser/resource_request_details.h" | |
| 23 #include "content/public/browser/site_instance.h" | |
| 24 #include "content/public/browser/storage_partition.h" | |
| 25 #include "content/public/browser/user_metrics.h" | |
| 26 #include "content/public/browser/web_contents.h" | |
| 27 #include "content/public/browser/web_contents_delegate.h" | |
| 28 #include "content/public/common/media_stream_request.h" | |
| 29 #include "content/public/common/page_zoom.h" | |
| 30 #include "content/public/common/result_codes.h" | |
| 31 #include "content/public/common/stop_find_action.h" | |
| 32 #include "content/public/common/url_constants.h" | |
| 33 #include "extensions/browser/api/extensions_api_client.h" | |
| 34 #include "extensions/browser/extension_system.h" | |
| 35 #include "extensions/browser/guest_view/guest_view_constants.h" | |
| 36 #include "extensions/browser/guest_view/guest_view_manager.h" | |
| 37 #include "extensions/browser/guest_view/web_view/web_view_constants.h" | |
| 38 #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h" | |
| 39 #include "extensions/browser/guest_view/web_view/web_view_permission_types.h" | |
| 40 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" | |
| 41 #include "extensions/common/constants.h" | |
| 42 #include "extensions/common/extension_messages.h" | |
| 43 #include "ipc/ipc_message_macros.h" | |
| 44 #include "net/base/escape.h" | |
| 45 #include "net/base/net_errors.h" | |
| 46 #include "ui/base/models/simple_menu_model.h" | |
| 47 | |
| 48 using base::UserMetricsAction; | |
| 49 using content::RenderFrameHost; | |
| 50 using content::ResourceType; | |
| 51 using content::WebContents; | |
| 52 | |
| 53 namespace extensions { | |
| 54 | |
| 55 namespace { | |
| 56 | |
| 57 std::string WindowOpenDispositionToString( | |
| 58 WindowOpenDisposition window_open_disposition) { | |
| 59 switch (window_open_disposition) { | |
| 60 case IGNORE_ACTION: | |
| 61 return "ignore"; | |
| 62 case SAVE_TO_DISK: | |
| 63 return "save_to_disk"; | |
| 64 case CURRENT_TAB: | |
| 65 return "current_tab"; | |
| 66 case NEW_BACKGROUND_TAB: | |
| 67 return "new_background_tab"; | |
| 68 case NEW_FOREGROUND_TAB: | |
| 69 return "new_foreground_tab"; | |
| 70 case NEW_WINDOW: | |
| 71 return "new_window"; | |
| 72 case NEW_POPUP: | |
| 73 return "new_popup"; | |
| 74 default: | |
| 75 NOTREACHED() << "Unknown Window Open Disposition"; | |
| 76 return "ignore"; | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 static std::string TerminationStatusToString(base::TerminationStatus status) { | |
| 81 switch (status) { | |
| 82 case base::TERMINATION_STATUS_NORMAL_TERMINATION: | |
| 83 return "normal"; | |
| 84 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: | |
| 85 case base::TERMINATION_STATUS_STILL_RUNNING: | |
| 86 return "abnormal"; | |
| 87 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: | |
| 88 return "killed"; | |
| 89 case base::TERMINATION_STATUS_PROCESS_CRASHED: | |
| 90 return "crashed"; | |
| 91 case base::TERMINATION_STATUS_MAX_ENUM: | |
| 92 break; | |
| 93 } | |
| 94 NOTREACHED() << "Unknown Termination Status."; | |
| 95 return "unknown"; | |
| 96 } | |
| 97 | |
| 98 std::string GetStoragePartitionIdFromSiteURL(const GURL& site_url) { | |
| 99 const std::string& partition_id = site_url.query(); | |
| 100 bool persist_storage = site_url.path().find("persist") != std::string::npos; | |
| 101 return (persist_storage ? webview::kPersistPrefix : "") + partition_id; | |
| 102 } | |
| 103 | |
| 104 void ParsePartitionParam(const base::DictionaryValue& create_params, | |
| 105 std::string* storage_partition_id, | |
| 106 bool* persist_storage) { | |
| 107 std::string partition_str; | |
| 108 if (!create_params.GetString(webview::kStoragePartitionId, &partition_str)) { | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 // Since the "persist:" prefix is in ASCII, StartsWith will work fine on | |
| 113 // UTF-8 encoded |partition_id|. If the prefix is a match, we can safely | |
| 114 // remove the prefix without splicing in the middle of a multi-byte codepoint. | |
| 115 // We can use the rest of the string as UTF-8 encoded one. | |
| 116 if (StartsWithASCII(partition_str, "persist:", true)) { | |
| 117 size_t index = partition_str.find(":"); | |
| 118 CHECK(index != std::string::npos); | |
| 119 // It is safe to do index + 1, since we tested for the full prefix above. | |
| 120 *storage_partition_id = partition_str.substr(index + 1); | |
| 121 | |
| 122 if (storage_partition_id->empty()) { | |
| 123 // TODO(lazyboy): Better way to deal with this error. | |
| 124 return; | |
| 125 } | |
| 126 *persist_storage = true; | |
| 127 } else { | |
| 128 *storage_partition_id = partition_str; | |
| 129 *persist_storage = false; | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 } // namespace | |
| 134 | |
| 135 // static | |
| 136 GuestViewBase* WebViewGuest::Create(content::BrowserContext* browser_context, | |
| 137 int guest_instance_id) { | |
| 138 return new WebViewGuest(browser_context, guest_instance_id); | |
| 139 } | |
| 140 | |
| 141 // static | |
| 142 bool WebViewGuest::GetGuestPartitionConfigForSite( | |
| 143 const GURL& site, | |
| 144 std::string* partition_domain, | |
| 145 std::string* partition_name, | |
| 146 bool* in_memory) { | |
| 147 if (!site.SchemeIs(content::kGuestScheme)) | |
| 148 return false; | |
| 149 | |
| 150 // Since guest URLs are only used for packaged apps, there must be an app | |
| 151 // id in the URL. | |
| 152 CHECK(site.has_host()); | |
| 153 *partition_domain = site.host(); | |
| 154 // Since persistence is optional, the path must either be empty or the | |
| 155 // literal string. | |
| 156 *in_memory = (site.path() != "/persist"); | |
| 157 // The partition name is user supplied value, which we have encoded when the | |
| 158 // URL was created, so it needs to be decoded. | |
| 159 *partition_name = | |
| 160 net::UnescapeURLComponent(site.query(), net::UnescapeRule::NORMAL); | |
| 161 return true; | |
| 162 } | |
| 163 | |
| 164 // static | |
| 165 const char WebViewGuest::Type[] = "webview"; | |
| 166 | |
| 167 // static | |
| 168 int WebViewGuest::GetViewInstanceId(WebContents* contents) { | |
| 169 WebViewGuest* guest = FromWebContents(contents); | |
| 170 if (!guest) | |
| 171 return guestview::kInstanceIDNone; | |
| 172 | |
| 173 return guest->view_instance_id(); | |
| 174 } | |
| 175 | |
| 176 const char* WebViewGuest::GetAPINamespace() { | |
| 177 return webview::kAPINamespace; | |
| 178 } | |
| 179 | |
| 180 void WebViewGuest::CreateWebContents( | |
| 181 const std::string& embedder_extension_id, | |
| 182 int embedder_render_process_id, | |
| 183 const base::DictionaryValue& create_params, | |
| 184 const WebContentsCreatedCallback& callback) { | |
| 185 content::RenderProcessHost* embedder_render_process_host = | |
| 186 content::RenderProcessHost::FromID(embedder_render_process_id); | |
| 187 std::string storage_partition_id; | |
| 188 bool persist_storage = false; | |
| 189 std::string storage_partition_string; | |
| 190 ParsePartitionParam(create_params, &storage_partition_id, &persist_storage); | |
| 191 // Validate that the partition id coming from the renderer is valid UTF-8, | |
| 192 // since we depend on this in other parts of the code, such as FilePath | |
| 193 // creation. If the validation fails, treat it as a bad message and kill the | |
| 194 // renderer process. | |
| 195 if (!base::IsStringUTF8(storage_partition_id)) { | |
| 196 content::RecordAction( | |
| 197 base::UserMetricsAction("BadMessageTerminate_BPGM")); | |
| 198 base::KillProcess( | |
| 199 embedder_render_process_host->GetHandle(), | |
| 200 content::RESULT_CODE_KILLED_BAD_MESSAGE, false); | |
| 201 callback.Run(NULL); | |
| 202 return; | |
| 203 } | |
| 204 std::string url_encoded_partition = net::EscapeQueryParamValue( | |
| 205 storage_partition_id, false); | |
| 206 // The SiteInstance of a given webview tag is based on the fact that it's | |
| 207 // a guest process in addition to which platform application the tag | |
| 208 // belongs to and what storage partition is in use, rather than the URL | |
| 209 // that the tag is being navigated to. | |
| 210 GURL guest_site(base::StringPrintf("%s://%s/%s?%s", | |
| 211 content::kGuestScheme, | |
| 212 embedder_extension_id.c_str(), | |
| 213 persist_storage ? "persist" : "", | |
| 214 url_encoded_partition.c_str())); | |
| 215 | |
| 216 // If we already have a webview tag in the same app using the same storage | |
| 217 // partition, we should use the same SiteInstance so the existing tag and | |
| 218 // the new tag can script each other. | |
| 219 GuestViewManager* guest_view_manager = | |
| 220 GuestViewManager::FromBrowserContext( | |
| 221 embedder_render_process_host->GetBrowserContext()); | |
| 222 content::SiteInstance* guest_site_instance = | |
| 223 guest_view_manager->GetGuestSiteInstance(guest_site); | |
| 224 if (!guest_site_instance) { | |
| 225 // Create the SiteInstance in a new BrowsingInstance, which will ensure | |
| 226 // that webview tags are also not allowed to send messages across | |
| 227 // different partitions. | |
| 228 guest_site_instance = content::SiteInstance::CreateForURL( | |
| 229 embedder_render_process_host->GetBrowserContext(), guest_site); | |
| 230 } | |
| 231 WebContents::CreateParams params( | |
| 232 embedder_render_process_host->GetBrowserContext(), | |
| 233 guest_site_instance); | |
| 234 params.guest_delegate = this; | |
| 235 callback.Run(WebContents::Create(params)); | |
| 236 } | |
| 237 | |
| 238 void WebViewGuest::DidAttachToEmbedder() { | |
| 239 SetUpAutoSize(); | |
| 240 | |
| 241 std::string name; | |
| 242 if (attach_params()->GetString(webview::kName, &name)) { | |
| 243 // If the guest window's name is empty, then the WebView tag's name is | |
| 244 // assigned. Otherwise, the guest window's name takes precedence over the | |
| 245 // WebView tag's name. | |
| 246 if (name_.empty()) | |
| 247 name_ = name; | |
| 248 } | |
| 249 ReportFrameNameChange(name_); | |
| 250 | |
| 251 std::string user_agent_override; | |
| 252 if (attach_params()->GetString(webview::kParameterUserAgentOverride, | |
| 253 &user_agent_override)) { | |
| 254 SetUserAgentOverride(user_agent_override); | |
| 255 } else { | |
| 256 SetUserAgentOverride(""); | |
| 257 } | |
| 258 | |
| 259 std::string src; | |
| 260 if (attach_params()->GetString("src", &src) && !src.empty()) | |
| 261 NavigateGuest(src); | |
| 262 | |
| 263 if (GetOpener()) { | |
| 264 // We need to do a navigation here if the target URL has changed between | |
| 265 // the time the WebContents was created and the time it was attached. | |
| 266 // We also need to do an initial navigation if a RenderView was never | |
| 267 // created for the new window in cases where there is no referrer. | |
| 268 PendingWindowMap::iterator it = | |
| 269 GetOpener()->pending_new_windows_.find(this); | |
| 270 if (it != GetOpener()->pending_new_windows_.end()) { | |
| 271 const NewWindowInfo& new_window_info = it->second; | |
| 272 if (new_window_info.changed || !guest_web_contents()->HasOpener()) | |
| 273 NavigateGuest(new_window_info.url.spec()); | |
| 274 } else { | |
| 275 NOTREACHED(); | |
| 276 } | |
| 277 | |
| 278 // Once a new guest is attached to the DOM of the embedder page, then the | |
| 279 // lifetime of the new guest is no longer managed by the opener guest. | |
| 280 GetOpener()->pending_new_windows_.erase(this); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 void WebViewGuest::DidInitialize() { | |
| 285 script_executor_.reset( | |
| 286 new ScriptExecutor(guest_web_contents(), &script_observers_)); | |
| 287 | |
| 288 notification_registrar_.Add( | |
| 289 this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, | |
| 290 content::Source<WebContents>(guest_web_contents())); | |
| 291 | |
| 292 notification_registrar_.Add( | |
| 293 this, content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, | |
| 294 content::Source<WebContents>(guest_web_contents())); | |
| 295 | |
| 296 if (web_view_guest_delegate_) | |
| 297 web_view_guest_delegate_->OnDidInitialize(); | |
| 298 AttachWebViewHelpers(guest_web_contents()); | |
| 299 } | |
| 300 | |
| 301 void WebViewGuest::AttachWebViewHelpers(WebContents* contents) { | |
| 302 if (web_view_guest_delegate_) | |
| 303 web_view_guest_delegate_->OnAttachWebViewHelpers(contents); | |
| 304 web_view_permission_helper_.reset(new WebViewPermissionHelper(this)); | |
| 305 } | |
| 306 | |
| 307 void WebViewGuest::DidStopLoading() { | |
| 308 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 309 DispatchEventToEmbedder( | |
| 310 new GuestViewBase::Event(webview::kEventLoadStop, args.Pass())); | |
| 311 } | |
| 312 | |
| 313 void WebViewGuest::EmbedderDestroyed() { | |
| 314 if (web_view_guest_delegate_) | |
| 315 web_view_guest_delegate_->OnEmbedderDestroyed(); | |
| 316 } | |
| 317 | |
| 318 void WebViewGuest::GuestDestroyed() { | |
| 319 // Clean up custom context menu items for this guest. | |
| 320 if (web_view_guest_delegate_) | |
| 321 web_view_guest_delegate_->OnGuestDestroyed(); | |
| 322 RemoveWebViewStateFromIOThread(web_contents()); | |
| 323 } | |
| 324 | |
| 325 void WebViewGuest::GuestReady() { | |
| 326 // The guest RenderView should always live in an isolated guest process. | |
| 327 CHECK(guest_web_contents()->GetRenderProcessHost()->IsIsolatedGuest()); | |
| 328 Send(new ExtensionMsg_SetFrameName( | |
| 329 guest_web_contents()->GetRoutingID(), name_)); | |
| 330 } | |
| 331 | |
| 332 void WebViewGuest::GuestSizeChangedDueToAutoSize(const gfx::Size& old_size, | |
| 333 const gfx::Size& new_size) { | |
| 334 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 335 args->SetInteger(webview::kOldHeight, old_size.height()); | |
| 336 args->SetInteger(webview::kOldWidth, old_size.width()); | |
| 337 args->SetInteger(webview::kNewHeight, new_size.height()); | |
| 338 args->SetInteger(webview::kNewWidth, new_size.width()); | |
| 339 DispatchEventToEmbedder( | |
| 340 new GuestViewBase::Event(webview::kEventSizeChanged, args.Pass())); | |
| 341 } | |
| 342 | |
| 343 bool WebViewGuest::IsAutoSizeSupported() const { | |
| 344 return true; | |
| 345 } | |
| 346 | |
| 347 bool WebViewGuest::IsDragAndDropEnabled() const { | |
| 348 return true; | |
| 349 } | |
| 350 | |
| 351 void WebViewGuest::WillDestroy() { | |
| 352 if (!attached() && GetOpener()) | |
| 353 GetOpener()->pending_new_windows_.erase(this); | |
| 354 DestroyUnattachedWindows(); | |
| 355 } | |
| 356 | |
| 357 bool WebViewGuest::AddMessageToConsole(WebContents* source, | |
| 358 int32 level, | |
| 359 const base::string16& message, | |
| 360 int32 line_no, | |
| 361 const base::string16& source_id) { | |
| 362 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 363 // Log levels are from base/logging.h: LogSeverity. | |
| 364 args->SetInteger(webview::kLevel, level); | |
| 365 args->SetString(webview::kMessage, message); | |
| 366 args->SetInteger(webview::kLine, line_no); | |
| 367 args->SetString(webview::kSourceId, source_id); | |
| 368 DispatchEventToEmbedder( | |
| 369 new GuestViewBase::Event(webview::kEventConsoleMessage, args.Pass())); | |
| 370 return true; | |
| 371 } | |
| 372 | |
| 373 void WebViewGuest::CloseContents(WebContents* source) { | |
| 374 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 375 DispatchEventToEmbedder( | |
| 376 new GuestViewBase::Event(webview::kEventClose, args.Pass())); | |
| 377 } | |
| 378 | |
| 379 void WebViewGuest::FindReply(WebContents* source, | |
| 380 int request_id, | |
| 381 int number_of_matches, | |
| 382 const gfx::Rect& selection_rect, | |
| 383 int active_match_ordinal, | |
| 384 bool final_update) { | |
| 385 find_helper_.FindReply(request_id, number_of_matches, selection_rect, | |
| 386 active_match_ordinal, final_update); | |
| 387 } | |
| 388 | |
| 389 bool WebViewGuest::HandleContextMenu( | |
| 390 const content::ContextMenuParams& params) { | |
| 391 if (!web_view_guest_delegate_) | |
| 392 return false; | |
| 393 return web_view_guest_delegate_->HandleContextMenu(params); | |
| 394 } | |
| 395 | |
| 396 void WebViewGuest::HandleKeyboardEvent( | |
| 397 WebContents* source, | |
| 398 const content::NativeWebKeyboardEvent& event) { | |
| 399 if (!attached()) | |
| 400 return; | |
| 401 | |
| 402 if (HandleKeyboardShortcuts(event)) | |
| 403 return; | |
| 404 | |
| 405 // Send the unhandled keyboard events back to the embedder to reprocess them. | |
| 406 // TODO(fsamuel): This introduces the possibility of out-of-order keyboard | |
| 407 // events because the guest may be arbitrarily delayed when responding to | |
| 408 // keyboard events. In that time, the embedder may have received and processed | |
| 409 // additional key events. This needs to be fixed as soon as possible. | |
| 410 // See http://crbug.com/229882. | |
| 411 embedder_web_contents()->GetDelegate()->HandleKeyboardEvent( | |
| 412 web_contents(), event); | |
| 413 } | |
| 414 | |
| 415 void WebViewGuest::LoadProgressChanged(content::WebContents* source, | |
| 416 double progress) { | |
| 417 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 418 args->SetString(guestview::kUrl, guest_web_contents()->GetURL().spec()); | |
| 419 args->SetDouble(webview::kProgress, progress); | |
| 420 DispatchEventToEmbedder( | |
| 421 new GuestViewBase::Event(webview::kEventLoadProgress, args.Pass())); | |
| 422 } | |
| 423 | |
| 424 void WebViewGuest::LoadAbort(bool is_top_level, | |
| 425 const GURL& url, | |
| 426 const std::string& error_type) { | |
| 427 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 428 args->SetBoolean(guestview::kIsTopLevel, is_top_level); | |
| 429 args->SetString(guestview::kUrl, url.possibly_invalid_spec()); | |
| 430 args->SetString(guestview::kReason, error_type); | |
| 431 DispatchEventToEmbedder( | |
| 432 new GuestViewBase::Event(webview::kEventLoadAbort, args.Pass())); | |
| 433 } | |
| 434 | |
| 435 void WebViewGuest::OnFrameNameChanged(bool is_top_level, | |
| 436 const std::string& name) { | |
| 437 if (!is_top_level) | |
| 438 return; | |
| 439 | |
| 440 if (name_ == name) | |
| 441 return; | |
| 442 | |
| 443 ReportFrameNameChange(name); | |
| 444 } | |
| 445 | |
| 446 void WebViewGuest::CreateNewGuestWebViewWindow( | |
| 447 const content::OpenURLParams& params) { | |
| 448 GuestViewManager* guest_manager = | |
| 449 GuestViewManager::FromBrowserContext(browser_context()); | |
| 450 // Set the attach params to use the same partition as the opener. | |
| 451 // We pull the partition information from the site's URL, which is of the | |
| 452 // form guest://site/{persist}?{partition_name}. | |
| 453 const GURL& site_url = guest_web_contents()->GetSiteInstance()->GetSiteURL(); | |
| 454 const std::string storage_partition_id = | |
| 455 GetStoragePartitionIdFromSiteURL(site_url); | |
| 456 base::DictionaryValue create_params; | |
| 457 create_params.SetString(webview::kStoragePartitionId, storage_partition_id); | |
| 458 | |
| 459 guest_manager->CreateGuest(WebViewGuest::Type, | |
| 460 embedder_extension_id(), | |
| 461 embedder_web_contents(), | |
| 462 create_params, | |
| 463 base::Bind(&WebViewGuest::NewGuestWebViewCallback, | |
| 464 base::Unretained(this), | |
| 465 params)); | |
| 466 } | |
| 467 | |
| 468 void WebViewGuest::NewGuestWebViewCallback( | |
| 469 const content::OpenURLParams& params, | |
| 470 content::WebContents* guest_web_contents) { | |
| 471 WebViewGuest* new_guest = WebViewGuest::FromWebContents(guest_web_contents); | |
| 472 new_guest->SetOpener(this); | |
| 473 | |
| 474 // Take ownership of |new_guest|. | |
| 475 pending_new_windows_.insert( | |
| 476 std::make_pair(new_guest, NewWindowInfo(params.url, std::string()))); | |
| 477 | |
| 478 // Request permission to show the new window. | |
| 479 RequestNewWindowPermission(params.disposition, gfx::Rect(), | |
| 480 params.user_gesture, | |
| 481 new_guest->guest_web_contents()); | |
| 482 } | |
| 483 | |
| 484 // TODO(fsamuel): Find a reliable way to test the 'responsive' and | |
| 485 // 'unresponsive' events. | |
| 486 void WebViewGuest::RendererResponsive(content::WebContents* source) { | |
| 487 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 488 args->SetInteger(webview::kProcessId, | |
| 489 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
| 490 DispatchEventToEmbedder( | |
| 491 new GuestViewBase::Event(webview::kEventResponsive, args.Pass())); | |
| 492 } | |
| 493 | |
| 494 void WebViewGuest::RendererUnresponsive(content::WebContents* source) { | |
| 495 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 496 args->SetInteger(webview::kProcessId, | |
| 497 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
| 498 DispatchEventToEmbedder( | |
| 499 new GuestViewBase::Event(webview::kEventUnresponsive, args.Pass())); | |
| 500 } | |
| 501 | |
| 502 void WebViewGuest::Observe(int type, | |
| 503 const content::NotificationSource& source, | |
| 504 const content::NotificationDetails& details) { | |
| 505 switch (type) { | |
| 506 case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: { | |
| 507 DCHECK_EQ(content::Source<WebContents>(source).ptr(), | |
| 508 guest_web_contents()); | |
| 509 if (content::Source<WebContents>(source).ptr() == guest_web_contents()) | |
| 510 LoadHandlerCalled(); | |
| 511 break; | |
| 512 } | |
| 513 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { | |
| 514 DCHECK_EQ(content::Source<WebContents>(source).ptr(), | |
| 515 guest_web_contents()); | |
| 516 content::ResourceRedirectDetails* resource_redirect_details = | |
| 517 content::Details<content::ResourceRedirectDetails>(details).ptr(); | |
| 518 bool is_top_level = resource_redirect_details->resource_type == | |
| 519 content::RESOURCE_TYPE_MAIN_FRAME; | |
| 520 LoadRedirect(resource_redirect_details->url, | |
| 521 resource_redirect_details->new_url, | |
| 522 is_top_level); | |
| 523 break; | |
| 524 } | |
| 525 default: | |
| 526 NOTREACHED() << "Unexpected notification sent."; | |
| 527 break; | |
| 528 } | |
| 529 } | |
| 530 | |
| 531 double WebViewGuest::GetZoom() { | |
| 532 if (!web_view_guest_delegate_) | |
| 533 return 1.0; | |
| 534 return web_view_guest_delegate_->GetZoom(); | |
| 535 } | |
| 536 | |
| 537 void WebViewGuest::Find( | |
| 538 const base::string16& search_text, | |
| 539 const blink::WebFindOptions& options, | |
| 540 scoped_refptr<WebViewInternalFindFunction> find_function) { | |
| 541 find_helper_.Find(guest_web_contents(), search_text, options, find_function); | |
| 542 } | |
| 543 | |
| 544 void WebViewGuest::StopFinding(content::StopFindAction action) { | |
| 545 find_helper_.CancelAllFindSessions(); | |
| 546 guest_web_contents()->StopFinding(action); | |
| 547 } | |
| 548 | |
| 549 void WebViewGuest::Go(int relative_index) { | |
| 550 guest_web_contents()->GetController().GoToOffset(relative_index); | |
| 551 } | |
| 552 | |
| 553 void WebViewGuest::Reload() { | |
| 554 // TODO(fsamuel): Don't check for repost because we don't want to show | |
| 555 // Chromium's repost warning. We might want to implement a separate API | |
| 556 // for registering a callback if a repost is about to happen. | |
| 557 guest_web_contents()->GetController().Reload(false); | |
| 558 } | |
| 559 | |
| 560 void WebViewGuest::SetUserAgentOverride( | |
| 561 const std::string& user_agent_override) { | |
| 562 if (!attached()) | |
| 563 return; | |
| 564 is_overriding_user_agent_ = !user_agent_override.empty(); | |
| 565 if (is_overriding_user_agent_) { | |
| 566 content::RecordAction(UserMetricsAction("WebView.Guest.OverrideUA")); | |
| 567 } | |
| 568 guest_web_contents()->SetUserAgentOverride(user_agent_override); | |
| 569 } | |
| 570 | |
| 571 void WebViewGuest::Stop() { | |
| 572 guest_web_contents()->Stop(); | |
| 573 } | |
| 574 | |
| 575 void WebViewGuest::Terminate() { | |
| 576 content::RecordAction(UserMetricsAction("WebView.Guest.Terminate")); | |
| 577 base::ProcessHandle process_handle = | |
| 578 guest_web_contents()->GetRenderProcessHost()->GetHandle(); | |
| 579 if (process_handle) | |
| 580 base::KillProcess(process_handle, content::RESULT_CODE_KILLED, false); | |
| 581 } | |
| 582 | |
| 583 bool WebViewGuest::ClearData(const base::Time remove_since, | |
| 584 uint32 removal_mask, | |
| 585 const base::Closure& callback) { | |
| 586 content::RecordAction(UserMetricsAction("WebView.Guest.ClearData")); | |
| 587 content::StoragePartition* partition = | |
| 588 content::BrowserContext::GetStoragePartition( | |
| 589 guest_web_contents()->GetBrowserContext(), | |
| 590 guest_web_contents()->GetSiteInstance()); | |
| 591 | |
| 592 if (!partition) | |
| 593 return false; | |
| 594 | |
| 595 partition->ClearData( | |
| 596 removal_mask, | |
| 597 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, | |
| 598 GURL(), | |
| 599 content::StoragePartition::OriginMatcherFunction(), | |
| 600 remove_since, | |
| 601 base::Time::Now(), | |
| 602 callback); | |
| 603 return true; | |
| 604 } | |
| 605 | |
| 606 WebViewGuest::WebViewGuest(content::BrowserContext* browser_context, | |
| 607 int guest_instance_id) | |
| 608 : GuestView<WebViewGuest>(browser_context, guest_instance_id), | |
| 609 is_overriding_user_agent_(false), | |
| 610 find_helper_(this), | |
| 611 javascript_dialog_helper_(this) { | |
| 612 web_view_guest_delegate_.reset( | |
| 613 ExtensionsAPIClient::Get()->CreateWebViewGuestDelegate(this)); | |
| 614 } | |
| 615 | |
| 616 WebViewGuest::~WebViewGuest() { | |
| 617 } | |
| 618 | |
| 619 void WebViewGuest::DidCommitProvisionalLoadForFrame( | |
| 620 content::RenderFrameHost* render_frame_host, | |
| 621 const GURL& url, | |
| 622 content::PageTransition transition_type) { | |
| 623 find_helper_.CancelAllFindSessions(); | |
| 624 | |
| 625 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 626 args->SetString(guestview::kUrl, url.spec()); | |
| 627 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent()); | |
| 628 args->SetInteger(webview::kInternalCurrentEntryIndex, | |
| 629 guest_web_contents()->GetController().GetCurrentEntryIndex()); | |
| 630 args->SetInteger(webview::kInternalEntryCount, | |
| 631 guest_web_contents()->GetController().GetEntryCount()); | |
| 632 args->SetInteger(webview::kInternalProcessId, | |
| 633 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
| 634 DispatchEventToEmbedder( | |
| 635 new GuestViewBase::Event(webview::kEventLoadCommit, args.Pass())); | |
| 636 if (web_view_guest_delegate_) { | |
| 637 web_view_guest_delegate_->OnDidCommitProvisionalLoadForFrame( | |
| 638 !render_frame_host->GetParent()); | |
| 639 } | |
| 640 } | |
| 641 | |
| 642 void WebViewGuest::DidFailProvisionalLoad( | |
| 643 content::RenderFrameHost* render_frame_host, | |
| 644 const GURL& validated_url, | |
| 645 int error_code, | |
| 646 const base::string16& error_description) { | |
| 647 LoadAbort(!render_frame_host->GetParent(), validated_url, | |
| 648 net::ErrorToShortString(error_code)); | |
| 649 } | |
| 650 | |
| 651 void WebViewGuest::DidStartProvisionalLoadForFrame( | |
| 652 content::RenderFrameHost* render_frame_host, | |
| 653 const GURL& validated_url, | |
| 654 bool is_error_page, | |
| 655 bool is_iframe_srcdoc) { | |
| 656 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 657 args->SetString(guestview::kUrl, validated_url.spec()); | |
| 658 args->SetBoolean(guestview::kIsTopLevel, !render_frame_host->GetParent()); | |
| 659 DispatchEventToEmbedder( | |
| 660 new GuestViewBase::Event(webview::kEventLoadStart, args.Pass())); | |
| 661 } | |
| 662 | |
| 663 void WebViewGuest::DocumentLoadedInFrame( | |
| 664 content::RenderFrameHost* render_frame_host) { | |
| 665 if (web_view_guest_delegate_) | |
| 666 web_view_guest_delegate_->OnDocumentLoadedInFrame(render_frame_host); | |
| 667 } | |
| 668 | |
| 669 bool WebViewGuest::OnMessageReceived(const IPC::Message& message, | |
| 670 RenderFrameHost* render_frame_host) { | |
| 671 bool handled = true; | |
| 672 IPC_BEGIN_MESSAGE_MAP(WebViewGuest, message) | |
| 673 IPC_MESSAGE_HANDLER(ExtensionHostMsg_FrameNameChanged, OnFrameNameChanged) | |
| 674 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 675 IPC_END_MESSAGE_MAP() | |
| 676 return handled; | |
| 677 } | |
| 678 | |
| 679 void WebViewGuest::RenderProcessGone(base::TerminationStatus status) { | |
| 680 // Cancel all find sessions in progress. | |
| 681 find_helper_.CancelAllFindSessions(); | |
| 682 | |
| 683 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 684 args->SetInteger(webview::kProcessId, | |
| 685 guest_web_contents()->GetRenderProcessHost()->GetID()); | |
| 686 args->SetString(webview::kReason, TerminationStatusToString(status)); | |
| 687 DispatchEventToEmbedder( | |
| 688 new GuestViewBase::Event(webview::kEventExit, args.Pass())); | |
| 689 } | |
| 690 | |
| 691 void WebViewGuest::UserAgentOverrideSet(const std::string& user_agent) { | |
| 692 if (!attached()) | |
| 693 return; | |
| 694 content::NavigationController& controller = | |
| 695 guest_web_contents()->GetController(); | |
| 696 content::NavigationEntry* entry = controller.GetVisibleEntry(); | |
| 697 if (!entry) | |
| 698 return; | |
| 699 entry->SetIsOverridingUserAgent(!user_agent.empty()); | |
| 700 guest_web_contents()->GetController().Reload(false); | |
| 701 } | |
| 702 | |
| 703 void WebViewGuest::ReportFrameNameChange(const std::string& name) { | |
| 704 name_ = name; | |
| 705 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 706 args->SetString(webview::kName, name); | |
| 707 DispatchEventToEmbedder( | |
| 708 new GuestViewBase::Event(webview::kEventFrameNameChanged, args.Pass())); | |
| 709 } | |
| 710 | |
| 711 void WebViewGuest::LoadHandlerCalled() { | |
| 712 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 713 DispatchEventToEmbedder( | |
| 714 new GuestViewBase::Event(webview::kEventContentLoad, args.Pass())); | |
| 715 } | |
| 716 | |
| 717 void WebViewGuest::LoadRedirect(const GURL& old_url, | |
| 718 const GURL& new_url, | |
| 719 bool is_top_level) { | |
| 720 scoped_ptr<base::DictionaryValue> args(new base::DictionaryValue()); | |
| 721 args->SetBoolean(guestview::kIsTopLevel, is_top_level); | |
| 722 args->SetString(webview::kNewURL, new_url.spec()); | |
| 723 args->SetString(webview::kOldURL, old_url.spec()); | |
| 724 DispatchEventToEmbedder( | |
| 725 new GuestViewBase::Event(webview::kEventLoadRedirect, args.Pass())); | |
| 726 } | |
| 727 | |
| 728 void WebViewGuest::PushWebViewStateToIOThread() { | |
| 729 const GURL& site_url = guest_web_contents()->GetSiteInstance()->GetSiteURL(); | |
| 730 std::string partition_domain; | |
| 731 std::string partition_id; | |
| 732 bool in_memory; | |
| 733 if (!GetGuestPartitionConfigForSite( | |
| 734 site_url, &partition_domain, &partition_id, &in_memory)) { | |
| 735 NOTREACHED(); | |
| 736 return; | |
| 737 } | |
| 738 DCHECK(embedder_extension_id() == partition_domain); | |
| 739 | |
| 740 WebViewRendererState::WebViewInfo web_view_info; | |
| 741 web_view_info.embedder_process_id = embedder_render_process_id(); | |
| 742 web_view_info.instance_id = view_instance_id(); | |
| 743 web_view_info.partition_id = partition_id; | |
| 744 web_view_info.embedder_extension_id = embedder_extension_id(); | |
| 745 | |
| 746 content::BrowserThread::PostTask( | |
| 747 content::BrowserThread::IO, | |
| 748 FROM_HERE, | |
| 749 base::Bind(&WebViewRendererState::AddGuest, | |
| 750 base::Unretained(WebViewRendererState::GetInstance()), | |
| 751 guest_web_contents()->GetRenderProcessHost()->GetID(), | |
| 752 guest_web_contents()->GetRoutingID(), | |
| 753 web_view_info)); | |
| 754 } | |
| 755 | |
| 756 // static | |
| 757 void WebViewGuest::RemoveWebViewStateFromIOThread( | |
| 758 WebContents* web_contents) { | |
| 759 content::BrowserThread::PostTask( | |
| 760 content::BrowserThread::IO, FROM_HERE, | |
| 761 base::Bind( | |
| 762 &WebViewRendererState::RemoveGuest, | |
| 763 base::Unretained(WebViewRendererState::GetInstance()), | |
| 764 web_contents->GetRenderProcessHost()->GetID(), | |
| 765 web_contents->GetRoutingID())); | |
| 766 } | |
| 767 | |
| 768 content::WebContents* WebViewGuest::CreateNewGuestWindow( | |
| 769 const content::WebContents::CreateParams& create_params) { | |
| 770 GuestViewManager* guest_manager = | |
| 771 GuestViewManager::FromBrowserContext(browser_context()); | |
| 772 return guest_manager->CreateGuestWithWebContentsParams( | |
| 773 WebViewGuest::Type, | |
| 774 embedder_extension_id(), | |
| 775 embedder_web_contents()->GetRenderProcessHost()->GetID(), | |
| 776 create_params); | |
| 777 } | |
| 778 | |
| 779 void WebViewGuest::RequestMediaAccessPermission( | |
| 780 content::WebContents* source, | |
| 781 const content::MediaStreamRequest& request, | |
| 782 const content::MediaResponseCallback& callback) { | |
| 783 web_view_permission_helper_->RequestMediaAccessPermission(source, | |
| 784 request, | |
| 785 callback); | |
| 786 } | |
| 787 | |
| 788 void WebViewGuest::CanDownload( | |
| 789 content::RenderViewHost* render_view_host, | |
| 790 const GURL& url, | |
| 791 const std::string& request_method, | |
| 792 const base::Callback<void(bool)>& callback) { | |
| 793 web_view_permission_helper_->CanDownload(render_view_host, | |
| 794 url, | |
| 795 request_method, | |
| 796 callback); | |
| 797 } | |
| 798 | |
| 799 void WebViewGuest::RequestPointerLockPermission( | |
| 800 bool user_gesture, | |
| 801 bool last_unlocked_by_target, | |
| 802 const base::Callback<void(bool)>& callback) { | |
| 803 web_view_permission_helper_->RequestPointerLockPermission( | |
| 804 user_gesture, | |
| 805 last_unlocked_by_target, | |
| 806 callback); | |
| 807 } | |
| 808 | |
| 809 void WebViewGuest::WillAttachToEmbedder() { | |
| 810 // We must install the mapping from guests to WebViews prior to resuming | |
| 811 // suspended resource loads so that the WebRequest API will catch resource | |
| 812 // requests. | |
| 813 PushWebViewStateToIOThread(); | |
| 814 } | |
| 815 | |
| 816 content::JavaScriptDialogManager* | |
| 817 WebViewGuest::GetJavaScriptDialogManager() { | |
| 818 return &javascript_dialog_helper_; | |
| 819 } | |
| 820 | |
| 821 content::ColorChooser* WebViewGuest::OpenColorChooser( | |
| 822 WebContents* web_contents, | |
| 823 SkColor color, | |
| 824 const std::vector<content::ColorSuggestion>& suggestions) { | |
| 825 if (!attached() || !embedder_web_contents()->GetDelegate()) | |
| 826 return NULL; | |
| 827 return embedder_web_contents()->GetDelegate()->OpenColorChooser( | |
| 828 web_contents, color, suggestions); | |
| 829 } | |
| 830 | |
| 831 void WebViewGuest::RunFileChooser(WebContents* web_contents, | |
| 832 const content::FileChooserParams& params) { | |
| 833 if (!attached() || !embedder_web_contents()->GetDelegate()) | |
| 834 return; | |
| 835 | |
| 836 embedder_web_contents()->GetDelegate()->RunFileChooser(web_contents, params); | |
| 837 } | |
| 838 | |
| 839 void WebViewGuest::NavigateGuest(const std::string& src) { | |
| 840 GURL url = ResolveURL(src); | |
| 841 | |
| 842 // Do not allow navigating a guest to schemes other than known safe schemes. | |
| 843 // This will block the embedder trying to load unwanted schemes, e.g. | |
| 844 // chrome://settings. | |
| 845 bool scheme_is_blocked = | |
| 846 (!content::ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme( | |
| 847 url.scheme()) && | |
| 848 !url.SchemeIs(url::kAboutScheme)) || | |
| 849 url.SchemeIs(url::kJavaScriptScheme); | |
| 850 if (scheme_is_blocked || !url.is_valid()) { | |
| 851 LoadAbort(true /* is_top_level */, url, | |
| 852 net::ErrorToShortString(net::ERR_ABORTED)); | |
| 853 return; | |
| 854 } | |
| 855 | |
| 856 GURL validated_url(url); | |
| 857 guest_web_contents()->GetRenderProcessHost()-> | |
| 858 FilterURL(false, &validated_url); | |
| 859 // As guests do not swap processes on navigation, only navigations to | |
| 860 // normal web URLs are supported. No protocol handlers are installed for | |
| 861 // other schemes (e.g., WebUI or extensions), and no permissions or bindings | |
| 862 // can be granted to the guest process. | |
| 863 LoadURLWithParams(validated_url, | |
| 864 content::Referrer(), | |
| 865 content::PAGE_TRANSITION_AUTO_TOPLEVEL, | |
| 866 guest_web_contents()); | |
| 867 } | |
| 868 | |
| 869 bool WebViewGuest::HandleKeyboardShortcuts( | |
| 870 const content::NativeWebKeyboardEvent& event) { | |
| 871 if (event.type != blink::WebInputEvent::RawKeyDown) | |
| 872 return false; | |
| 873 | |
| 874 // If the user hits the escape key without any modifiers then unlock the | |
| 875 // mouse if necessary. | |
| 876 if ((event.windowsKeyCode == ui::VKEY_ESCAPE) && | |
| 877 !(event.modifiers & blink::WebInputEvent::InputModifiers)) { | |
| 878 return guest_web_contents()->GotResponseToLockMouseRequest(false); | |
| 879 } | |
| 880 | |
| 881 #if defined(OS_MACOSX) | |
| 882 if (event.modifiers != blink::WebInputEvent::MetaKey) | |
| 883 return false; | |
| 884 | |
| 885 if (event.windowsKeyCode == ui::VKEY_OEM_4) { | |
| 886 Go(-1); | |
| 887 return true; | |
| 888 } | |
| 889 | |
| 890 if (event.windowsKeyCode == ui::VKEY_OEM_6) { | |
| 891 Go(1); | |
| 892 return true; | |
| 893 } | |
| 894 #else | |
| 895 if (event.windowsKeyCode == ui::VKEY_BROWSER_BACK) { | |
| 896 Go(-1); | |
| 897 return true; | |
| 898 } | |
| 899 | |
| 900 if (event.windowsKeyCode == ui::VKEY_BROWSER_FORWARD) { | |
| 901 Go(1); | |
| 902 return true; | |
| 903 } | |
| 904 #endif | |
| 905 | |
| 906 return false; | |
| 907 } | |
| 908 | |
| 909 void WebViewGuest::SetUpAutoSize() { | |
| 910 // Read the autosize parameters passed in from the embedder. | |
| 911 bool auto_size_enabled = false; | |
| 912 attach_params()->GetBoolean(webview::kAttributeAutoSize, &auto_size_enabled); | |
| 913 | |
| 914 int max_height = 0; | |
| 915 int max_width = 0; | |
| 916 attach_params()->GetInteger(webview::kAttributeMaxHeight, &max_height); | |
| 917 attach_params()->GetInteger(webview::kAttributeMaxWidth, &max_width); | |
| 918 | |
| 919 int min_height = 0; | |
| 920 int min_width = 0; | |
| 921 attach_params()->GetInteger(webview::kAttributeMinHeight, &min_height); | |
| 922 attach_params()->GetInteger(webview::kAttributeMinWidth, &min_width); | |
| 923 | |
| 924 // Call SetAutoSize to apply all the appropriate validation and clipping of | |
| 925 // values. | |
| 926 SetAutoSize(auto_size_enabled, | |
| 927 gfx::Size(min_width, min_height), | |
| 928 gfx::Size(max_width, max_height)); | |
| 929 } | |
| 930 | |
| 931 void WebViewGuest::ShowContextMenu( | |
| 932 int request_id, | |
| 933 const WebViewGuestDelegate::MenuItemVector* items) { | |
| 934 if (web_view_guest_delegate_) | |
| 935 web_view_guest_delegate_->OnShowContextMenu(request_id, items); | |
| 936 } | |
| 937 | |
| 938 void WebViewGuest::SetName(const std::string& name) { | |
| 939 if (name_ == name) | |
| 940 return; | |
| 941 name_ = name; | |
| 942 | |
| 943 Send(new ExtensionMsg_SetFrameName(routing_id(), name_)); | |
| 944 } | |
| 945 | |
| 946 void WebViewGuest::SetZoom(double zoom_factor) { | |
| 947 if (web_view_guest_delegate_) | |
| 948 web_view_guest_delegate_->OnSetZoom(zoom_factor); | |
| 949 } | |
| 950 | |
| 951 void WebViewGuest::AddNewContents(content::WebContents* source, | |
| 952 content::WebContents* new_contents, | |
| 953 WindowOpenDisposition disposition, | |
| 954 const gfx::Rect& initial_pos, | |
| 955 bool user_gesture, | |
| 956 bool* was_blocked) { | |
| 957 if (was_blocked) | |
| 958 *was_blocked = false; | |
| 959 RequestNewWindowPermission(disposition, | |
| 960 initial_pos, | |
| 961 user_gesture, | |
| 962 new_contents); | |
| 963 } | |
| 964 | |
| 965 content::WebContents* WebViewGuest::OpenURLFromTab( | |
| 966 content::WebContents* source, | |
| 967 const content::OpenURLParams& params) { | |
| 968 // If the guest wishes to navigate away prior to attachment then we save the | |
| 969 // navigation to perform upon attachment. Navigation initializes a lot of | |
| 970 // state that assumes an embedder exists, such as RenderWidgetHostViewGuest. | |
| 971 // Navigation also resumes resource loading which we don't want to allow | |
| 972 // until attachment. | |
| 973 if (!attached()) { | |
| 974 WebViewGuest* opener = GetOpener(); | |
| 975 PendingWindowMap::iterator it = | |
| 976 opener->pending_new_windows_.find(this); | |
| 977 if (it == opener->pending_new_windows_.end()) | |
| 978 return NULL; | |
| 979 const NewWindowInfo& info = it->second; | |
| 980 NewWindowInfo new_window_info(params.url, info.name); | |
| 981 new_window_info.changed = new_window_info.url != info.url; | |
| 982 it->second = new_window_info; | |
| 983 return NULL; | |
| 984 } | |
| 985 if (params.disposition == CURRENT_TAB) { | |
| 986 // This can happen for cross-site redirects. | |
| 987 LoadURLWithParams(params.url, params.referrer, params.transition, source); | |
| 988 return source; | |
| 989 } | |
| 990 | |
| 991 CreateNewGuestWebViewWindow(params); | |
| 992 return NULL; | |
| 993 } | |
| 994 | |
| 995 void WebViewGuest::WebContentsCreated(WebContents* source_contents, | |
| 996 int opener_render_frame_id, | |
| 997 const base::string16& frame_name, | |
| 998 const GURL& target_url, | |
| 999 content::WebContents* new_contents) { | |
| 1000 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents); | |
| 1001 CHECK(guest); | |
| 1002 guest->SetOpener(this); | |
| 1003 std::string guest_name = base::UTF16ToUTF8(frame_name); | |
| 1004 guest->name_ = guest_name; | |
| 1005 pending_new_windows_.insert( | |
| 1006 std::make_pair(guest, NewWindowInfo(target_url, guest_name))); | |
| 1007 } | |
| 1008 | |
| 1009 void WebViewGuest::LoadURLWithParams(const GURL& url, | |
| 1010 const content::Referrer& referrer, | |
| 1011 content::PageTransition transition_type, | |
| 1012 content::WebContents* web_contents) { | |
| 1013 content::NavigationController::LoadURLParams load_url_params(url); | |
| 1014 load_url_params.referrer = referrer; | |
| 1015 load_url_params.transition_type = transition_type; | |
| 1016 load_url_params.extra_headers = std::string(); | |
| 1017 if (is_overriding_user_agent_) { | |
| 1018 load_url_params.override_user_agent = | |
| 1019 content::NavigationController::UA_OVERRIDE_TRUE; | |
| 1020 } | |
| 1021 web_contents->GetController().LoadURLWithParams(load_url_params); | |
| 1022 } | |
| 1023 | |
| 1024 void WebViewGuest::RequestNewWindowPermission( | |
| 1025 WindowOpenDisposition disposition, | |
| 1026 const gfx::Rect& initial_bounds, | |
| 1027 bool user_gesture, | |
| 1028 content::WebContents* new_contents) { | |
| 1029 WebViewGuest* guest = WebViewGuest::FromWebContents(new_contents); | |
| 1030 if (!guest) | |
| 1031 return; | |
| 1032 PendingWindowMap::iterator it = pending_new_windows_.find(guest); | |
| 1033 if (it == pending_new_windows_.end()) | |
| 1034 return; | |
| 1035 const NewWindowInfo& new_window_info = it->second; | |
| 1036 | |
| 1037 // Retrieve the opener partition info if we have it. | |
| 1038 const GURL& site_url = new_contents->GetSiteInstance()->GetSiteURL(); | |
| 1039 std::string storage_partition_id = GetStoragePartitionIdFromSiteURL(site_url); | |
| 1040 | |
| 1041 base::DictionaryValue request_info; | |
| 1042 request_info.SetInteger(webview::kInitialHeight, initial_bounds.height()); | |
| 1043 request_info.SetInteger(webview::kInitialWidth, initial_bounds.width()); | |
| 1044 request_info.Set(webview::kTargetURL, | |
| 1045 new base::StringValue(new_window_info.url.spec())); | |
| 1046 request_info.Set(webview::kName, new base::StringValue(new_window_info.name)); | |
| 1047 request_info.SetInteger(webview::kWindowID, guest->guest_instance_id()); | |
| 1048 // We pass in partition info so that window-s created through newwindow | |
| 1049 // API can use it to set their partition attribute. | |
| 1050 request_info.Set(webview::kStoragePartitionId, | |
| 1051 new base::StringValue(storage_partition_id)); | |
| 1052 request_info.Set( | |
| 1053 webview::kWindowOpenDisposition, | |
| 1054 new base::StringValue(WindowOpenDispositionToString(disposition))); | |
| 1055 | |
| 1056 web_view_permission_helper_-> | |
| 1057 RequestPermission(WEB_VIEW_PERMISSION_TYPE_NEW_WINDOW, | |
| 1058 request_info, | |
| 1059 base::Bind(&WebViewGuest::OnWebViewNewWindowResponse, | |
| 1060 base::Unretained(this), | |
| 1061 guest->guest_instance_id()), | |
| 1062 false /* allowed_by_default */); | |
| 1063 } | |
| 1064 | |
| 1065 void WebViewGuest::DestroyUnattachedWindows() { | |
| 1066 // Destroy() reaches in and removes the WebViewGuest from its opener's | |
| 1067 // pending_new_windows_ set. To avoid mutating the set while iterating, we | |
| 1068 // create a copy of the pending new windows set and iterate over the copy. | |
| 1069 PendingWindowMap pending_new_windows(pending_new_windows_); | |
| 1070 // Clean up unattached new windows opened by this guest. | |
| 1071 for (PendingWindowMap::const_iterator it = pending_new_windows.begin(); | |
| 1072 it != pending_new_windows.end(); ++it) { | |
| 1073 it->first->Destroy(); | |
| 1074 } | |
| 1075 // All pending windows should be removed from the set after Destroy() is | |
| 1076 // called on all of them. | |
| 1077 DCHECK(pending_new_windows_.empty()); | |
| 1078 } | |
| 1079 | |
| 1080 GURL WebViewGuest::ResolveURL(const std::string& src) { | |
| 1081 if (!in_extension()) { | |
| 1082 NOTREACHED(); | |
| 1083 return GURL(src); | |
| 1084 } | |
| 1085 | |
| 1086 GURL default_url(base::StringPrintf("%s://%s/", | |
| 1087 kExtensionScheme, | |
| 1088 embedder_extension_id().c_str())); | |
| 1089 return default_url.Resolve(src); | |
| 1090 } | |
| 1091 | |
| 1092 void WebViewGuest::OnWebViewNewWindowResponse( | |
| 1093 int new_window_instance_id, | |
| 1094 bool allow, | |
| 1095 const std::string& user_input) { | |
| 1096 WebViewGuest* guest = | |
| 1097 WebViewGuest::From(embedder_render_process_id(), new_window_instance_id); | |
| 1098 if (!guest) | |
| 1099 return; | |
| 1100 | |
| 1101 if (!allow) | |
| 1102 guest->Destroy(); | |
| 1103 } | |
| 1104 | |
| 1105 } // namespace extensions | |
| OLD | NEW |