Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Side by Side Diff: extensions/browser/guest_view/web_view/web_view_guest.cc

Issue 505893002: Revert of Move core web_view code to extensions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698