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