Update api::WebContents
[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::DidAddMessageToConsole(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<std::string>& 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_process_id,
405                                      int opener_render_frame_id,
406                                      const std::string& frame_name,
407                                      const GURL& target_url,
408                                      content::WebContents* new_contents) {
409   v8::Locker locker(isolate());
410   v8::HandleScope handle_scope(isolate());
411   auto api_web_contents = CreateFrom(isolate(), new_contents, BROWSER_WINDOW);
412   Emit("-web-contents-created", api_web_contents, target_url, frame_name);
413 }
414
415 void WebContents::AddNewContents(content::WebContents* source,
416                                  content::WebContents* new_contents,
417                                  WindowOpenDisposition disposition,
418                                  const gfx::Rect& initial_rect,
419                                  bool user_gesture,
420                                  bool* was_blocked) {
421   v8::Locker locker(isolate());
422   v8::HandleScope handle_scope(isolate());
423   auto api_web_contents = CreateFrom(isolate(), new_contents);
424   if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
425       initial_rect.x(), initial_rect.y(), initial_rect.width(),
426       initial_rect.height())) {
427     api_web_contents->DestroyWebContents();
428   }
429 }
430
431 content::WebContents* WebContents::OpenURLFromTab(
432     content::WebContents* source,
433     const content::OpenURLParams& params) {
434   if (params.disposition != CURRENT_TAB) {
435     if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
436       Emit("-new-window", params.url, "", params.disposition);
437     else
438       Emit("new-window", params.url, "", params.disposition);
439     return nullptr;
440   }
441
442   // Give user a chance to cancel navigation.
443   if (Emit("will-navigate", params.url))
444     return nullptr;
445
446   // Don't load the URL if the web contents was marked as destroyed from a
447   // will-navigate event listener
448   if (IsDestroyed())
449     return nullptr;
450
451   return CommonWebContentsDelegate::OpenURLFromTab(source, params);
452 }
453
454 void WebContents::BeforeUnloadFired(content::WebContents* tab,
455                                     bool proceed,
456                                     bool* proceed_to_fire_unload) {
457   if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
458     *proceed_to_fire_unload = proceed;
459   else
460     *proceed_to_fire_unload = true;
461 }
462
463 void WebContents::MoveContents(content::WebContents* source,
464                                const gfx::Rect& pos) {
465   Emit("move", pos);
466 }
467
468 void WebContents::CloseContents(content::WebContents* source) {
469   Emit("close");
470
471   if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
472     owner_window()->CloseContents(source);
473 }
474
475 void WebContents::ActivateContents(content::WebContents* source) {
476   Emit("activate");
477 }
478
479 void WebContents::UpdateTargetURL(content::WebContents* source,
480                                   const GURL& url) {
481   Emit("update-target-url", url);
482 }
483
484 bool WebContents::IsPopupOrPanel(const content::WebContents* source) const {
485   return type_ == BROWSER_WINDOW;
486 }
487
488 void WebContents::HandleKeyboardEvent(
489     content::WebContents* source,
490     const content::NativeWebKeyboardEvent& event) {
491   if (type_ == WEB_VIEW && embedder_) {
492     // Send the unhandled keyboard events back to the embedder.
493     embedder_->HandleKeyboardEvent(source, event);
494   } else {
495     // Go to the default keyboard handling.
496     CommonWebContentsDelegate::HandleKeyboardEvent(source, event);
497   }
498 }
499
500 bool WebContents::PreHandleKeyboardEvent(
501     content::WebContents* source,
502     const content::NativeWebKeyboardEvent& event,
503     bool* is_keyboard_shortcut) {
504   if (event.type == blink::WebInputEvent::Type::RawKeyDown
505       || event.type == blink::WebInputEvent::Type::KeyUp)
506     return Emit("before-input-event", event);
507   else
508     return false;
509 }
510
511 void WebContents::EnterFullscreenModeForTab(content::WebContents* source,
512                                             const GURL& origin) {
513   auto permission_helper =
514       WebContentsPermissionHelper::FromWebContents(source);
515   auto callback = base::Bind(&WebContents::OnEnterFullscreenModeForTab,
516                              base::Unretained(this), source, origin);
517   permission_helper->RequestFullscreenPermission(callback);
518 }
519
520 void WebContents::OnEnterFullscreenModeForTab(content::WebContents* source,
521                                               const GURL& origin,
522                                               bool allowed) {
523   if (!allowed)
524     return;
525   CommonWebContentsDelegate::EnterFullscreenModeForTab(source, origin);
526   Emit("enter-html-full-screen");
527 }
528
529 void WebContents::ExitFullscreenModeForTab(content::WebContents* source) {
530   CommonWebContentsDelegate::ExitFullscreenModeForTab(source);
531   Emit("leave-html-full-screen");
532 }
533
534 void WebContents::RendererUnresponsive(
535     content::WebContents* source,
536     const content::WebContentsUnresponsiveState& unresponsive_state) {
537   Emit("unresponsive");
538   if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
539     owner_window()->RendererUnresponsive(source);
540 }
541
542 void WebContents::RendererResponsive(content::WebContents* source) {
543   Emit("responsive");
544   if ((type_ == BROWSER_WINDOW || type_ == OFF_SCREEN) && owner_window())
545     owner_window()->RendererResponsive(source);
546 }
547
548 bool WebContents::HandleContextMenu(const content::ContextMenuParams& params) {
549   if (params.custom_context.is_pepper_menu) {
550     Emit("pepper-context-menu", std::make_pair(params, web_contents()));
551     web_contents()->NotifyContextMenuClosed(params.custom_context);
552   } else {
553     Emit("context-menu", std::make_pair(params, web_contents()));
554   }
555
556   return true;
557 }
558
559 bool WebContents::OnGoToEntryOffset(int offset) {
560   GoToOffset(offset);
561   return false;
562 }
563
564 void WebContents::FindReply(content::WebContents* web_contents,
565                             int request_id,
566                             int number_of_matches,
567                             const gfx::Rect& selection_rect,
568                             int active_match_ordinal,
569                             bool final_update) {
570   if (!final_update)
571     return;
572
573   v8::Locker locker(isolate());
574   v8::HandleScope handle_scope(isolate());
575   mate::Dictionary result = mate::Dictionary::CreateEmpty(isolate());
576   result.Set("requestId", request_id);
577   result.Set("matches", number_of_matches);
578   result.Set("selectionArea", selection_rect);
579   result.Set("activeMatchOrdinal", active_match_ordinal);
580   result.Set("finalUpdate", final_update);  // Deprecate after 2.0
581   Emit("found-in-page", result);
582 }
583
584 bool WebContents::CheckMediaAccessPermission(
585     content::WebContents* web_contents,
586     const GURL& security_origin,
587     content::MediaStreamType type) {
588   return true;
589 }
590
591 void WebContents::RequestMediaAccessPermission(
592     content::WebContents* web_contents,
593     const content::MediaStreamRequest& request,
594     const content::MediaResponseCallback& callback) {
595   auto permission_helper =
596       WebContentsPermissionHelper::FromWebContents(web_contents);
597   permission_helper->RequestMediaAccessPermission(request, callback);
598 }
599
600 void WebContents::RequestToLockMouse(
601     content::WebContents* web_contents,
602     bool user_gesture,
603     bool last_unlocked_by_target) {
604   auto permission_helper =
605       WebContentsPermissionHelper::FromWebContents(web_contents);
606   permission_helper->RequestPointerLockPermission(user_gesture);
607 }
608
609 std::unique_ptr<content::BluetoothChooser> WebContents::RunBluetoothChooser(
610     content::RenderFrameHost* frame,
611     const content::BluetoothChooser::EventHandler& event_handler) {
612   std::unique_ptr<BluetoothChooser> bluetooth_chooser(
613       new BluetoothChooser(this, event_handler));
614   return std::move(bluetooth_chooser);
615 }
616
617 void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
618   // Do nothing, we override this method just to avoid compilation error since
619   // there are two virtual functions named BeforeUnloadFired.
620 }
621
622 void WebContents::RenderViewCreated(content::RenderViewHost* render_view_host) {
623   const auto impl = content::RenderWidgetHostImpl::FromID(
624       render_view_host->GetProcess()->GetID(),
625       render_view_host->GetRoutingID());
626   if (impl)
627     impl->disable_hidden_ = !background_throttling_;
628 }
629
630 void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
631   Emit("render-view-deleted", render_view_host->GetProcess()->GetID());
632 }
633
634 void WebContents::RenderProcessGone(base::TerminationStatus status) {
635   Emit("crashed", status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED);
636 }
637
638 void WebContents::PluginCrashed(const base::FilePath& plugin_path,
639                                 base::ProcessId plugin_pid) {
640   content::WebPluginInfo info;
641   auto plugin_service = content::PluginService::GetInstance();
642   plugin_service->GetPluginInfoByPath(plugin_path, &info);
643   Emit("plugin-crashed", info.name, info.version);
644 }
645
646 void WebContents::MediaStartedPlaying(const MediaPlayerInfo& video_type,
647                                       const MediaPlayerId& id) {
648   Emit("media-started-playing");
649 }
650
651 void WebContents::MediaStoppedPlaying(const MediaPlayerInfo& video_type,
652                                       const MediaPlayerId& id) {
653   Emit("media-paused");
654 }
655
656 void WebContents::DidChangeThemeColor(SkColor theme_color) {
657   Emit("did-change-theme-color", atom::ToRGBHex(theme_color));
658 }
659
660 void WebContents::DocumentLoadedInFrame(
661     content::RenderFrameHost* render_frame_host) {
662   if (!render_frame_host->GetParent())
663     Emit("dom-ready");
664 }
665
666 void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
667                                 const GURL& validated_url) {
668   bool is_main_frame = !render_frame_host->GetParent();
669   Emit("did-frame-finish-load", is_main_frame);
670
671   if (is_main_frame)
672     Emit("did-finish-load");
673 }
674
675 void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
676                               const GURL& url,
677                               int error_code,
678                               const base::string16& error_description,
679                               bool was_ignored_by_handler) {
680   bool is_main_frame = !render_frame_host->GetParent();
681   Emit("did-fail-load", error_code, error_description, url, is_main_frame);
682 }
683
684 void WebContents::DidStartLoading() {
685   Emit("did-start-loading");
686 }
687
688 void WebContents::DidStopLoading() {
689   Emit("did-stop-loading");
690 }
691
692 void WebContents::DidGetResourceResponseStart(
693     const content::ResourceRequestDetails& details) {
694   Emit("did-get-response-details",
695        details.socket_address.IsEmpty(),
696        details.url,
697        details.original_url,
698        details.http_response_code,
699        details.method,
700        details.referrer,
701        details.headers.get(),
702        ResourceTypeToString(details.resource_type));
703 }
704
705 void WebContents::DidGetRedirectForResourceRequest(
706     const content::ResourceRedirectDetails& details) {
707   Emit("did-get-redirect-request",
708        details.url,
709        details.new_url,
710        (details.resource_type == content::RESOURCE_TYPE_MAIN_FRAME),
711        details.http_response_code,
712        details.method,
713        details.referrer,
714        details.headers.get());
715 }
716
717 void WebContents::DidFinishNavigation(
718     content::NavigationHandle* navigation_handle) {
719   bool is_main_frame = navigation_handle->IsInMainFrame();
720   if (navigation_handle->HasCommitted() && !navigation_handle->IsErrorPage()) {
721     auto url = navigation_handle->GetURL();
722     bool is_in_page = navigation_handle->IsSamePage();
723     if (is_main_frame && !is_in_page) {
724       Emit("did-navigate", url);
725     } else if (is_in_page) {
726       Emit("did-navigate-in-page", url, is_main_frame);
727     }
728   } else {
729     auto url = navigation_handle->GetURL();
730     int code = navigation_handle->GetNetErrorCode();
731     auto description = net::ErrorToShortString(code);
732     Emit("did-fail-provisional-load", code, description, url, is_main_frame);
733
734     // Do not emit "did-fail-load" for canceled requests.
735     if (code != net::ERR_ABORTED)
736       Emit("did-fail-load", code, description, url, is_main_frame);
737   }
738 }
739
740 void WebContents::TitleWasSet(content::NavigationEntry* entry,
741                               bool explicit_set) {
742   if (entry)
743     Emit("-page-title-updated", entry->GetTitle(), explicit_set);
744   else
745     Emit("-page-title-updated", "", explicit_set);
746 }
747
748 void WebContents::DidUpdateFaviconURL(
749     const std::vector<content::FaviconURL>& urls) {
750   std::set<GURL> unique_urls;
751   for (const auto& iter : urls) {
752     if (iter.icon_type != content::FaviconURL::FAVICON)
753       continue;
754     const GURL& url = iter.icon_url;
755     if (url.is_valid())
756       unique_urls.insert(url);
757   }
758   Emit("page-favicon-updated", unique_urls);
759 }
760
761 void WebContents::DevToolsReloadPage() {
762   Emit("devtools-reload-page");
763 }
764
765 void WebContents::DevToolsFocused() {
766   Emit("devtools-focused");
767 }
768
769 void WebContents::DevToolsOpened() {
770   v8::Locker locker(isolate());
771   v8::HandleScope handle_scope(isolate());
772   auto handle = WebContents::CreateFrom(
773       isolate(), managed_web_contents()->GetDevToolsWebContents());
774   devtools_web_contents_.Reset(isolate(), handle.ToV8());
775
776   // Set inspected tabID.
777   base::FundamentalValue tab_id(ID());
778   managed_web_contents()->CallClientFunction(
779       "DevToolsAPI.setInspectedTabId", &tab_id, nullptr, nullptr);
780
781   // Inherit owner window in devtools.
782   if (owner_window())
783     handle->SetOwnerWindow(managed_web_contents()->GetDevToolsWebContents(),
784                            owner_window());
785
786   Emit("devtools-opened");
787 }
788
789 void WebContents::DevToolsClosed() {
790   v8::Locker locker(isolate());
791   v8::HandleScope handle_scope(isolate());
792   devtools_web_contents_.Reset();
793
794   Emit("devtools-closed");
795 }
796
797 bool WebContents::OnMessageReceived(const IPC::Message& message) {
798   bool handled = true;
799   IPC_BEGIN_MESSAGE_MAP(WebContents, message)
800     IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage)
801     IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync,
802                                     OnRendererMessageSync)
803     IPC_MESSAGE_HANDLER_CODE(ViewHostMsg_SetCursor, OnCursorChange,
804       handled = false)
805     IPC_MESSAGE_UNHANDLED(handled = false)
806   IPC_END_MESSAGE_MAP()
807
808   return handled;
809 }
810
811 // There are three ways of destroying a webContents:
812 // 1. call webContents.destroy();
813 // 2. garbage collection;
814 // 3. user closes the window of webContents;
815 // For webview only #1 will happen, for BrowserWindow both #1 and #3 may
816 // happen. The #2 should never happen for webContents, because webview is
817 // managed by GuestViewManager, and BrowserWindow's webContents is managed
818 // by api::Window.
819 // For #1, the destructor will do the cleanup work and we only need to make
820 // sure "destroyed" event is emitted. For #3, the content::WebContents will
821 // be destroyed on close, and WebContentsDestroyed would be called for it, so
822 // we need to make sure the api::WebContents is also deleted.
823 void WebContents::WebContentsDestroyed() {
824   // This event is only for internal use, which is emitted when WebContents is
825   // being destroyed.
826   Emit("will-destroy");
827
828   // Cleanup relationships with other parts.
829   RemoveFromWeakMap();
830
831   // We can not call Destroy here because we need to call Emit first, but we
832   // also do not want any method to be used, so just mark as destroyed here.
833   MarkDestroyed();
834
835   Emit("destroyed");
836
837   // Destroy the native class in next tick.
838   base::ThreadTaskRunnerHandle::Get()->PostTask(
839       FROM_HERE, GetDestroyClosure());
840 }
841
842 void WebContents::NavigationEntryCommitted(
843     const content::LoadCommittedDetails& details) {
844   Emit("navigation-entry-commited", details.entry->GetURL(),
845        details.is_in_page, details.did_replace_entry);
846 }
847
848 int64_t WebContents::GetID() const {
849   int64_t process_id = web_contents()->GetRenderProcessHost()->GetID();
850   int64_t routing_id = web_contents()->GetRoutingID();
851   int64_t rv = (process_id << 32) + routing_id;
852   return rv;
853 }
854
855 int WebContents::GetProcessID() const {
856   return web_contents()->GetRenderProcessHost()->GetID();
857 }
858
859 WebContents::Type WebContents::GetType() const {
860   return type_;
861 }
862
863 bool WebContents::Equal(const WebContents* web_contents) const {
864   return GetID() == web_contents->GetID();
865 }
866
867 void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
868   if (!url.is_valid() || url.spec().size() > url::kMaxURLChars) {
869     Emit("did-fail-load",
870          static_cast<int>(net::ERR_INVALID_URL),
871          net::ErrorToShortString(net::ERR_INVALID_URL),
872          url.possibly_invalid_spec(),
873          true);
874     return;
875   }
876
877   content::NavigationController::LoadURLParams params(url);
878
879   GURL http_referrer;
880   if (options.Get("httpReferrer", &http_referrer))
881     params.referrer = content::Referrer(http_referrer.GetAsReferrer(),
882                                         blink::WebReferrerPolicyDefault);
883
884   std::string user_agent;
885   if (options.Get("userAgent", &user_agent))
886     web_contents()->SetUserAgentOverride(user_agent);
887
888   std::string extra_headers;
889   if (options.Get("extraHeaders", &extra_headers))
890     params.extra_headers = extra_headers;
891
892   scoped_refptr<content::ResourceRequestBodyImpl> body;
893   if (options.Get("postData", &body)) {
894     params.post_data = body;
895     params.load_type = content::NavigationController::LOAD_TYPE_HTTP_POST;
896   }
897
898   params.transition_type = ui::PAGE_TRANSITION_TYPED;
899   params.should_clear_history_list = true;
900   params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
901   web_contents()->GetController().LoadURLWithParams(params);
902
903   // Set the background color of RenderWidgetHostView.
904   // We have to call it right after LoadURL because the RenderViewHost is only
905   // created after loading a page.
906   const auto view = web_contents()->GetRenderWidgetHostView();
907   if (view) {
908     WebContentsPreferences* web_preferences =
909         WebContentsPreferences::FromWebContents(web_contents());
910     std::string color_name;
911     if (web_preferences->web_preferences()->GetString(options::kBackgroundColor,
912                                                       &color_name)) {
913       view->SetBackgroundColor(ParseHexColor(color_name));
914     } else {
915       view->SetBackgroundColor(SK_ColorTRANSPARENT);
916     }
917   }
918 }
919
920 void WebContents::DownloadURL(const GURL& url) {
921   auto browser_context = web_contents()->GetBrowserContext();
922   auto download_manager =
923     content::BrowserContext::GetDownloadManager(browser_context);
924
925   download_manager->DownloadUrl(
926       content::DownloadUrlParameters::CreateForWebContentsMainFrame(
927           web_contents(), url));
928 }
929
930 GURL WebContents::GetURL() const {
931   return web_contents()->GetURL();
932 }
933
934 base::string16 WebContents::GetTitle() const {
935   return web_contents()->GetTitle();
936 }
937
938 bool WebContents::IsLoading() const {
939   return web_contents()->IsLoading();
940 }
941
942 bool WebContents::IsLoadingMainFrame() const {
943   // Comparing site instances works because Electron always creates a new site
944   // instance when navigating, regardless of origin. See AtomBrowserClient.
945   return (web_contents()->GetLastCommittedURL().is_empty() ||
946           web_contents()->GetSiteInstance() !=
947           web_contents()->GetPendingSiteInstance()) && IsLoading();
948 }
949
950 bool WebContents::IsWaitingForResponse() const {
951   return web_contents()->IsWaitingForResponse();
952 }
953
954 void WebContents::Stop() {
955   web_contents()->Stop();
956 }
957
958 void WebContents::GoBack() {
959   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
960   web_contents()->GetController().GoBack();
961 }
962
963 void WebContents::GoForward() {
964   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
965   web_contents()->GetController().GoForward();
966 }
967
968 void WebContents::GoToOffset(int offset) {
969   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
970   web_contents()->GetController().GoToOffset(offset);
971 }
972
973 bool WebContents::IsCrashed() const {
974   return web_contents()->IsCrashed();
975 }
976
977 void WebContents::SetUserAgent(const std::string& user_agent,
978                                mate::Arguments* args) {
979   web_contents()->SetUserAgentOverride(user_agent);
980 }
981
982 std::string WebContents::GetUserAgent() {
983   return web_contents()->GetUserAgentOverride();
984 }
985
986 bool WebContents::SavePage(const base::FilePath& full_file_path,
987                            const content::SavePageType& save_type,
988                            const SavePageHandler::SavePageCallback& callback) {
989   auto handler = new SavePageHandler(web_contents(), callback);
990   return handler->Handle(full_file_path, save_type);
991 }
992
993 void WebContents::OpenDevTools(mate::Arguments* args) {
994   if (type_ == REMOTE)
995     return;
996
997   if (!enable_devtools_)
998     return;
999
1000   std::string state;
1001   if (type_ == WEB_VIEW || !owner_window()) {
1002     state = "detach";
1003   } else if (args && args->Length() == 1) {
1004     bool detach = false;
1005     mate::Dictionary options;
1006     if (args->GetNext(&options)) {
1007       options.Get("mode", &state);
1008
1009       // TODO(kevinsawicki) Remove in 2.0
1010       options.Get("detach", &detach);
1011       if (state.empty() && detach)
1012         state = "detach";
1013     }
1014   }
1015   managed_web_contents()->SetDockState(state);
1016   managed_web_contents()->ShowDevTools();
1017 }
1018
1019 void WebContents::CloseDevTools() {
1020   if (type_ == REMOTE)
1021     return;
1022
1023   managed_web_contents()->CloseDevTools();
1024 }
1025
1026 bool WebContents::IsDevToolsOpened() {
1027   if (type_ == REMOTE)
1028     return false;
1029
1030   return managed_web_contents()->IsDevToolsViewShowing();
1031 }
1032
1033 bool WebContents::IsDevToolsFocused() {
1034   if (type_ == REMOTE)
1035     return false;
1036
1037   return managed_web_contents()->GetView()->IsDevToolsViewFocused();
1038 }
1039
1040 void WebContents::EnableDeviceEmulation(
1041     const blink::WebDeviceEmulationParams& params) {
1042   if (type_ == REMOTE)
1043     return;
1044
1045   Send(new ViewMsg_EnableDeviceEmulation(routing_id(), params));
1046 }
1047
1048 void WebContents::DisableDeviceEmulation() {
1049   if (type_ == REMOTE)
1050     return;
1051
1052   Send(new ViewMsg_DisableDeviceEmulation(routing_id()));
1053 }
1054
1055 void WebContents::ToggleDevTools() {
1056   if (IsDevToolsOpened())
1057     CloseDevTools();
1058   else
1059     OpenDevTools(nullptr);
1060 }
1061
1062 void WebContents::InspectElement(int x, int y) {
1063   if (type_ == REMOTE)
1064     return;
1065
1066   if (!enable_devtools_)
1067     return;
1068
1069   if (!managed_web_contents()->GetDevToolsWebContents())
1070     OpenDevTools(nullptr);
1071   managed_web_contents()->InspectElement(x, y);
1072 }
1073
1074 void WebContents::InspectServiceWorker() {
1075   if (type_ == REMOTE)
1076     return;
1077
1078   if (!enable_devtools_)
1079     return;
1080
1081   for (const auto& agent_host : content::DevToolsAgentHost::GetOrCreateAll()) {
1082     if (agent_host->GetType() ==
1083         content::DevToolsAgentHost::TYPE_SERVICE_WORKER) {
1084       OpenDevTools(nullptr);
1085       managed_web_contents()->AttachTo(agent_host);
1086       break;
1087     }
1088   }
1089 }
1090
1091 void WebContents::HasServiceWorker(
1092     const base::Callback<void(bool)>& callback) {
1093   auto context = GetServiceWorkerContext(web_contents());
1094   if (!context)
1095     return;
1096
1097   context->CheckHasServiceWorker(web_contents()->GetLastCommittedURL(),
1098                                  GURL::EmptyGURL(),
1099                                  callback);
1100 }
1101
1102 void WebContents::UnregisterServiceWorker(
1103     const base::Callback<void(bool)>& callback) {
1104   auto context = GetServiceWorkerContext(web_contents());
1105   if (!context)
1106     return;
1107
1108   context->UnregisterServiceWorker(web_contents()->GetLastCommittedURL(),
1109                                    callback);
1110 }
1111
1112 void WebContents::SetAudioMuted(bool muted) {
1113   web_contents()->SetAudioMuted(muted);
1114 }
1115
1116 bool WebContents::IsAudioMuted() {
1117   return web_contents()->IsAudioMuted();
1118 }
1119
1120 void WebContents::Print(mate::Arguments* args) {
1121   PrintSettings settings = { false, false };
1122   if (args->Length() == 1 && !args->GetNext(&settings)) {
1123     args->ThrowError();
1124     return;
1125   }
1126
1127   printing::PrintViewManagerBasic::FromWebContents(web_contents())->
1128       PrintNow(settings.silent, settings.print_background);
1129 }
1130
1131 void WebContents::PrintToPDF(const base::DictionaryValue& setting,
1132                              const PrintToPDFCallback& callback) {
1133   printing::PrintPreviewMessageHandler::FromWebContents(web_contents())->
1134       PrintToPDF(setting, callback);
1135 }
1136
1137 void WebContents::AddWorkSpace(mate::Arguments* args,
1138                                const base::FilePath& path) {
1139   if (path.empty()) {
1140     args->ThrowError("path cannot be empty");
1141     return;
1142   }
1143   DevToolsAddFileSystem(path);
1144 }
1145
1146 void WebContents::RemoveWorkSpace(mate::Arguments* args,
1147                                   const base::FilePath& path) {
1148   if (path.empty()) {
1149     args->ThrowError("path cannot be empty");
1150     return;
1151   }
1152   DevToolsRemoveFileSystem(path);
1153 }
1154
1155 void WebContents::Undo() {
1156   web_contents()->Undo();
1157 }
1158
1159 void WebContents::Redo() {
1160   web_contents()->Redo();
1161 }
1162
1163 void WebContents::Cut() {
1164   web_contents()->Cut();
1165 }
1166
1167 void WebContents::Copy() {
1168   web_contents()->Copy();
1169 }
1170
1171 void WebContents::Paste() {
1172   web_contents()->Paste();
1173 }
1174
1175 void WebContents::PasteAndMatchStyle() {
1176   web_contents()->PasteAndMatchStyle();
1177 }
1178
1179 void WebContents::Delete() {
1180   web_contents()->Delete();
1181 }
1182
1183 void WebContents::SelectAll() {
1184   web_contents()->SelectAll();
1185 }
1186
1187 void WebContents::Unselect() {
1188   web_contents()->Unselect();
1189 }
1190
1191 void WebContents::Replace(const base::string16& word) {
1192   web_contents()->Replace(word);
1193 }
1194
1195 void WebContents::ReplaceMisspelling(const base::string16& word) {
1196   web_contents()->ReplaceMisspelling(word);
1197 }
1198
1199 uint32_t WebContents::FindInPage(mate::Arguments* args) {
1200   uint32_t request_id = GetNextRequestId();
1201   base::string16 search_text;
1202   blink::WebFindOptions options;
1203   if (!args->GetNext(&search_text) || search_text.empty()) {
1204     args->ThrowError("Must provide a non-empty search content");
1205     return 0;
1206   }
1207
1208   args->GetNext(&options);
1209
1210   web_contents()->Find(request_id, search_text, options);
1211   return request_id;
1212 }
1213
1214 void WebContents::StopFindInPage(content::StopFindAction action) {
1215   web_contents()->StopFinding(action);
1216 }
1217
1218 void WebContents::ShowDefinitionForSelection() {
1219 #if defined(OS_MACOSX)
1220   const auto view = web_contents()->GetRenderWidgetHostView();
1221   if (view)
1222     view->ShowDefinitionForSelection();
1223 #endif
1224 }
1225
1226 void WebContents::CopyImageAt(int x, int y) {
1227   const auto host = web_contents()->GetMainFrame();
1228   if (host)
1229     host->CopyImageAt(x, y);
1230 }
1231
1232 void WebContents::Focus() {
1233   web_contents()->Focus();
1234 }
1235
1236 #if !defined(OS_MACOSX)
1237 bool WebContents::IsFocused() const {
1238   auto view = web_contents()->GetRenderWidgetHostView();
1239   if (!view) return false;
1240
1241   if (GetType() != BACKGROUND_PAGE) {
1242     auto window = web_contents()->GetNativeView()->GetToplevelWindow();
1243     if (window && !window->IsVisible())
1244       return false;
1245   }
1246
1247   return view->HasFocus();
1248 }
1249 #endif
1250
1251 void WebContents::TabTraverse(bool reverse) {
1252   web_contents()->FocusThroughTabTraversal(reverse);
1253 }
1254
1255 bool WebContents::SendIPCMessage(bool all_frames,
1256                                  const base::string16& channel,
1257                                  const base::ListValue& args) {
1258   return Send(new AtomViewMsg_Message(routing_id(), all_frames, channel, args));
1259 }
1260
1261 void WebContents::SendInputEvent(v8::Isolate* isolate,
1262                                  v8::Local<v8::Value> input_event) {
1263   const auto view = web_contents()->GetRenderWidgetHostView();
1264   if (!view)
1265     return;
1266   const auto host = view->GetRenderWidgetHost();
1267   if (!host)
1268     return;
1269
1270   int type = mate::GetWebInputEventType(isolate, input_event);
1271   if (blink::WebInputEvent::isMouseEventType(type)) {
1272     blink::WebMouseEvent mouse_event;
1273     if (mate::ConvertFromV8(isolate, input_event, &mouse_event)) {
1274       host->ForwardMouseEvent(mouse_event);
1275       return;
1276     }
1277   } else if (blink::WebInputEvent::isKeyboardEventType(type)) {
1278     content::NativeWebKeyboardEvent keyboard_event;
1279     if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) {
1280       host->ForwardKeyboardEvent(keyboard_event);
1281       return;
1282     }
1283   } else if (type == blink::WebInputEvent::MouseWheel) {
1284     blink::WebMouseWheelEvent mouse_wheel_event;
1285     if (mate::ConvertFromV8(isolate, input_event, &mouse_wheel_event)) {
1286       host->ForwardWheelEvent(mouse_wheel_event);
1287       return;
1288     }
1289   }
1290
1291   isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
1292       isolate, "Invalid event object")));
1293 }
1294
1295 void WebContents::BeginFrameSubscription(mate::Arguments* args) {
1296   bool only_dirty = false;
1297   FrameSubscriber::FrameCaptureCallback callback;
1298
1299   args->GetNext(&only_dirty);
1300   if (!args->GetNext(&callback)) {
1301     args->ThrowError();
1302     return;
1303   }
1304
1305   const auto view = web_contents()->GetRenderWidgetHostView();
1306   if (view) {
1307     std::unique_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber(
1308         isolate(), view, callback, only_dirty));
1309     view->BeginFrameSubscription(std::move(frame_subscriber));
1310   }
1311 }
1312
1313 void WebContents::EndFrameSubscription() {
1314   const auto view = web_contents()->GetRenderWidgetHostView();
1315   if (view)
1316     view->EndFrameSubscription();
1317 }
1318
1319 void WebContents::StartDrag(const mate::Dictionary& item,
1320                             mate::Arguments* args) {
1321   base::FilePath file;
1322   std::vector<base::FilePath> files;
1323   if (!item.Get("files", &files) && item.Get("file", &file)) {
1324     files.push_back(file);
1325   }
1326
1327   mate::Handle<NativeImage> icon;
1328   if (!item.Get("icon", &icon) && !file.empty()) {
1329     // TODO(zcbenz): Set default icon from file.
1330   }
1331
1332   // Error checking.
1333   if (icon.IsEmpty()) {
1334     args->ThrowError("Must specify 'icon' option");
1335     return;
1336   }
1337
1338 #if defined(OS_MACOSX)
1339   // NSWindow.dragImage requires a non-empty NSImage
1340   if (icon->image().IsEmpty()) {
1341     args->ThrowError("Must specify non-empty 'icon' option");
1342     return;
1343   }
1344 #endif
1345
1346   // Start dragging.
1347   if (!files.empty()) {
1348     base::MessageLoop::ScopedNestableTaskAllower allow(
1349         base::MessageLoop::current());
1350     DragFileItems(files, icon->image(), web_contents()->GetNativeView());
1351   } else {
1352     args->ThrowError("Must specify either 'file' or 'files' option");
1353   }
1354 }
1355
1356 void WebContents::CapturePage(mate::Arguments* args) {
1357   gfx::Rect rect;
1358   base::Callback<void(const gfx::Image&)> callback;
1359
1360   if (!(args->Length() == 1 && args->GetNext(&callback)) &&
1361       !(args->Length() == 2 && args->GetNext(&rect)
1362                             && args->GetNext(&callback))) {
1363     args->ThrowError();
1364     return;
1365   }
1366
1367   const auto view = web_contents()->GetRenderWidgetHostView();
1368   const auto host = view ? view->GetRenderWidgetHost() : nullptr;
1369   if (!view || !host) {
1370     callback.Run(gfx::Image());
1371     return;
1372   }
1373
1374   // Capture full page if user doesn't specify a |rect|.
1375   const gfx::Size view_size = rect.IsEmpty() ? view->GetViewBounds().size() :
1376                                                rect.size();
1377
1378   // By default, the requested bitmap size is the view size in screen
1379   // coordinates.  However, if there's more pixel detail available on the
1380   // current system, increase the requested bitmap size to capture it all.
1381   gfx::Size bitmap_size = view_size;
1382   const gfx::NativeView native_view = view->GetNativeView();
1383   const float scale =
1384       display::Screen::GetScreen()->GetDisplayNearestWindow(native_view)
1385       .device_scale_factor();
1386   if (scale > 1.0f)
1387     bitmap_size = gfx::ScaleToCeiledSize(view_size, scale);
1388
1389   host->CopyFromBackingStore(gfx::Rect(rect.origin(), view_size),
1390                              bitmap_size,
1391                              base::Bind(&OnCapturePageDone, callback),
1392                              kBGRA_8888_SkColorType);
1393 }
1394
1395 void WebContents::OnCursorChange(const content::WebCursor& cursor) {
1396   content::WebCursor::CursorInfo info;
1397   cursor.GetCursorInfo(&info);
1398
1399   if (cursor.IsCustom()) {
1400     Emit("cursor-changed", CursorTypeToString(info),
1401       gfx::Image::CreateFrom1xBitmap(info.custom_image),
1402       info.image_scale_factor,
1403       gfx::Size(info.custom_image.width(), info.custom_image.height()),
1404       info.hotspot);
1405   } else {
1406     Emit("cursor-changed", CursorTypeToString(info));
1407   }
1408 }
1409
1410 void WebContents::SetSize(const SetSizeParams& params) {
1411   if (guest_delegate_)
1412     guest_delegate_->SetSize(params);
1413 }
1414
1415 bool WebContents::IsGuest() const {
1416   return type_ == WEB_VIEW;
1417 }
1418
1419 bool WebContents::IsOffScreen() const {
1420   return type_ == OFF_SCREEN;
1421 }
1422
1423 void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) {
1424   mate::Handle<NativeImage> image =
1425       NativeImage::Create(isolate(), gfx::Image::CreateFrom1xBitmap(bitmap));
1426   Emit("paint", dirty_rect, image);
1427 }
1428
1429 void WebContents::StartPainting() {
1430   if (!IsOffScreen())
1431     return;
1432
1433   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1434       web_contents()->GetRenderWidgetHostView());
1435   if (osr_rwhv)
1436     osr_rwhv->SetPainting(true);
1437 }
1438
1439 void WebContents::StopPainting() {
1440   if (!IsOffScreen())
1441     return;
1442
1443   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1444       web_contents()->GetRenderWidgetHostView());
1445   if (osr_rwhv)
1446     osr_rwhv->SetPainting(false);
1447 }
1448
1449 bool WebContents::IsPainting() const {
1450   if (!IsOffScreen())
1451     return false;
1452
1453   const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1454       web_contents()->GetRenderWidgetHostView());
1455   return osr_rwhv && osr_rwhv->IsPainting();
1456 }
1457
1458 void WebContents::SetFrameRate(int frame_rate) {
1459   if (!IsOffScreen())
1460     return;
1461
1462   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1463       web_contents()->GetRenderWidgetHostView());
1464   if (osr_rwhv)
1465     osr_rwhv->SetFrameRate(frame_rate);
1466 }
1467
1468 int WebContents::GetFrameRate() const {
1469   if (!IsOffScreen())
1470     return 0;
1471
1472   const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1473       web_contents()->GetRenderWidgetHostView());
1474   return osr_rwhv ? osr_rwhv->GetFrameRate() : 0;
1475 }
1476
1477 void WebContents::Invalidate() {
1478   if (!IsOffScreen())
1479     return;
1480
1481   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1482       web_contents()->GetRenderWidgetHostView());
1483   if (osr_rwhv)
1484     osr_rwhv->Invalidate();
1485 }
1486
1487 v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
1488   WebContentsPreferences* web_preferences =
1489       WebContentsPreferences::FromWebContents(web_contents());
1490   return mate::ConvertToV8(isolate, *web_preferences->web_preferences());
1491 }
1492
1493 v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() {
1494   if (owner_window())
1495     return Window::From(isolate(), owner_window());
1496   else
1497     return v8::Null(isolate());
1498 }
1499
1500 int32_t WebContents::ID() const {
1501   return weak_map_id();
1502 }
1503
1504 v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
1505   return v8::Local<v8::Value>::New(isolate, session_);
1506 }
1507
1508 content::WebContents* WebContents::HostWebContents() {
1509   if (!embedder_)
1510     return nullptr;
1511   return embedder_->web_contents();
1512 }
1513
1514 void WebContents::SetEmbedder(const WebContents* embedder) {
1515   if (embedder) {
1516     NativeWindow* owner_window = nullptr;
1517     auto relay = NativeWindowRelay::FromWebContents(embedder->web_contents());
1518     if (relay) {
1519       owner_window = relay->window.get();
1520     }
1521     if (owner_window)
1522       SetOwnerWindow(owner_window);
1523
1524     content::RenderWidgetHostView* rwhv =
1525         web_contents()->GetRenderWidgetHostView();
1526     if (rwhv) {
1527       rwhv->Hide();
1528       rwhv->Show();
1529     }
1530   }
1531 }
1532
1533 v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
1534   if (devtools_web_contents_.IsEmpty())
1535     return v8::Null(isolate);
1536   else
1537     return v8::Local<v8::Value>::New(isolate, devtools_web_contents_);
1538 }
1539
1540 v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
1541   if (debugger_.IsEmpty()) {
1542     auto handle = atom::api::Debugger::Create(isolate, web_contents());
1543     debugger_.Reset(isolate, handle.ToV8());
1544   }
1545   return v8::Local<v8::Value>::New(isolate, debugger_);
1546 }
1547
1548 // static
1549 void WebContents::BuildPrototype(v8::Isolate* isolate,
1550                                  v8::Local<v8::FunctionTemplate> prototype) {
1551   prototype->SetClassName(mate::StringToV8(isolate, "WebContents"));
1552   mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
1553       .MakeDestroyable()
1554       .SetMethod("getId", &WebContents::GetID)
1555       .SetMethod("getProcessId", &WebContents::GetProcessID)
1556       .SetMethod("equal", &WebContents::Equal)
1557       .SetMethod("_loadURL", &WebContents::LoadURL)
1558       .SetMethod("downloadURL", &WebContents::DownloadURL)
1559       .SetMethod("_getURL", &WebContents::GetURL)
1560       .SetMethod("getTitle", &WebContents::GetTitle)
1561       .SetMethod("isLoading", &WebContents::IsLoading)
1562       .SetMethod("isLoadingMainFrame", &WebContents::IsLoadingMainFrame)
1563       .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
1564       .SetMethod("_stop", &WebContents::Stop)
1565       .SetMethod("_goBack", &WebContents::GoBack)
1566       .SetMethod("_goForward", &WebContents::GoForward)
1567       .SetMethod("_goToOffset", &WebContents::GoToOffset)
1568       .SetMethod("isCrashed", &WebContents::IsCrashed)
1569       .SetMethod("setUserAgent", &WebContents::SetUserAgent)
1570       .SetMethod("getUserAgent", &WebContents::GetUserAgent)
1571       .SetMethod("savePage", &WebContents::SavePage)
1572       .SetMethod("openDevTools", &WebContents::OpenDevTools)
1573       .SetMethod("closeDevTools", &WebContents::CloseDevTools)
1574       .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
1575       .SetMethod("isDevToolsFocused", &WebContents::IsDevToolsFocused)
1576       .SetMethod("enableDeviceEmulation",
1577                  &WebContents::EnableDeviceEmulation)
1578       .SetMethod("disableDeviceEmulation",
1579                  &WebContents::DisableDeviceEmulation)
1580       .SetMethod("toggleDevTools", &WebContents::ToggleDevTools)
1581       .SetMethod("inspectElement", &WebContents::InspectElement)
1582       .SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
1583       .SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
1584       .SetMethod("undo", &WebContents::Undo)
1585       .SetMethod("redo", &WebContents::Redo)
1586       .SetMethod("cut", &WebContents::Cut)
1587       .SetMethod("copy", &WebContents::Copy)
1588       .SetMethod("paste", &WebContents::Paste)
1589       .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle)
1590       .SetMethod("delete", &WebContents::Delete)
1591       .SetMethod("selectAll", &WebContents::SelectAll)
1592       .SetMethod("unselect", &WebContents::Unselect)
1593       .SetMethod("replace", &WebContents::Replace)
1594       .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
1595       .SetMethod("findInPage", &WebContents::FindInPage)
1596       .SetMethod("stopFindInPage", &WebContents::StopFindInPage)
1597       .SetMethod("focus", &WebContents::Focus)
1598       .SetMethod("isFocused", &WebContents::IsFocused)
1599       .SetMethod("tabTraverse", &WebContents::TabTraverse)
1600       .SetMethod("_send", &WebContents::SendIPCMessage)
1601       .SetMethod("sendInputEvent", &WebContents::SendInputEvent)
1602       .SetMethod("beginFrameSubscription",
1603                  &WebContents::BeginFrameSubscription)
1604       .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
1605       .SetMethod("startDrag", &WebContents::StartDrag)
1606       .SetMethod("setSize", &WebContents::SetSize)
1607       .SetMethod("isGuest", &WebContents::IsGuest)
1608       .SetMethod("isOffscreen", &WebContents::IsOffScreen)
1609       .SetMethod("startPainting", &WebContents::StartPainting)
1610       .SetMethod("stopPainting", &WebContents::StopPainting)
1611       .SetMethod("isPainting", &WebContents::IsPainting)
1612       .SetMethod("setFrameRate", &WebContents::SetFrameRate)
1613       .SetMethod("getFrameRate", &WebContents::GetFrameRate)
1614       .SetMethod("invalidate", &WebContents::Invalidate)
1615       .SetMethod("getType", &WebContents::GetType)
1616       .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
1617       .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
1618       .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
1619       .SetMethod("unregisterServiceWorker",
1620                  &WebContents::UnregisterServiceWorker)
1621       .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
1622       .SetMethod("print", &WebContents::Print)
1623       .SetMethod("_printToPDF", &WebContents::PrintToPDF)
1624       .SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
1625       .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
1626       .SetMethod("showDefinitionForSelection",
1627                  &WebContents::ShowDefinitionForSelection)
1628       .SetMethod("copyImageAt", &WebContents::CopyImageAt)
1629       .SetMethod("capturePage", &WebContents::CapturePage)
1630       .SetMethod("setEmbedder", &WebContents::SetEmbedder)
1631       .SetProperty("id", &WebContents::ID)
1632       .SetProperty("session", &WebContents::Session)
1633       .SetProperty("hostWebContents", &WebContents::HostWebContents)
1634       .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
1635       .SetProperty("debugger", &WebContents::Debugger);
1636 }
1637
1638 AtomBrowserContext* WebContents::GetBrowserContext() const {
1639   return static_cast<AtomBrowserContext*>(web_contents()->GetBrowserContext());
1640 }
1641
1642 void WebContents::OnRendererMessage(const base::string16& channel,
1643                                     const base::ListValue& args) {
1644   // webContents.emit(channel, new Event(), args...);
1645   Emit(base::UTF16ToUTF8(channel), args);
1646 }
1647
1648 void WebContents::OnRendererMessageSync(const base::string16& channel,
1649                                         const base::ListValue& args,
1650                                         IPC::Message* message) {
1651   // webContents.emit(channel, new Event(sender, message), args...);
1652   EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
1653 }
1654
1655 // static
1656 mate::Handle<WebContents> WebContents::CreateFrom(
1657     v8::Isolate* isolate, content::WebContents* web_contents) {
1658   // We have an existing WebContents object in JS.
1659   auto existing = TrackableObject::FromWrappedClass(isolate, web_contents);
1660   if (existing)
1661     return mate::CreateHandle(isolate, static_cast<WebContents*>(existing));
1662
1663   // Otherwise create a new WebContents wrapper object.
1664   return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
1665         REMOTE));
1666 }
1667
1668 mate::Handle<WebContents> WebContents::CreateFrom(
1669     v8::Isolate* isolate, content::WebContents* web_contents, Type type) {
1670   // Otherwise create a new WebContents wrapper object.
1671   return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
1672         type));
1673 }
1674
1675 // static
1676 mate::Handle<WebContents> WebContents::Create(
1677     v8::Isolate* isolate, const mate::Dictionary& options) {
1678   return mate::CreateHandle(isolate, new WebContents(isolate, options));
1679 }
1680
1681 }  // namespace api
1682
1683 }  // namespace atom
1684
1685 namespace {
1686
1687 using atom::api::WebContents;
1688
1689 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
1690                 v8::Local<v8::Context> context, void* priv) {
1691   v8::Isolate* isolate = context->GetIsolate();
1692   mate::Dictionary dict(isolate, exports);
1693   dict.Set("WebContents", WebContents::GetConstructor(isolate)->GetFunction());
1694   dict.SetMethod("create", &WebContents::Create);
1695   dict.SetMethod("fromId", &mate::TrackableObject<WebContents>::FromWeakMapID);
1696   dict.SetMethod("getAllWebContents",
1697                  &mate::TrackableObject<WebContents>::GetAll);
1698 }
1699
1700 }  // namespace
1701
1702 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_web_contents, Initialize)