Merge pull request #8406 from electron/use_gn-chrome54
[platform/framework/web/crosswalk-tizen.git] / atom / browser / api / atom_api_web_contents.cc
1 // Copyright (c) 2014 GitHub, Inc.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
4
5 #include "atom/browser/api/atom_api_web_contents.h"
6
7 #include <set>
8 #include <string>
9
10 #include "atom/browser/api/atom_api_debugger.h"
11 #include "atom/browser/api/atom_api_session.h"
12 #include "atom/browser/api/atom_api_window.h"
13 #include "atom/browser/atom_browser_client.h"
14 #include "atom/browser/atom_browser_context.h"
15 #include "atom/browser/atom_browser_main_parts.h"
16 #include "atom/browser/atom_security_state_model_client.h"
17 #include "atom/browser/lib/bluetooth_chooser.h"
18 #include "atom/browser/native_window.h"
19 #include "atom/browser/net/atom_network_delegate.h"
20 #include "atom/browser/osr/osr_output_device.h"
21 #include "atom/browser/osr/osr_render_widget_host_view.h"
22 #include "atom/browser/osr/osr_web_contents_view.h"
23 #include "atom/browser/ui/drag_util.h"
24 #include "atom/browser/web_contents_permission_helper.h"
25 #include "atom/browser/web_contents_preferences.h"
26 #include "atom/browser/web_view_guest_delegate.h"
27 #include "atom/common/api/api_messages.h"
28 #include "atom/common/api/event_emitter_caller.h"
29 #include "atom/common/color_util.h"
30 #include "atom/common/mouse_util.h"
31 #include "atom/common/native_mate_converters/blink_converter.h"
32 #include "atom/common/native_mate_converters/callback.h"
33 #include "atom/common/native_mate_converters/content_converter.h"
34 #include "atom/common/native_mate_converters/file_path_converter.h"
35 #include "atom/common/native_mate_converters/gfx_converter.h"
36 #include "atom/common/native_mate_converters/gurl_converter.h"
37 #include "atom/common/native_mate_converters/image_converter.h"
38 #include "atom/common/native_mate_converters/net_converter.h"
39 #include "atom/common/native_mate_converters/string16_converter.h"
40 #include "atom/common/native_mate_converters/value_converter.h"
41 #include "atom/common/options_switches.h"
42 #include "base/strings/utf_string_conversions.h"
43 #include "base/threading/thread_task_runner_handle.h"
44 #include "brightray/browser/inspectable_web_contents.h"
45 #include "brightray/browser/inspectable_web_contents_view.h"
46 #include "chrome/browser/printing/print_preview_message_handler.h"
47 #include "chrome/browser/printing/print_view_manager_basic.h"
48 #include "content/browser/renderer_host/render_widget_host_impl.h"
49 #include "content/browser/web_contents/web_contents_impl.h"
50 #include "content/common/view_messages.h"
51 #include "content/public/browser/favicon_status.h"
52 #include "content/public/browser/native_web_keyboard_event.h"
53 #include "content/public/browser/navigation_details.h"
54 #include "content/public/browser/navigation_entry.h"
55 #include "content/public/browser/navigation_handle.h"
56 #include "content/public/browser/plugin_service.h"
57 #include "content/public/browser/render_frame_host.h"
58 #include "content/public/browser/render_process_host.h"
59 #include "content/public/browser/render_view_host.h"
60 #include "content/public/browser/render_widget_host.h"
61 #include "content/public/browser/render_widget_host_view.h"
62 #include "content/public/browser/resource_request_details.h"
63 #include "content/public/browser/service_worker_context.h"
64 #include "content/public/browser/site_instance.h"
65 #include "content/public/browser/storage_partition.h"
66 #include "content/public/browser/web_contents.h"
67 #include "content/public/common/context_menu_params.h"
68 #include "native_mate/dictionary.h"
69 #include "native_mate/object_template_builder.h"
70 #include "net/url_request/url_request_context.h"
71 #include "third_party/WebKit/public/web/WebFindOptions.h"
72 #include "third_party/WebKit/public/web/WebInputEvent.h"
73 #include "ui/display/screen.h"
74
75 #if !defined(OS_MACOSX)
76 #include "ui/aura/window.h"
77 #endif
78
79 #include "atom/common/node_includes.h"
80
81 namespace {
82
83 struct PrintSettings {
84   bool silent;
85   bool print_background;
86 };
87
88 }  // namespace
89
90 namespace mate {
91
92 template<>
93 struct Converter<atom::SetSizeParams> {
94   static bool FromV8(v8::Isolate* isolate,
95                      v8::Local<v8::Value> val,
96                      atom::SetSizeParams* out) {
97     mate::Dictionary params;
98     if (!ConvertFromV8(isolate, val, &params))
99       return false;
100     bool autosize;
101     if (params.Get("enableAutoSize", &autosize))
102       out->enable_auto_size.reset(new bool(true));
103     gfx::Size size;
104     if (params.Get("min", &size))
105       out->min_size.reset(new gfx::Size(size));
106     if (params.Get("max", &size))
107       out->max_size.reset(new gfx::Size(size));
108     if (params.Get("normal", &size))
109       out->normal_size.reset(new gfx::Size(size));
110     return true;
111   }
112 };
113
114 template<>
115 struct Converter<PrintSettings> {
116   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
117                      PrintSettings* out) {
118     mate::Dictionary dict;
119     if (!ConvertFromV8(isolate, val, &dict))
120       return false;
121     dict.Get("silent", &(out->silent));
122     dict.Get("printBackground", &(out->print_background));
123     return true;
124   }
125 };
126
127 template<>
128 struct Converter<WindowOpenDisposition> {
129   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
130                                    WindowOpenDisposition val) {
131     std::string disposition = "other";
132     switch (val) {
133       case CURRENT_TAB: disposition = "default"; break;
134       case NEW_FOREGROUND_TAB: disposition = "foreground-tab"; break;
135       case NEW_BACKGROUND_TAB: disposition = "background-tab"; break;
136       case NEW_POPUP: case NEW_WINDOW: disposition = "new-window"; break;
137       case SAVE_TO_DISK: disposition = "save-to-disk"; break;
138       default: break;
139     }
140     return mate::ConvertToV8(isolate, disposition);
141   }
142 };
143
144 template<>
145 struct Converter<content::SavePageType> {
146   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
147                      content::SavePageType* out) {
148     std::string save_type;
149     if (!ConvertFromV8(isolate, val, &save_type))
150       return false;
151     save_type = base::ToLowerASCII(save_type);
152     if (save_type == "htmlonly") {
153       *out = content::SAVE_PAGE_TYPE_AS_ONLY_HTML;
154     } else if (save_type == "htmlcomplete") {
155       *out = content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML;
156     } else if (save_type == "mhtml") {
157       *out = content::SAVE_PAGE_TYPE_AS_MHTML;
158     } else {
159       return false;
160     }
161     return true;
162   }
163 };
164
165 template<>
166 struct Converter<atom::api::WebContents::Type> {
167   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
168                                    atom::api::WebContents::Type val) {
169     using Type = atom::api::WebContents::Type;
170     std::string type = "";
171     switch (val) {
172       case Type::BACKGROUND_PAGE: type = "backgroundPage"; break;
173       case Type::BROWSER_WINDOW: type = "window"; break;
174       case Type::REMOTE: type = "remote"; break;
175       case Type::WEB_VIEW: type = "webview"; break;
176       case Type::OFF_SCREEN: type = "offscreen"; break;
177       default: break;
178     }
179     return mate::ConvertToV8(isolate, type);
180   }
181
182   static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
183                      atom::api::WebContents::Type* out) {
184     using Type = atom::api::WebContents::Type;
185     std::string type;
186     if (!ConvertFromV8(isolate, val, &type))
187       return false;
188     if (type == "webview") {
189       *out = Type::WEB_VIEW;
190     } else if (type == "backgroundPage") {
191       *out = Type::BACKGROUND_PAGE;
192     } else if (type == "offscreen") {
193       *out = Type::OFF_SCREEN;
194     } else {
195       return false;
196     }
197     return true;
198   }
199 };
200
201 }  // namespace mate
202
203
204 namespace atom {
205
206 namespace api {
207
208 namespace {
209
210 content::ServiceWorkerContext* GetServiceWorkerContext(
211     const content::WebContents* web_contents) {
212   auto context = web_contents->GetBrowserContext();
213   auto site_instance = web_contents->GetSiteInstance();
214   if (!context || !site_instance)
215     return nullptr;
216
217   auto storage_partition =
218       content::BrowserContext::GetStoragePartition(context, site_instance);
219   if (!storage_partition)
220     return nullptr;
221
222   return storage_partition->GetServiceWorkerContext();
223 }
224
225 // Called when CapturePage is done.
226 void OnCapturePageDone(base::Callback<void(const gfx::Image&)> callback,
227                        const SkBitmap& bitmap,
228                        content::ReadbackResponse response) {
229   callback.Run(gfx::Image::CreateFrom1xBitmap(bitmap));
230 }
231
232 }  // namespace
233
234 WebContents::WebContents(v8::Isolate* isolate,
235                          content::WebContents* web_contents,
236                          Type type)
237     : content::WebContentsObserver(web_contents),
238       embedder_(nullptr),
239       type_(type),
240       request_id_(0),
241       background_throttling_(true),
242       enable_devtools_(true) {
243
244   if (type == REMOTE) {
245     web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
246     Init(isolate);
247     AttachAsUserData(web_contents);
248   } else {
249     const mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
250     auto session = Session::CreateFrom(isolate, GetBrowserContext());
251     session_.Reset(isolate, session.ToV8());
252     InitWithSessionAndOptions(isolate, web_contents, session, options);
253   }
254 }
255
256 WebContents::WebContents(v8::Isolate* isolate,
257                          const mate::Dictionary& options)
258     : embedder_(nullptr),
259       type_(BROWSER_WINDOW),
260       request_id_(0),
261       background_throttling_(true),
262       enable_devtools_(true) {
263   // Read options.
264   options.Get("backgroundThrottling", &background_throttling_);
265
266   // FIXME(zcbenz): We should read "type" parameter for better design, but
267   // on Windows we have encountered a compiler bug that if we read "type"
268   // from |options| and then set |type_|, a memory corruption will happen
269   // and Electron will soon crash.
270   // Remvoe this after we upgraded to use VS 2015 Update 3.
271   bool b = false;
272   if (options.Get("isGuest", &b) && b)
273     type_ = WEB_VIEW;
274   else if (options.Get("isBackgroundPage", &b) && b)
275     type_ = BACKGROUND_PAGE;
276   else if (options.Get("offscreen", &b) && b)
277     type_ = OFF_SCREEN;
278
279   // Whether to enable DevTools.
280   options.Get("devTools", &enable_devtools_);
281
282   // Obtain the session.
283   std::string partition;
284   mate::Handle<api::Session> session;
285   if (options.Get("session", &session)) {
286   } else if (options.Get("partition", &partition)) {
287     session = Session::FromPartition(isolate, partition);
288   } else {
289     // Use the default session if not specified.
290     session = Session::FromPartition(isolate, "");
291   }
292   session_.Reset(isolate, session.ToV8());
293
294   content::WebContents* web_contents;
295   if (IsGuest()) {
296     scoped_refptr<content::SiteInstance> site_instance =
297         content::SiteInstance::CreateForURL(
298             session->browser_context(), GURL("chrome-guest://fake-host"));
299     content::WebContents::CreateParams params(
300         session->browser_context(), site_instance);
301     guest_delegate_.reset(new WebViewGuestDelegate);
302     params.guest_delegate = guest_delegate_.get();
303     web_contents = content::WebContents::Create(params);
304   } else if (IsOffScreen()) {
305     bool transparent = false;
306     options.Get("transparent", &transparent);
307
308     content::WebContents::CreateParams params(session->browser_context());
309     auto* view = new OffScreenWebContentsView(
310         transparent, base::Bind(&WebContents::OnPaint, base::Unretained(this)));
311     params.view = view;
312     params.delegate_view = view;
313
314     web_contents = content::WebContents::Create(params);
315     view->SetWebContents(web_contents);
316   } else {
317     content::WebContents::CreateParams params(session->browser_context());
318     web_contents = content::WebContents::Create(params);
319   }
320
321   InitWithSessionAndOptions(isolate, web_contents, session, options);
322 }
323
324 void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate,
325                                             content::WebContents *web_contents,
326                                             mate::Handle<api::Session> session,
327                                             const mate::Dictionary& options) {
328   Observe(web_contents);
329   InitWithWebContents(web_contents, session->browser_context());
330
331   managed_web_contents()->GetView()->SetDelegate(this);
332
333   // Save the preferences in C++.
334   new WebContentsPreferences(web_contents, options);
335
336   // Intialize permission helper.
337   WebContentsPermissionHelper::CreateForWebContents(web_contents);
338   // Intialize security state client.
339   AtomSecurityStateModelClient::CreateForWebContents(web_contents);
340
341   web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
342
343   if (IsGuest()) {
344     guest_delegate_->Initialize(this);
345
346     NativeWindow* owner_window = nullptr;
347     if (options.Get("embedder", &embedder_) && embedder_) {
348       // New WebContents's owner_window is the embedder's owner_window.
349       auto relay =
350           NativeWindowRelay::FromWebContents(embedder_->web_contents());
351       if (relay)
352         owner_window = relay->window.get();
353     }
354     if (owner_window)
355       SetOwnerWindow(owner_window);
356   }
357
358   Init(isolate);
359   AttachAsUserData(web_contents);
360 }
361
362 WebContents::~WebContents() {
363   // The destroy() is called.
364   if (managed_web_contents()) {
365     // For webview we need to tell content module to do some cleanup work before
366     // destroying it.
367     if (type_ == WEB_VIEW)
368       guest_delegate_->Destroy();
369
370     // The WebContentsDestroyed will not be called automatically because we
371     // unsubscribe from webContents before destroying it. So we have to manually
372     // call it here to make sure "destroyed" event is emitted.
373     RenderViewDeleted(web_contents()->GetRenderViewHost());
374     WebContentsDestroyed();
375   }
376 }
377
378 bool WebContents::AddMessageToConsole(content::WebContents* source,
379                                       int32_t level,
380                                       const base::string16& message,
381                                       int32_t line_no,
382                                       const base::string16& source_id) {
383   if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) {
384     return false;
385   } else {
386     Emit("console-message", level, message, line_no, source_id);
387     return true;
388   }
389 }
390
391 void WebContents::OnCreateWindow(
392     const GURL& target_url,
393     const std::string& frame_name,
394     WindowOpenDisposition disposition,
395     const std::vector<base::string16>& features,
396     const scoped_refptr<content::ResourceRequestBodyImpl>& body) {
397   if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
398     Emit("-new-window", target_url, frame_name, disposition, features, body);
399   else
400     Emit("new-window", target_url, frame_name, disposition, features);
401 }
402
403 void WebContents::WebContentsCreated(content::WebContents* source_contents,
404                                      int opener_render_frame_id,
405                                      const std::string& frame_name,
406                                      const GURL& target_url,
407                                      content::WebContents* new_contents) {
408   v8::Locker locker(isolate());
409   v8::HandleScope handle_scope(isolate());
410   auto api_web_contents = CreateFrom(isolate(), new_contents, BROWSER_WINDOW);
411   Emit("-web-contents-created", api_web_contents, target_url, frame_name);
412 }
413
414 void WebContents::AddNewContents(content::WebContents* source,
415                                  content::WebContents* new_contents,
416                                  WindowOpenDisposition disposition,
417                                  const gfx::Rect& initial_rect,
418                                  bool user_gesture,
419                                  bool* was_blocked) {
420   v8::Locker locker(isolate());
421   v8::HandleScope handle_scope(isolate());
422   auto api_web_contents = CreateFrom(isolate(), new_contents);
423   if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
424       initial_rect.x(), initial_rect.y(), initial_rect.width(),
425       initial_rect.height())) {
426     api_web_contents->DestroyWebContents();
427   }
428 }
429
430 content::WebContents* WebContents::OpenURLFromTab(
431     content::WebContents* source,
432     const content::OpenURLParams& params) {
433   if (params.disposition != CURRENT_TAB) {
434     if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
435       Emit("-new-window", params.url, "", params.disposition);
436     else
437       Emit("new-window", params.url, "", params.disposition);
438     return nullptr;
439   }
440
441   // Give user a chance to cancel navigation.
442   if (Emit("will-navigate", params.url))
443     return nullptr;
444
445   // Don't load the URL if the web contents was marked as destroyed from a
446   // will-navigate event listener
447   if (IsDestroyed())
448     return nullptr;
449
450   return CommonWebContentsDelegate::OpenURLFromTab(source, params);
451 }
452
453 void WebContents::BeforeUnloadFired(content::WebContents* tab,
454                                     bool proceed,
455                                     bool* proceed_to_fire_unload) {
456   if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
457     *proceed_to_fire_unload = proceed;
458   else
459     *proceed_to_fire_unload = true;
460 }
461
462 void WebContents::MoveContents(content::WebContents* source,
463                                const gfx::Rect& pos) {
464   Emit("move", pos);
465 }
466
467 void WebContents::CloseContents(content::WebContents* source) {
468   Emit("close");
469
470   if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
471     owner_window()->CloseContents(source);
472 }
473
474 void WebContents::ActivateContents(content::WebContents* source) {
475   Emit("activate");
476 }
477
478 void WebContents::UpdateTargetURL(content::WebContents* source,
479                                   const GURL& url) {
480   Emit("update-target-url", url);
481 }
482
483 bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
484   return type_ == BROWSER_WINDOW;
485 }
486
487 void WebContents::HandleKeyboardEvent(
488     content::WebContents* source,
489     const content::NativeWebKeyboardEvent& event) {
490   if (type_ == WEB_VIEW && embedder_) {
491     // Send the unhandled keyboard events back to the embedder.
492     embedder_->HandleKeyboardEvent(source, event);
493   } else {
494     // Go to the default keyboard handling.
495     CommonWebContentsDelegate::HandleKeyboardEvent(source, event);
496   }
497 }
498
499 bool WebContents::PreHandleKeyboardEvent(
500     content::WebContents* source,
501     const content::NativeWebKeyboardEvent& event,
502     bool* is_keyboard_shortcut) {
503   if (event.type == blink::WebInputEvent::Type::RawKeyDown
504       || event.type == blink::WebInputEvent::Type::KeyUp)
505     return Emit("before-input-event", event);
506   else
507     return false;
508 }
509
510 void WebContents::EnterFullscreenModeForTab(content::WebContents* source,
511                                             const GURL& origin) {
512   auto permission_helper =
513       WebContentsPermissionHelper::FromWebContents(source);
514   auto callback = base::Bind(&WebContents::OnEnterFullscreenModeForTab,
515                              base::Unretained(this), source, origin);
516   permission_helper->RequestFullscreenPermission(callback);
517 }
518
519 void WebContents::OnEnterFullscreenModeForTab(content::WebContents* source,
520                                               const GURL& origin,
521                                               bool allowed) {
522   if (!allowed)
523     return;
524   CommonWebContentsDelegate::EnterFullscreenModeForTab(source, origin);
525   Emit("enter-html-full-screen");
526 }
527
528 void WebContents::ExitFullscreenModeForTab(content::WebContents* source) {
529   CommonWebContentsDelegate::ExitFullscreenModeForTab(source);
530   Emit("leave-html-full-screen");
531 }
532
533 void WebContents::RendererUnresponsive(content::WebContents* source) {
534   Emit("unresponsive");
535   if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
536     owner_window()->RendererUnresponsive(source);
537 }
538
539 void WebContents::RendererResponsive(content::WebContents* source) {
540   Emit("responsive");
541   if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
542     owner_window()->RendererResponsive(source);
543 }
544
545 bool WebContents::HandleContextMenu(const content::ContextMenuParams& params) {
546   if (params.custom_context.is_pepper_menu) {
547     Emit("pepper-context-menu", std::make_pair(params, web_contents()));
548     web_contents()->NotifyContextMenuClosed(params.custom_context);
549   } else {
550     Emit("context-menu", std::make_pair(params, web_contents()));
551   }
552
553   return true;
554 }
555
556 bool WebContents::OnGoToEntryOffset(int offset) {
557   GoToOffset(offset);
558   return false;
559 }
560
561 void WebContents::FindReply(content::WebContents* web_contents,
562                             int request_id,
563                             int number_of_matches,
564                             const gfx::Rect& selection_rect,
565                             int active_match_ordinal,
566                             bool final_update) {
567   if (!final_update)
568     return;
569
570   v8::Locker locker(isolate());
571   v8::HandleScope handle_scope(isolate());
572   mate::Dictionary result = mate::Dictionary::CreateEmpty(isolate());
573   result.Set("requestId", request_id);
574   result.Set("matches", number_of_matches);
575   result.Set("selectionArea", selection_rect);
576   result.Set("activeMatchOrdinal", active_match_ordinal);
577   result.Set("finalUpdate", final_update);  // Deprecate after 2.0
578   Emit("found-in-page", result);
579 }
580
581 bool WebContents::CheckMediaAccessPermission(
582     content::WebContents* web_contents,
583     const GURL& security_origin,
584     content::MediaStreamType type) {
585   return true;
586 }
587
588 void WebContents::RequestMediaAccessPermission(
589     content::WebContents* web_contents,
590     const content::MediaStreamRequest& request,
591     const content::MediaResponseCallback& callback) {
592   auto permission_helper =
593       WebContentsPermissionHelper::FromWebContents(web_contents);
594   permission_helper->RequestMediaAccessPermission(request, callback);
595 }
596
597 void WebContents::RequestToLockMouse(
598     content::WebContents* web_contents,
599     bool user_gesture,
600     bool last_unlocked_by_target) {
601   auto permission_helper =
602       WebContentsPermissionHelper::FromWebContents(web_contents);
603   permission_helper->RequestPointerLockPermission(user_gesture);
604 }
605
606 std::unique_ptr<content::BluetoothChooser> WebContents::RunBluetoothChooser(
607     content::RenderFrameHost* frame,
608     const content::BluetoothChooser::EventHandler& event_handler) {
609   std::unique_ptr<BluetoothChooser> bluetooth_chooser(
610       new BluetoothChooser(this, event_handler));
611   return std::move(bluetooth_chooser);
612 }
613
614 void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
615   // Do nothing, we override this method just to avoid compilation error since
616   // there are two virtual functions named BeforeUnloadFired.
617 }
618
619 void WebContents::RenderViewCreated(content::RenderViewHost* render_view_host) {
620   const auto impl = content::RenderWidgetHostImpl::FromID(
621       render_view_host->GetProcess()->GetID(),
622       render_view_host->GetRoutingID());
623   if (impl)
624     impl->disable_hidden_ = !background_throttling_;
625 }
626
627 void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
628   Emit("render-view-deleted", render_view_host->GetProcess()->GetID());
629 }
630
631 void WebContents::RenderProcessGone(base::TerminationStatus status) {
632   Emit("crashed", status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
633 }
634
635 void WebContents::PluginCrashed(const base::FilePath& plugin_path,
636                                 base::ProcessId plugin_pid) {
637   content::WebPluginInfo info;
638   auto plugin_service = content::PluginService::GetInstance();
639   plugin_service->GetPluginInfoByPath(plugin_path, &info);
640   Emit("plugin-crashed", info.name, info.version);
641 }
642
643 void WebContents::MediaStartedPlaying(const MediaPlayerId& id) {
644   Emit("media-started-playing");
645 }
646
647 void WebContents::MediaStoppedPlaying(const MediaPlayerId& id) {
648   Emit("media-paused");
649 }
650
651 void WebContents::DidChangeThemeColor(SkColor theme_color) {
652   Emit("did-change-theme-color", atom::ToRGBHex(theme_color));
653 }
654
655 void WebContents::DocumentLoadedInFrame(
656     content::RenderFrameHost* render_frame_host) {
657   if (!render_frame_host->GetParent())
658     Emit("dom-ready");
659 }
660
661 void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
662                                 const GURL& validated_url) {
663   bool is_main_frame = !render_frame_host->GetParent();
664   Emit("did-frame-finish-load", is_main_frame);
665
666   if (is_main_frame)
667     Emit("did-finish-load");
668 }
669
670 void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
671                               const GURL& url,
672                               int error_code,
673                               const base::string16& error_description,
674                               bool was_ignored_by_handler) {
675   bool is_main_frame = !render_frame_host->GetParent();
676   Emit("did-fail-load", error_code, error_description, url, is_main_frame);
677 }
678
679 void WebContents::DidStartLoading() {
680   Emit("did-start-loading");
681 }
682
683 void WebContents::DidStopLoading() {
684   Emit("did-stop-loading");
685 }
686
687 void WebContents::DidGetResourceResponseStart(
688     const content::ResourceRequestDetails& details) {
689   Emit("did-get-response-details",
690        details.socket_address.IsEmpty(),
691        details.url,
692        details.original_url,
693        details.http_response_code,
694        details.method,
695        details.referrer,
696        details.headers.get(),
697        ResourceTypeToString(details.resource_type));
698 }
699
700 void WebContents::DidGetRedirectForResourceRequest(
701     content::RenderFrameHost* render_frame_host,
702     const content::ResourceRedirectDetails& details) {
703   Emit("did-get-redirect-request",
704        details.url,
705        details.new_url,
706        (details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME),
707        details.http_response_code,
708        details.method,
709        details.referrer,
710        details.headers.get());
711 }
712
713 void WebContents::DidFinishNavigation(
714     content::NavigationHandle* navigation_handle) {
715   bool is_main_frame = navigation_handle->IsInMainFrame();
716   if (navigation_handle->HasCommitted() && !navigation_handle->IsErrorPage()) {
717     auto url = navigation_handle->GetURL();
718     bool is_in_page = navigation_handle->IsSamePage();
719     if (is_main_frame && !is_in_page) {
720       Emit("did-navigate", url);
721     } else if (is_in_page) {
722       Emit("did-navigate-in-page", url, is_main_frame);
723     }
724   } else {
725     auto url = navigation_handle->GetURL();
726     int code = navigation_handle->GetNetErrorCode();
727     auto description = net::ErrorToShortString(code);
728     Emit("did-fail-provisional-load", code, description, url, is_main_frame);
729
730     // Do not emit "did-fail-load" for canceled requests.
731     if (code != net::ERR_ABORTED)
732       Emit("did-fail-load", code, description, url, is_main_frame);
733   }
734 }
735
736 void WebContents::TitleWasSet(content::NavigationEntry* entry,
737                               bool explicit_set) {
738   if (entry)
739     Emit("-page-title-updated", entry->GetTitle(), explicit_set);
740   else
741     Emit("-page-title-updated", "", explicit_set);
742 }
743
744 void WebContents::DidUpdateFaviconURL(
745     const std::vector<content::FaviconURL>& urls) {
746   std::set<GURL> unique_urls;
747   for (const auto& iter : urls) {
748     if (iter.icon_type != content::FaviconURL::FAVICON)
749       continue;
750     const GURL& url = iter.icon_url;
751     if (url.is_valid())
752       unique_urls.insert(url);
753   }
754   Emit("page-favicon-updated", unique_urls);
755 }
756
757 void WebContents::DevToolsReloadPage() {
758   Emit("devtools-reload-page");
759 }
760
761 void WebContents::DevToolsFocused() {
762   Emit("devtools-focused");
763 }
764
765 void WebContents::DevToolsOpened() {
766   v8::Locker locker(isolate());
767   v8::HandleScope handle_scope(isolate());
768   auto handle = WebContents::CreateFrom(
769       isolate(), managed_web_contents()->GetDevToolsWebContents());
770   devtools_web_contents_.Reset(isolate(), handle.ToV8());
771
772   // Set inspected tabID.
773   base::FundamentalValue tab_id(ID());
774   managed_web_contents()->CallClientFunction(
775       "DevToolsAPI.setInspectedTabId", &tab_id, nullptr, nullptr);
776
777   // Inherit owner window in devtools.
778   if (owner_window())
779     handle->SetOwnerWindow(managed_web_contents()->GetDevToolsWebContents(),
780                            owner_window());
781
782   Emit("devtools-opened");
783 }
784
785 void WebContents::DevToolsClosed() {
786   v8::Locker locker(isolate());
787   v8::HandleScope handle_scope(isolate());
788   devtools_web_contents_.Reset();
789
790   Emit("devtools-closed");
791 }
792
793 bool WebContents::OnMessageReceived(const IPC::Message& message) {
794   bool handled = true;
795   IPC_BEGIN_MESSAGE_MAP(WebContents, message)
796     IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
797     IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
798                                     OnRendererMessageSync)
799     IPC_MESSAGE_HANDLER_CODE(ViewHostMsg_SetCursor, OnCursorChange,
800       handled = false)
801     IPC_MESSAGE_UNHANDLED(handled = false)
802   IPC_END_MESSAGE_MAP()
803
804   return handled;
805 }
806
807 // There are three ways of destroying a webContents:
808 // 1. call webContents.destroy();
809 // 2. garbage collection;
810 // 3. user closes the window of webContents;
811 // For webview only #1 will happen, for BrowserWindow both #1 and #3 may
812 // happen. The #2 should never happen for webContents, because webview is
813 // managed by GuestViewManager, and BrowserWindow's webContents is managed
814 // by api::Window.
815 // For #1, the destructor will do the cleanup work and we only need to make
816 // sure "destroyed" event is emitted. For #3, the content::WebContents will
817 // be destroyed on close, and WebContentsDestroyed would be called for it, so
818 // we need to make sure the api::WebContents is also deleted.
819 void WebContents::WebContentsDestroyed() {
820   // This event is only for internal use, which is emitted when WebContents is
821   // being destroyed.
822   Emit("will-destroy");
823
824   // Cleanup relationships with other parts.
825   RemoveFromWeakMap();
826
827   // We can not call Destroy here because we need to call Emit first, but we
828   // also do not want any method to be used, so just mark as destroyed here.
829   MarkDestroyed();
830
831   Emit("destroyed");
832
833   // Destroy the native class in next tick.
834   base::ThreadTaskRunnerHandle::Get()->PostTask(
835       FROM_HERE, GetDestroyClosure());
836 }
837
838 void WebContents::NavigationEntryCommitted(
839     const content::LoadCommittedDetails& details) {
840   Emit("navigation-entry-commited", details.entry->GetURL(),
841        details.is_in_page, details.did_replace_entry);
842 }
843
844 int64_t WebContents::GetID() const {
845   int64_t process_id = web_contents()->GetRenderProcessHost()->GetID();
846   int64_t routing_id = web_contents()->GetRoutingID();
847   int64_t rv = (process_id << 32) + routing_id;
848   return rv;
849 }
850
851 int WebContents::GetProcessID() const {
852   return web_contents()->GetRenderProcessHost()->GetID();
853 }
854
855 WebContents::Type WebContents::GetType() const {
856   return type_;
857 }
858
859 bool WebContents::Equal(const WebContents* web_contents) const {
860   return GetID() == web_contents->GetID();
861 }
862
863 void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
864   if (!url.is_valid()) {
865     Emit("did-fail-load",
866          static_cast<int>(net::ERR_INVALID_URL),
867          net::ErrorToShortString(net::ERR_INVALID_URL),
868          url.possibly_invalid_spec(),
869          true);
870     return;
871   }
872
873   content::NavigationController::LoadURLParams params(url);
874
875   GURL http_referrer;
876   if (options.Get("httpReferrer", &http_referrer))
877     params.referrer = content::Referrer(http_referrer.GetAsReferrer(),
878                                         blink::WebReferrerPolicyDefault);
879
880   std::string user_agent;
881   if (options.Get("userAgent", &user_agent))
882     web_contents()->SetUserAgentOverride(user_agent);
883
884   std::string extra_headers;
885   if (options.Get("extraHeaders", &extra_headers))
886     params.extra_headers = extra_headers;
887
888   scoped_refptr<content::ResourceRequestBodyImpl> body;
889   if (options.Get("postData", &body)) {
890     params.post_data = body;
891     params.load_type = content::NavigationController::LOAD_TYPE_HTTP_POST;
892   }
893
894   params.transition_type = ui::PAGE_TRANSITION_TYPED;
895   params.should_clear_history_list = true;
896   params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
897   web_contents()->GetController().LoadURLWithParams(params);
898
899   // Set the background color of RenderWidgetHostView.
900   // We have to call it right after LoadURL because the RenderViewHost is only
901   // created after loading a page.
902   const auto view = web_contents()->GetRenderWidgetHostView();
903   WebContentsPreferences* web_preferences =
904       WebContentsPreferences::FromWebContents(web_contents());
905   std::string color_name;
906   if (web_preferences->web_preferences()->GetString(options::kBackgroundColor,
907                                                     &color_name)) {
908     view->SetBackgroundColor(ParseHexColor(color_name));
909   } else {
910     view->SetBackgroundColor(SK_ColorTRANSPARENT);
911   }
912 }
913
914 void WebContents::DownloadURL(const GURL& url) {
915   auto browser_context = web_contents()->GetBrowserContext();
916   auto download_manager =
917     content::BrowserContext::GetDownloadManager(browser_context);
918
919   download_manager->DownloadUrl(
920       content::DownloadUrlParameters::CreateForWebContentsMainFrame(
921           web_contents(), url));
922 }
923
924 GURL WebContents::GetURL() const {
925   return web_contents()->GetURL();
926 }
927
928 base::string16 WebContents::GetTitle() const {
929   return web_contents()->GetTitle();
930 }
931
932 bool WebContents::IsLoading() const {
933   return web_contents()->IsLoading();
934 }
935
936 bool WebContents::IsLoadingMainFrame() const {
937   // Comparing site instances works because Electron always creates a new site
938   // instance when navigating, regardless of origin. See AtomBrowserClient.
939   return (web_contents()->GetLastCommittedURL().is_empty() ||
940           web_contents()->GetSiteInstance() !=
941           web_contents()->GetPendingSiteInstance()) && IsLoading();
942 }
943
944 bool WebContents::IsWaitingForResponse() const {
945   return web_contents()->IsWaitingForResponse();
946 }
947
948 void WebContents::Stop() {
949   web_contents()->Stop();
950 }
951
952 void WebContents::GoBack() {
953   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
954   web_contents()->GetController().GoBack();
955 }
956
957 void WebContents::GoForward() {
958   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
959   web_contents()->GetController().GoForward();
960 }
961
962 void WebContents::GoToOffset(int offset) {
963   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
964   web_contents()->GetController().GoToOffset(offset);
965 }
966
967 bool WebContents::IsCrashed() const {
968   return web_contents()->IsCrashed();
969 }
970
971 void WebContents::SetUserAgent(const std::string& user_agent,
972                                mate::Arguments* args) {
973   web_contents()->SetUserAgentOverride(user_agent);
974 }
975
976 std::string WebContents::GetUserAgent() {
977   return web_contents()->GetUserAgentOverride();
978 }
979
980 bool WebContents::SavePage(const base::FilePath& full_file_path,
981                            const content::SavePageType& save_type,
982                            const SavePageHandler::SavePageCallback& callback) {
983   auto handler = new SavePageHandler(web_contents(), callback);
984   return handler->Handle(full_file_path, save_type);
985 }
986
987 void WebContents::OpenDevTools(mate::Arguments* args) {
988   if (type_ == REMOTE)
989     return;
990
991   if (!enable_devtools_)
992     return;
993
994   std::string state;
995   if (type_ == WEB_VIEW || !owner_window()) {
996     state = "detach";
997   } else if (args && args->Length() == 1) {
998     bool detach = false;
999     mate::Dictionary options;
1000     if (args->GetNext(&options)) {
1001       options.Get("mode", &state);
1002
1003       // TODO(kevinsawicki) Remove in 2.0
1004       options.Get("detach", &detach);
1005       if (state.empty() && detach)
1006         state = "detach";
1007     }
1008   }
1009   managed_web_contents()->SetDockState(state);
1010   managed_web_contents()->ShowDevTools();
1011 }
1012
1013 void WebContents::CloseDevTools() {
1014   if (type_ == REMOTE)
1015     return;
1016
1017   managed_web_contents()->CloseDevTools();
1018 }
1019
1020 bool WebContents::IsDevToolsOpened() {
1021   if (type_ == REMOTE)
1022     return false;
1023
1024   return managed_web_contents()->IsDevToolsViewShowing();
1025 }
1026
1027 bool WebContents::IsDevToolsFocused() {
1028   if (type_ == REMOTE)
1029     return false;
1030
1031   return managed_web_contents()->GetView()->IsDevToolsViewFocused();
1032 }
1033
1034 void WebContents::EnableDeviceEmulation(
1035     const blink::WebDeviceEmulationParams& params) {
1036   if (type_ == REMOTE)
1037     return;
1038
1039   Send(new ViewMsg_EnableDeviceEmulation(routing_id(), params));
1040 }
1041
1042 void WebContents::DisableDeviceEmulation() {
1043   if (type_ == REMOTE)
1044     return;
1045
1046   Send(new ViewMsg_DisableDeviceEmulation(routing_id()));
1047 }
1048
1049 void WebContents::ToggleDevTools() {
1050   if (IsDevToolsOpened())
1051     CloseDevTools();
1052   else
1053     OpenDevTools(nullptr);
1054 }
1055
1056 void WebContents::InspectElement(int x, int y) {
1057   if (type_ == REMOTE)
1058     return;
1059
1060   if (!enable_devtools_)
1061     return;
1062
1063   if (!managed_web_contents()->GetDevToolsWebContents())
1064     OpenDevTools(nullptr);
1065   managed_web_contents()->InspectElement(x, y);
1066 }
1067
1068 void WebContents::InspectServiceWorker() {
1069   if (type_ == REMOTE)
1070     return;
1071
1072   if (!enable_devtools_)
1073     return;
1074
1075   for (const auto& agent_host : content::DevToolsAgentHost::GetOrCreateAll()) {
1076     if (agent_host->GetType() ==
1077         content::DevToolsAgentHost::TYPE_SERVICE_WORKER) {
1078       OpenDevTools(nullptr);
1079       managed_web_contents()->AttachTo(agent_host);
1080       break;
1081     }
1082   }
1083 }
1084
1085 void WebContents::HasServiceWorker(
1086     const base::Callback<void(bool)>& callback) {
1087   auto context = GetServiceWorkerContext(web_contents());
1088   if (!context)
1089     return;
1090
1091   context->CheckHasServiceWorker(web_contents()->GetLastCommittedURL(),
1092                                  GURL::EmptyGURL(),
1093                                  callback);
1094 }
1095
1096 void WebContents::UnregisterServiceWorker(
1097     const base::Callback<void(bool)>& callback) {
1098   auto context = GetServiceWorkerContext(web_contents());
1099   if (!context)
1100     return;
1101
1102   context->UnregisterServiceWorker(web_contents()->GetLastCommittedURL(),
1103                                    callback);
1104 }
1105
1106 void WebContents::SetAudioMuted(bool muted) {
1107   web_contents()->SetAudioMuted(muted);
1108 }
1109
1110 bool WebContents::IsAudioMuted() {
1111   return web_contents()->IsAudioMuted();
1112 }
1113
1114 void WebContents::Print(mate::Arguments* args) {
1115   PrintSettings settings = { false, false };
1116   if (args->Length() == 1 && !args->GetNext(&settings)) {
1117     args->ThrowError();
1118     return;
1119   }
1120
1121   printing::PrintViewManagerBasic::FromWebContents(web_contents())->
1122       PrintNow(settings.silent, settings.print_background);
1123 }
1124
1125 void WebContents::PrintToPDF(const base::DictionaryValue& setting,
1126                              const PrintToPDFCallback& callback) {
1127   printing::PrintPreviewMessageHandler::FromWebContents(web_contents())->
1128       PrintToPDF(setting, callback);
1129 }
1130
1131 void WebContents::AddWorkSpace(mate::Arguments* args,
1132                                const base::FilePath& path) {
1133   if (path.empty()) {
1134     args->ThrowError("path cannot be empty");
1135     return;
1136   }
1137   DevToolsAddFileSystem(path);
1138 }
1139
1140 void WebContents::RemoveWorkSpace(mate::Arguments* args,
1141                                   const base::FilePath& path) {
1142   if (path.empty()) {
1143     args->ThrowError("path cannot be empty");
1144     return;
1145   }
1146   DevToolsRemoveFileSystem(path);
1147 }
1148
1149 void WebContents::Undo() {
1150   web_contents()->Undo();
1151 }
1152
1153 void WebContents::Redo() {
1154   web_contents()->Redo();
1155 }
1156
1157 void WebContents::Cut() {
1158   web_contents()->Cut();
1159 }
1160
1161 void WebContents::Copy() {
1162   web_contents()->Copy();
1163 }
1164
1165 void WebContents::Paste() {
1166   web_contents()->Paste();
1167 }
1168
1169 void WebContents::PasteAndMatchStyle() {
1170   web_contents()->PasteAndMatchStyle();
1171 }
1172
1173 void WebContents::Delete() {
1174   web_contents()->Delete();
1175 }
1176
1177 void WebContents::SelectAll() {
1178   web_contents()->SelectAll();
1179 }
1180
1181 void WebContents::Unselect() {
1182   web_contents()->Unselect();
1183 }
1184
1185 void WebContents::Replace(const base::string16& word) {
1186   web_contents()->Replace(word);
1187 }
1188
1189 void WebContents::ReplaceMisspelling(const base::string16& word) {
1190   web_contents()->ReplaceMisspelling(word);
1191 }
1192
1193 uint32_t WebContents::FindInPage(mate::Arguments* args) {
1194   uint32_t request_id = GetNextRequestId();
1195   base::string16 search_text;
1196   blink::WebFindOptions options;
1197   if (!args->GetNext(&search_text) || search_text.empty()) {
1198     args->ThrowError("Must provide a non-empty search content");
1199     return 0;
1200   }
1201
1202   args->GetNext(&options);
1203
1204   web_contents()->Find(request_id, search_text, options);
1205   return request_id;
1206 }
1207
1208 void WebContents::StopFindInPage(content::StopFindAction action) {
1209   web_contents()->StopFinding(action);
1210 }
1211
1212 void WebContents::ShowDefinitionForSelection() {
1213 #if defined(OS_MACOSX)
1214   const auto view = web_contents()->GetRenderWidgetHostView();
1215   if (view)
1216     view->ShowDefinitionForSelection();
1217 #endif
1218 }
1219
1220 void WebContents::CopyImageAt(int x, int y) {
1221   const auto host = web_contents()->GetMainFrame();
1222   if (host)
1223     host->CopyImageAt(x, y);
1224 }
1225
1226 void WebContents::Focus() {
1227   web_contents()->Focus();
1228 }
1229
1230 #if !defined(OS_MACOSX)
1231 bool WebContents::IsFocused() const {
1232   auto view = web_contents()->GetRenderWidgetHostView();
1233   if (!view) return false;
1234
1235   if (GetType() != BACKGROUND_PAGE) {
1236     auto window = web_contents()->GetNativeView()->GetToplevelWindow();
1237     if (window && !window->IsVisible())
1238       return false;
1239   }
1240
1241   return view->HasFocus();
1242 }
1243 #endif
1244
1245 void WebContents::TabTraverse(bool reverse) {
1246   web_contents()->FocusThroughTabTraversal(reverse);
1247 }
1248
1249 bool WebContents::SendIPCMessage(bool all_frames,
1250                                  const base::string16& channel,
1251                                  const base::ListValue& args) {
1252   return Send(new AtomViewMsg_Message(routing_id(), all_frames, channel, args));
1253 }
1254
1255 void WebContents::SendInputEvent(v8::Isolate* isolate,
1256                                  v8::Local<v8::Value> input_event) {
1257   const auto view = web_contents()->GetRenderWidgetHostView();
1258   if (!view)
1259     return;
1260   const auto host = view->GetRenderWidgetHost();
1261   if (!host)
1262     return;
1263
1264   int type = mate::GetWebInputEventType(isolate, input_event);
1265   if (blink::WebInputEvent::isMouseEventType(type)) {
1266     blink::WebMouseEvent mouse_event;
1267     if (mate::ConvertFromV8(isolate, input_event, &mouse_event)) {
1268       host->ForwardMouseEvent(mouse_event);
1269       return;
1270     }
1271   } else if (blink::WebInputEvent::isKeyboardEventType(type)) {
1272     content::NativeWebKeyboardEvent keyboard_event;
1273     if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) {
1274       host->ForwardKeyboardEvent(keyboard_event);
1275       return;
1276     }
1277   } else if (type == blink::WebInputEvent::MouseWheel) {
1278     blink::WebMouseWheelEvent mouse_wheel_event;
1279     if (mate::ConvertFromV8(isolate, input_event, &mouse_wheel_event)) {
1280       host->ForwardWheelEvent(mouse_wheel_event);
1281       return;
1282     }
1283   }
1284
1285   isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
1286       isolate, "Invalid event object")));
1287 }
1288
1289 void WebContents::BeginFrameSubscription(mate::Arguments* args) {
1290   bool only_dirty = false;
1291   FrameSubscriber::FrameCaptureCallback callback;
1292
1293   args->GetNext(&only_dirty);
1294   if (!args->GetNext(&callback)) {
1295     args->ThrowError();
1296     return;
1297   }
1298
1299   const auto view = web_contents()->GetRenderWidgetHostView();
1300   if (view) {
1301     std::unique_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber(
1302         isolate(), view, callback, only_dirty));
1303     view->BeginFrameSubscription(std::move(frame_subscriber));
1304   }
1305 }
1306
1307 void WebContents::EndFrameSubscription() {
1308   const auto view = web_contents()->GetRenderWidgetHostView();
1309   if (view)
1310     view->EndFrameSubscription();
1311 }
1312
1313 void WebContents::StartDrag(const mate::Dictionary& item,
1314                             mate::Arguments* args) {
1315   base::FilePath file;
1316   std::vector<base::FilePath> files;
1317   if (!item.Get("files", &files) && item.Get("file", &file)) {
1318     files.push_back(file);
1319   }
1320
1321   mate::Handle<NativeImage> icon;
1322   if (!item.Get("icon", &icon) && !file.empty()) {
1323     // TODO(zcbenz): Set default icon from file.
1324   }
1325
1326   // Error checking.
1327   if (icon.IsEmpty()) {
1328     args->ThrowError("icon must be set");
1329     return;
1330   }
1331
1332   // Start dragging.
1333   if (!files.empty()) {
1334     base::MessageLoop::ScopedNestableTaskAllower allow(
1335         base::MessageLoop::current());
1336     DragFileItems(files, icon->image(), web_contents()->GetNativeView());
1337   } else {
1338     args->ThrowError("There is nothing to drag");
1339   }
1340 }
1341
1342 void WebContents::CapturePage(mate::Arguments* args) {
1343   gfx::Rect rect;
1344   base::Callback<void(const gfx::Image&)> callback;
1345
1346   if (!(args->Length() == 1 && args->GetNext(&callback)) &&
1347       !(args->Length() == 2 && args->GetNext(&rect)
1348                             && args->GetNext(&callback))) {
1349     args->ThrowError();
1350     return;
1351   }
1352
1353   const auto view = web_contents()->GetRenderWidgetHostView();
1354   const auto host = view ? view->GetRenderWidgetHost() : nullptr;
1355   if (!view || !host) {
1356     callback.Run(gfx::Image());
1357     return;
1358   }
1359
1360   // Capture full page if user doesn't specify a |rect|.
1361   const gfx::Size view_size = rect.IsEmpty() ? view->GetViewBounds().size() :
1362                                                rect.size();
1363
1364   // By default, the requested bitmap size is the view size in screen
1365   // coordinates.  However, if there's more pixel detail available on the
1366   // current system, increase the requested bitmap size to capture it all.
1367   gfx::Size bitmap_size = view_size;
1368   const gfx::NativeView native_view = view->GetNativeView();
1369   const float scale =
1370       display::Screen::GetScreen()->GetDisplayNearestWindow(native_view)
1371       .device_scale_factor();
1372   if (scale > 1.0f)
1373     bitmap_size = gfx::ScaleToCeiledSize(view_size, scale);
1374
1375   host->CopyFromBackingStore(gfx::Rect(rect.origin(), view_size),
1376                              bitmap_size,
1377                              base::Bind(&OnCapturePageDone, callback),
1378                              kBGRA_8888_SkColorType);
1379 }
1380
1381 void WebContents::OnCursorChange(const content::WebCursor& cursor) {
1382   content::WebCursor::CursorInfo info;
1383   cursor.GetCursorInfo(&info);
1384
1385   if (cursor.IsCustom()) {
1386     Emit("cursor-changed", CursorTypeToString(info),
1387       gfx::Image::CreateFrom1xBitmap(info.custom_image),
1388       info.image_scale_factor,
1389       gfx::Size(info.custom_image.width(), info.custom_image.height()),
1390       info.hotspot);
1391   } else {
1392     Emit("cursor-changed", CursorTypeToString(info));
1393   }
1394 }
1395
1396 void WebContents::SetSize(const SetSizeParams& params) {
1397   if (guest_delegate_)
1398     guest_delegate_->SetSize(params);
1399 }
1400
1401 bool WebContents::IsGuest() const {
1402   return type_ == WEB_VIEW;
1403 }
1404
1405 bool WebContents::IsOffScreen() const {
1406   return type_ == OFF_SCREEN;
1407 }
1408
1409 void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) {
1410   mate::Handle<NativeImage> image =
1411       NativeImage::Create(isolate(), gfx::Image::CreateFrom1xBitmap(bitmap));
1412   Emit("paint", dirty_rect, image);
1413 }
1414
1415 void WebContents::StartPainting() {
1416   if (!IsOffScreen())
1417     return;
1418
1419   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1420       web_contents()->GetRenderWidgetHostView());
1421   if (osr_rwhv)
1422     osr_rwhv->SetPainting(true);
1423 }
1424
1425 void WebContents::StopPainting() {
1426   if (!IsOffScreen())
1427     return;
1428
1429   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1430       web_contents()->GetRenderWidgetHostView());
1431   if (osr_rwhv)
1432     osr_rwhv->SetPainting(false);
1433 }
1434
1435 bool WebContents::IsPainting() const {
1436   if (!IsOffScreen())
1437     return false;
1438
1439   const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1440       web_contents()->GetRenderWidgetHostView());
1441   return osr_rwhv && osr_rwhv->IsPainting();
1442 }
1443
1444 void WebContents::SetFrameRate(int frame_rate) {
1445   if (!IsOffScreen())
1446     return;
1447
1448   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1449       web_contents()->GetRenderWidgetHostView());
1450   if (osr_rwhv)
1451     osr_rwhv->SetFrameRate(frame_rate);
1452 }
1453
1454 int WebContents::GetFrameRate() const {
1455   if (!IsOffScreen())
1456     return 0;
1457
1458   const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1459       web_contents()->GetRenderWidgetHostView());
1460   return osr_rwhv ? osr_rwhv->GetFrameRate() : 0;
1461 }
1462
1463 void WebContents::Invalidate() {
1464   if (!IsOffScreen())
1465     return;
1466
1467   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1468       web_contents()->GetRenderWidgetHostView());
1469   if (osr_rwhv)
1470     osr_rwhv->Invalidate();
1471 }
1472
1473 v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
1474   WebContentsPreferences* web_preferences =
1475       WebContentsPreferences::FromWebContents(web_contents());
1476   return mate::ConvertToV8(isolate, *web_preferences->web_preferences());
1477 }
1478
1479 v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() {
1480   if (owner_window())
1481     return Window::From(isolate(), owner_window());
1482   else
1483     return v8::Null(isolate());
1484 }
1485
1486 int32_t WebContents::ID() const {
1487   return weak_map_id();
1488 }
1489
1490 v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
1491   return v8::Local<v8::Value>::New(isolate, session_);
1492 }
1493
1494 content::WebContents* WebContents::HostWebContents() {
1495   if (!embedder_)
1496     return nullptr;
1497   return embedder_->web_contents();
1498 }
1499
1500 void WebContents::SetEmbedder(const WebContents* embedder) {
1501   if (embedder) {
1502     NativeWindow* owner_window = nullptr;
1503     auto relay = NativeWindowRelay::FromWebContents(embedder->web_contents());
1504     if (relay) {
1505       owner_window = relay->window.get();
1506     }
1507     if (owner_window)
1508       SetOwnerWindow(owner_window);
1509
1510     content::RenderWidgetHostView* rwhv =
1511         web_contents()->GetRenderWidgetHostView();
1512     if (rwhv) {
1513       rwhv->Hide();
1514       rwhv->Show();
1515     }
1516   }
1517 }
1518
1519 v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
1520   if (devtools_web_contents_.IsEmpty())
1521     return v8::Null(isolate);
1522   else
1523     return v8::Local<v8::Value>::New(isolate, devtools_web_contents_);
1524 }
1525
1526 v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
1527   if (debugger_.IsEmpty()) {
1528     auto handle = atom::api::Debugger::Create(isolate, web_contents());
1529     debugger_.Reset(isolate, handle.ToV8());
1530   }
1531   return v8::Local<v8::Value>::New(isolate, debugger_);
1532 }
1533
1534 // static
1535 void WebContents::BuildPrototype(v8::Isolate* isolate,
1536                                  v8::Local<v8::FunctionTemplate> prototype) {
1537   prototype->SetClassName(mate::StringToV8(isolate, "WebContents"));
1538   mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
1539       .MakeDestroyable()
1540       .SetMethod("getId", &WebContents::GetID)
1541       .SetMethod("getProcessId", &WebContents::GetProcessID)
1542       .SetMethod("equal", &WebContents::Equal)
1543       .SetMethod("_loadURL", &WebContents::LoadURL)
1544       .SetMethod("downloadURL", &WebContents::DownloadURL)
1545       .SetMethod("_getURL", &WebContents::GetURL)
1546       .SetMethod("getTitle", &WebContents::GetTitle)
1547       .SetMethod("isLoading", &WebContents::IsLoading)
1548       .SetMethod("isLoadingMainFrame", &WebContents::IsLoadingMainFrame)
1549       .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
1550       .SetMethod("_stop", &WebContents::Stop)
1551       .SetMethod("_goBack", &WebContents::GoBack)
1552       .SetMethod("_goForward", &WebContents::GoForward)
1553       .SetMethod("_goToOffset", &WebContents::GoToOffset)
1554       .SetMethod("isCrashed", &WebContents::IsCrashed)
1555       .SetMethod("setUserAgent", &WebContents::SetUserAgent)
1556       .SetMethod("getUserAgent", &WebContents::GetUserAgent)
1557       .SetMethod("savePage", &WebContents::SavePage)
1558       .SetMethod("openDevTools", &WebContents::OpenDevTools)
1559       .SetMethod("closeDevTools", &WebContents::CloseDevTools)
1560       .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
1561       .SetMethod("isDevToolsFocused", &WebContents::IsDevToolsFocused)
1562       .SetMethod("enableDeviceEmulation",
1563                  &WebContents::EnableDeviceEmulation)
1564       .SetMethod("disableDeviceEmulation",
1565                  &WebContents::DisableDeviceEmulation)
1566       .SetMethod("toggleDevTools", &WebContents::ToggleDevTools)
1567       .SetMethod("inspectElement", &WebContents::InspectElement)
1568       .SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
1569       .SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
1570       .SetMethod("undo", &WebContents::Undo)
1571       .SetMethod("redo", &WebContents::Redo)
1572       .SetMethod("cut", &WebContents::Cut)
1573       .SetMethod("copy", &WebContents::Copy)
1574       .SetMethod("paste", &WebContents::Paste)
1575       .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle)
1576       .SetMethod("delete", &WebContents::Delete)
1577       .SetMethod("selectAll", &WebContents::SelectAll)
1578       .SetMethod("unselect", &WebContents::Unselect)
1579       .SetMethod("replace", &WebContents::Replace)
1580       .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
1581       .SetMethod("findInPage", &WebContents::FindInPage)
1582       .SetMethod("stopFindInPage", &WebContents::StopFindInPage)
1583       .SetMethod("focus", &WebContents::Focus)
1584       .SetMethod("isFocused", &WebContents::IsFocused)
1585       .SetMethod("tabTraverse", &WebContents::TabTraverse)
1586       .SetMethod("_send", &WebContents::SendIPCMessage)
1587       .SetMethod("sendInputEvent", &WebContents::SendInputEvent)
1588       .SetMethod("beginFrameSubscription",
1589                  &WebContents::BeginFrameSubscription)
1590       .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
1591       .SetMethod("startDrag", &WebContents::StartDrag)
1592       .SetMethod("setSize", &WebContents::SetSize)
1593       .SetMethod("isGuest", &WebContents::IsGuest)
1594       .SetMethod("isOffscreen", &WebContents::IsOffScreen)
1595       .SetMethod("startPainting", &WebContents::StartPainting)
1596       .SetMethod("stopPainting", &WebContents::StopPainting)
1597       .SetMethod("isPainting", &WebContents::IsPainting)
1598       .SetMethod("setFrameRate", &WebContents::SetFrameRate)
1599       .SetMethod("getFrameRate", &WebContents::GetFrameRate)
1600       .SetMethod("invalidate", &WebContents::Invalidate)
1601       .SetMethod("getType", &WebContents::GetType)
1602       .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
1603       .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
1604       .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
1605       .SetMethod("unregisterServiceWorker",
1606                  &WebContents::UnregisterServiceWorker)
1607       .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
1608       .SetMethod("print", &WebContents::Print)
1609       .SetMethod("_printToPDF", &WebContents::PrintToPDF)
1610       .SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
1611       .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
1612       .SetMethod("showDefinitionForSelection",
1613                  &WebContents::ShowDefinitionForSelection)
1614       .SetMethod("copyImageAt", &WebContents::CopyImageAt)
1615       .SetMethod("capturePage", &WebContents::CapturePage)
1616       .SetMethod("setEmbedder", &WebContents::SetEmbedder)
1617       .SetProperty("id", &WebContents::ID)
1618       .SetProperty("session", &WebContents::Session)
1619       .SetProperty("hostWebContents", &WebContents::HostWebContents)
1620       .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
1621       .SetProperty("debugger", &WebContents::Debugger);
1622 }
1623
1624 AtomBrowserContext* WebContents::GetBrowserContext() const {
1625   return static_cast<AtomBrowserContext*>(web_contents()->GetBrowserContext());
1626 }
1627
1628 void WebContents::OnRendererMessage(const base::string16& channel,
1629                                     const base::ListValue& args) {
1630   // webContents.emit(channel, new Event(), args...);
1631   Emit(base::UTF16ToUTF8(channel), args);
1632 }
1633
1634 void WebContents::OnRendererMessageSync(const base::string16& channel,
1635                                         const base::ListValue& args,
1636                                         IPC::Message* message) {
1637   // webContents.emit(channel, new Event(sender, message), args...);
1638   EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
1639 }
1640
1641 // static
1642 mate::Handle<WebContents> WebContents::CreateFrom(
1643     v8::Isolate* isolate, content::WebContents* web_contents) {
1644   // We have an existing WebContents object in JS.
1645   auto existing = TrackableObject::FromWrappedClass(isolate, web_contents);
1646   if (existing)
1647     return mate::CreateHandle(isolate, static_cast<WebContents*>(existing));
1648
1649   // Otherwise create a new WebContents wrapper object.
1650   return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
1651         REMOTE));
1652 }
1653
1654 mate::Handle<WebContents> WebContents::CreateFrom(
1655     v8::Isolate* isolate, content::WebContents* web_contents, Type type) {
1656   // Otherwise create a new WebContents wrapper object.
1657   return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
1658         type));
1659 }
1660
1661 // static
1662 mate::Handle<WebContents> WebContents::Create(
1663     v8::Isolate* isolate, const mate::Dictionary& options) {
1664   return mate::CreateHandle(isolate, new WebContents(isolate, options));
1665 }
1666
1667 }  // namespace api
1668
1669 }  // namespace atom
1670
1671 namespace {
1672
1673 using atom::api::WebContents;
1674
1675 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
1676                 v8::Local<v8::Context> context, void* priv) {
1677   v8::Isolate* isolate = context->GetIsolate();
1678   mate::Dictionary dict(isolate, exports);
1679   dict.Set("WebContents", WebContents::GetConstructor(isolate)->GetFunction());
1680   dict.SetMethod("create", &WebContents::Create);
1681   dict.SetMethod("fromId", &mate::TrackableObject<WebContents>::FromWeakMapID);
1682   dict.SetMethod("getAllWebContents",
1683                  &mate::TrackableObject<WebContents>::GetAll);
1684 }
1685
1686 }  // namespace
1687
1688 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_web_contents, Initialize)