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

Side by Side Diff: chrome/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 "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
OLDNEW
« no previous file with comments | « chrome/browser/guest_view/web_view/web_view_guest.h ('k') | chrome/browser/guest_view/web_view/web_view_permission_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698