Signature of OnCreateWindow has changed
[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<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_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() || url.spec().size() > url::kMaxURLChars) {
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   if (view) {
904     WebContentsPreferences* web_preferences =
905         WebContentsPreferences::FromWebContents(web_contents());
906     std::string color_name;
907     if (web_preferences->web_preferences()->GetString(options::kBackgroundColor,
908                                                       &color_name)) {
909       view->SetBackgroundColor(ParseHexColor(color_name));
910     } else {
911       view->SetBackgroundColor(SK_ColorTRANSPARENT);
912     }
913   }
914 }
915
916 void WebContents::DownloadURL(const GURL& url) {
917   auto browser_context = web_contents()->GetBrowserContext();
918   auto download_manager =
919     content::BrowserContext::GetDownloadManager(browser_context);
920
921   download_manager->DownloadUrl(
922       content::DownloadUrlParameters::CreateForWebContentsMainFrame(
923           web_contents(), url));
924 }
925
926 GURL WebContents::GetURL() const {
927   return web_contents()->GetURL();
928 }
929
930 base::string16 WebContents::GetTitle() const {
931   return web_contents()->GetTitle();
932 }
933
934 bool WebContents::IsLoading() const {
935   return web_contents()->IsLoading();
936 }
937
938 bool WebContents::IsLoadingMainFrame() const {
939   // Comparing site instances works because Electron always creates a new site
940   // instance when navigating, regardless of origin. See AtomBrowserClient.
941   return (web_contents()->GetLastCommittedURL().is_empty() ||
942           web_contents()->GetSiteInstance() !=
943           web_contents()->GetPendingSiteInstance()) && IsLoading();
944 }
945
946 bool WebContents::IsWaitingForResponse() const {
947   return web_contents()->IsWaitingForResponse();
948 }
949
950 void WebContents::Stop() {
951   web_contents()->Stop();
952 }
953
954 void WebContents::GoBack() {
955   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
956   web_contents()->GetController().GoBack();
957 }
958
959 void WebContents::GoForward() {
960   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
961   web_contents()->GetController().GoForward();
962 }
963
964 void WebContents::GoToOffset(int offset) {
965   atom::AtomBrowserClient::SuppressRendererProcessRestartForOnce();
966   web_contents()->GetController().GoToOffset(offset);
967 }
968
969 bool WebContents::IsCrashed() const {
970   return web_contents()->IsCrashed();
971 }
972
973 void WebContents::SetUserAgent(const std::string& user_agent,
974                                mate::Arguments* args) {
975   web_contents()->SetUserAgentOverride(user_agent);
976 }
977
978 std::string WebContents::GetUserAgent() {
979   return web_contents()->GetUserAgentOverride();
980 }
981
982 bool WebContents::SavePage(const base::FilePath& full_file_path,
983                            const content::SavePageType& save_type,
984                            const SavePageHandler::SavePageCallback& callback) {
985   auto handler = new SavePageHandler(web_contents(), callback);
986   return handler->Handle(full_file_path, save_type);
987 }
988
989 void WebContents::OpenDevTools(mate::Arguments* args) {
990   if (type_ == REMOTE)
991     return;
992
993   if (!enable_devtools_)
994     return;
995
996   std::string state;
997   if (type_ == WEB_VIEW || !owner_window()) {
998     state = "detach";
999   } else if (args && args->Length() == 1) {
1000     bool detach = false;
1001     mate::Dictionary options;
1002     if (args->GetNext(&options)) {
1003       options.Get("mode", &state);
1004
1005       // TODO(kevinsawicki) Remove in 2.0
1006       options.Get("detach", &detach);
1007       if (state.empty() && detach)
1008         state = "detach";
1009     }
1010   }
1011   managed_web_contents()->SetDockState(state);
1012   managed_web_contents()->ShowDevTools();
1013 }
1014
1015 void WebContents::CloseDevTools() {
1016   if (type_ == REMOTE)
1017     return;
1018
1019   managed_web_contents()->CloseDevTools();
1020 }
1021
1022 bool WebContents::IsDevToolsOpened() {
1023   if (type_ == REMOTE)
1024     return false;
1025
1026   return managed_web_contents()->IsDevToolsViewShowing();
1027 }
1028
1029 bool WebContents::IsDevToolsFocused() {
1030   if (type_ == REMOTE)
1031     return false;
1032
1033   return managed_web_contents()->GetView()->IsDevToolsViewFocused();
1034 }
1035
1036 void WebContents::EnableDeviceEmulation(
1037     const blink::WebDeviceEmulationParams& params) {
1038   if (type_ == REMOTE)
1039     return;
1040
1041   Send(new ViewMsg_EnableDeviceEmulation(routing_id(), params));
1042 }
1043
1044 void WebContents::DisableDeviceEmulation() {
1045   if (type_ == REMOTE)
1046     return;
1047
1048   Send(new ViewMsg_DisableDeviceEmulation(routing_id()));
1049 }
1050
1051 void WebContents::ToggleDevTools() {
1052   if (IsDevToolsOpened())
1053     CloseDevTools();
1054   else
1055     OpenDevTools(nullptr);
1056 }
1057
1058 void WebContents::InspectElement(int x, int y) {
1059   if (type_ == REMOTE)
1060     return;
1061
1062   if (!enable_devtools_)
1063     return;
1064
1065   if (!managed_web_contents()->GetDevToolsWebContents())
1066     OpenDevTools(nullptr);
1067   managed_web_contents()->InspectElement(x, y);
1068 }
1069
1070 void WebContents::InspectServiceWorker() {
1071   if (type_ == REMOTE)
1072     return;
1073
1074   if (!enable_devtools_)
1075     return;
1076
1077   for (const auto& agent_host : content::DevToolsAgentHost::GetOrCreateAll()) {
1078     if (agent_host->GetType() ==
1079         content::DevToolsAgentHost::TYPE_SERVICE_WORKER) {
1080       OpenDevTools(nullptr);
1081       managed_web_contents()->AttachTo(agent_host);
1082       break;
1083     }
1084   }
1085 }
1086
1087 void WebContents::HasServiceWorker(
1088     const base::Callback<void(bool)>& callback) {
1089   auto context = GetServiceWorkerContext(web_contents());
1090   if (!context)
1091     return;
1092
1093   context->CheckHasServiceWorker(web_contents()->GetLastCommittedURL(),
1094                                  GURL::EmptyGURL(),
1095                                  callback);
1096 }
1097
1098 void WebContents::UnregisterServiceWorker(
1099     const base::Callback<void(bool)>& callback) {
1100   auto context = GetServiceWorkerContext(web_contents());
1101   if (!context)
1102     return;
1103
1104   context->UnregisterServiceWorker(web_contents()->GetLastCommittedURL(),
1105                                    callback);
1106 }
1107
1108 void WebContents::SetAudioMuted(bool muted) {
1109   web_contents()->SetAudioMuted(muted);
1110 }
1111
1112 bool WebContents::IsAudioMuted() {
1113   return web_contents()->IsAudioMuted();
1114 }
1115
1116 void WebContents::Print(mate::Arguments* args) {
1117   PrintSettings settings = { false, false };
1118   if (args->Length() == 1 && !args->GetNext(&settings)) {
1119     args->ThrowError();
1120     return;
1121   }
1122
1123   printing::PrintViewManagerBasic::FromWebContents(web_contents())->
1124       PrintNow(settings.silent, settings.print_background);
1125 }
1126
1127 void WebContents::PrintToPDF(const base::DictionaryValue& setting,
1128                              const PrintToPDFCallback& callback) {
1129   printing::PrintPreviewMessageHandler::FromWebContents(web_contents())->
1130       PrintToPDF(setting, callback);
1131 }
1132
1133 void WebContents::AddWorkSpace(mate::Arguments* args,
1134                                const base::FilePath& path) {
1135   if (path.empty()) {
1136     args->ThrowError("path cannot be empty");
1137     return;
1138   }
1139   DevToolsAddFileSystem(path);
1140 }
1141
1142 void WebContents::RemoveWorkSpace(mate::Arguments* args,
1143                                   const base::FilePath& path) {
1144   if (path.empty()) {
1145     args->ThrowError("path cannot be empty");
1146     return;
1147   }
1148   DevToolsRemoveFileSystem(path);
1149 }
1150
1151 void WebContents::Undo() {
1152   web_contents()->Undo();
1153 }
1154
1155 void WebContents::Redo() {
1156   web_contents()->Redo();
1157 }
1158
1159 void WebContents::Cut() {
1160   web_contents()->Cut();
1161 }
1162
1163 void WebContents::Copy() {
1164   web_contents()->Copy();
1165 }
1166
1167 void WebContents::Paste() {
1168   web_contents()->Paste();
1169 }
1170
1171 void WebContents::PasteAndMatchStyle() {
1172   web_contents()->PasteAndMatchStyle();
1173 }
1174
1175 void WebContents::Delete() {
1176   web_contents()->Delete();
1177 }
1178
1179 void WebContents::SelectAll() {
1180   web_contents()->SelectAll();
1181 }
1182
1183 void WebContents::Unselect() {
1184   web_contents()->Unselect();
1185 }
1186
1187 void WebContents::Replace(const base::string16& word) {
1188   web_contents()->Replace(word);
1189 }
1190
1191 void WebContents::ReplaceMisspelling(const base::string16& word) {
1192   web_contents()->ReplaceMisspelling(word);
1193 }
1194
1195 uint32_t WebContents::FindInPage(mate::Arguments* args) {
1196   uint32_t request_id = GetNextRequestId();
1197   base::string16 search_text;
1198   blink::WebFindOptions options;
1199   if (!args->GetNext(&search_text) || search_text.empty()) {
1200     args->ThrowError("Must provide a non-empty search content");
1201     return 0;
1202   }
1203
1204   args->GetNext(&options);
1205
1206   web_contents()->Find(request_id, search_text, options);
1207   return request_id;
1208 }
1209
1210 void WebContents::StopFindInPage(content::StopFindAction action) {
1211   web_contents()->StopFinding(action);
1212 }
1213
1214 void WebContents::ShowDefinitionForSelection() {
1215 #if defined(OS_MACOSX)
1216   const auto view = web_contents()->GetRenderWidgetHostView();
1217   if (view)
1218     view->ShowDefinitionForSelection();
1219 #endif
1220 }
1221
1222 void WebContents::CopyImageAt(int x, int y) {
1223   const auto host = web_contents()->GetMainFrame();
1224   if (host)
1225     host->CopyImageAt(x, y);
1226 }
1227
1228 void WebContents::Focus() {
1229   web_contents()->Focus();
1230 }
1231
1232 #if !defined(OS_MACOSX)
1233 bool WebContents::IsFocused() const {
1234   auto view = web_contents()->GetRenderWidgetHostView();
1235   if (!view) return false;
1236
1237   if (GetType() != BACKGROUND_PAGE) {
1238     auto window = web_contents()->GetNativeView()->GetToplevelWindow();
1239     if (window && !window->IsVisible())
1240       return false;
1241   }
1242
1243   return view->HasFocus();
1244 }
1245 #endif
1246
1247 void WebContents::TabTraverse(bool reverse) {
1248   web_contents()->FocusThroughTabTraversal(reverse);
1249 }
1250
1251 bool WebContents::SendIPCMessage(bool all_frames,
1252                                  const base::string16& channel,
1253                                  const base::ListValue& args) {
1254   return Send(new AtomViewMsg_Message(routing_id(), all_frames, channel, args));
1255 }
1256
1257 void WebContents::SendInputEvent(v8::Isolate* isolate,
1258                                  v8::Local<v8::Value> input_event) {
1259   const auto view = web_contents()->GetRenderWidgetHostView();
1260   if (!view)
1261     return;
1262   const auto host = view->GetRenderWidgetHost();
1263   if (!host)
1264     return;
1265
1266   int type = mate::GetWebInputEventType(isolate, input_event);
1267   if (blink::WebInputEvent::isMouseEventType(type)) {
1268     blink::WebMouseEvent mouse_event;
1269     if (mate::ConvertFromV8(isolate, input_event, &mouse_event)) {
1270       host->ForwardMouseEvent(mouse_event);
1271       return;
1272     }
1273   } else if (blink::WebInputEvent::isKeyboardEventType(type)) {
1274     content::NativeWebKeyboardEvent keyboard_event;
1275     if (mate::ConvertFromV8(isolate, input_event, &keyboard_event)) {
1276       host->ForwardKeyboardEvent(keyboard_event);
1277       return;
1278     }
1279   } else if (type == blink::WebInputEvent::MouseWheel) {
1280     blink::WebMouseWheelEvent mouse_wheel_event;
1281     if (mate::ConvertFromV8(isolate, input_event, &mouse_wheel_event)) {
1282       host->ForwardWheelEvent(mouse_wheel_event);
1283       return;
1284     }
1285   }
1286
1287   isolate->ThrowException(v8::Exception::Error(mate::StringToV8(
1288       isolate, "Invalid event object")));
1289 }
1290
1291 void WebContents::BeginFrameSubscription(mate::Arguments* args) {
1292   bool only_dirty = false;
1293   FrameSubscriber::FrameCaptureCallback callback;
1294
1295   args->GetNext(&only_dirty);
1296   if (!args->GetNext(&callback)) {
1297     args->ThrowError();
1298     return;
1299   }
1300
1301   const auto view = web_contents()->GetRenderWidgetHostView();
1302   if (view) {
1303     std::unique_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber(
1304         isolate(), view, callback, only_dirty));
1305     view->BeginFrameSubscription(std::move(frame_subscriber));
1306   }
1307 }
1308
1309 void WebContents::EndFrameSubscription() {
1310   const auto view = web_contents()->GetRenderWidgetHostView();
1311   if (view)
1312     view->EndFrameSubscription();
1313 }
1314
1315 void WebContents::StartDrag(const mate::Dictionary& item,
1316                             mate::Arguments* args) {
1317   base::FilePath file;
1318   std::vector<base::FilePath> files;
1319   if (!item.Get("files", &files) && item.Get("file", &file)) {
1320     files.push_back(file);
1321   }
1322
1323   mate::Handle<NativeImage> icon;
1324   if (!item.Get("icon", &icon) && !file.empty()) {
1325     // TODO(zcbenz): Set default icon from file.
1326   }
1327
1328   // Error checking.
1329   if (icon.IsEmpty()) {
1330     args->ThrowError("Must specify 'icon' option");
1331     return;
1332   }
1333
1334 #if defined(OS_MACOSX)
1335   // NSWindow.dragImage requires a non-empty NSImage
1336   if (icon->image().IsEmpty()) {
1337     args->ThrowError("Must specify non-empty 'icon' option");
1338     return;
1339   }
1340 #endif
1341
1342   // Start dragging.
1343   if (!files.empty()) {
1344     base::MessageLoop::ScopedNestableTaskAllower allow(
1345         base::MessageLoop::current());
1346     DragFileItems(files, icon->image(), web_contents()->GetNativeView());
1347   } else {
1348     args->ThrowError("Must specify either 'file' or 'files' option");
1349   }
1350 }
1351
1352 void WebContents::CapturePage(mate::Arguments* args) {
1353   gfx::Rect rect;
1354   base::Callback<void(const gfx::Image&)> callback;
1355
1356   if (!(args->Length() == 1 && args->GetNext(&callback)) &&
1357       !(args->Length() == 2 && args->GetNext(&rect)
1358                             && args->GetNext(&callback))) {
1359     args->ThrowError();
1360     return;
1361   }
1362
1363   const auto view = web_contents()->GetRenderWidgetHostView();
1364   const auto host = view ? view->GetRenderWidgetHost() : nullptr;
1365   if (!view || !host) {
1366     callback.Run(gfx::Image());
1367     return;
1368   }
1369
1370   // Capture full page if user doesn't specify a |rect|.
1371   const gfx::Size view_size = rect.IsEmpty() ? view->GetViewBounds().size() :
1372                                                rect.size();
1373
1374   // By default, the requested bitmap size is the view size in screen
1375   // coordinates.  However, if there's more pixel detail available on the
1376   // current system, increase the requested bitmap size to capture it all.
1377   gfx::Size bitmap_size = view_size;
1378   const gfx::NativeView native_view = view->GetNativeView();
1379   const float scale =
1380       display::Screen::GetScreen()->GetDisplayNearestWindow(native_view)
1381       .device_scale_factor();
1382   if (scale > 1.0f)
1383     bitmap_size = gfx::ScaleToCeiledSize(view_size, scale);
1384
1385   host->CopyFromBackingStore(gfx::Rect(rect.origin(), view_size),
1386                              bitmap_size,
1387                              base::Bind(&OnCapturePageDone, callback),
1388                              kBGRA_8888_SkColorType);
1389 }
1390
1391 void WebContents::OnCursorChange(const content::WebCursor& cursor) {
1392   content::WebCursor::CursorInfo info;
1393   cursor.GetCursorInfo(&info);
1394
1395   if (cursor.IsCustom()) {
1396     Emit("cursor-changed", CursorTypeToString(info),
1397       gfx::Image::CreateFrom1xBitmap(info.custom_image),
1398       info.image_scale_factor,
1399       gfx::Size(info.custom_image.width(), info.custom_image.height()),
1400       info.hotspot);
1401   } else {
1402     Emit("cursor-changed", CursorTypeToString(info));
1403   }
1404 }
1405
1406 void WebContents::SetSize(const SetSizeParams& params) {
1407   if (guest_delegate_)
1408     guest_delegate_->SetSize(params);
1409 }
1410
1411 bool WebContents::IsGuest() const {
1412   return type_ == WEB_VIEW;
1413 }
1414
1415 bool WebContents::IsOffScreen() const {
1416   return type_ == OFF_SCREEN;
1417 }
1418
1419 void WebContents::OnPaint(const gfx::Rect& dirty_rect, const SkBitmap& bitmap) {
1420   mate::Handle<NativeImage> image =
1421       NativeImage::Create(isolate(), gfx::Image::CreateFrom1xBitmap(bitmap));
1422   Emit("paint", dirty_rect, image);
1423 }
1424
1425 void WebContents::StartPainting() {
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(true);
1433 }
1434
1435 void WebContents::StopPainting() {
1436   if (!IsOffScreen())
1437     return;
1438
1439   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1440       web_contents()->GetRenderWidgetHostView());
1441   if (osr_rwhv)
1442     osr_rwhv->SetPainting(false);
1443 }
1444
1445 bool WebContents::IsPainting() const {
1446   if (!IsOffScreen())
1447     return false;
1448
1449   const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1450       web_contents()->GetRenderWidgetHostView());
1451   return osr_rwhv && osr_rwhv->IsPainting();
1452 }
1453
1454 void WebContents::SetFrameRate(int frame_rate) {
1455   if (!IsOffScreen())
1456     return;
1457
1458   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1459       web_contents()->GetRenderWidgetHostView());
1460   if (osr_rwhv)
1461     osr_rwhv->SetFrameRate(frame_rate);
1462 }
1463
1464 int WebContents::GetFrameRate() const {
1465   if (!IsOffScreen())
1466     return 0;
1467
1468   const auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1469       web_contents()->GetRenderWidgetHostView());
1470   return osr_rwhv ? osr_rwhv->GetFrameRate() : 0;
1471 }
1472
1473 void WebContents::Invalidate() {
1474   if (!IsOffScreen())
1475     return;
1476
1477   auto* osr_rwhv = static_cast<OffScreenRenderWidgetHostView*>(
1478       web_contents()->GetRenderWidgetHostView());
1479   if (osr_rwhv)
1480     osr_rwhv->Invalidate();
1481 }
1482
1483 v8::Local<v8::Value> WebContents::GetWebPreferences(v8::Isolate* isolate) {
1484   WebContentsPreferences* web_preferences =
1485       WebContentsPreferences::FromWebContents(web_contents());
1486   return mate::ConvertToV8(isolate, *web_preferences->web_preferences());
1487 }
1488
1489 v8::Local<v8::Value> WebContents::GetOwnerBrowserWindow() {
1490   if (owner_window())
1491     return Window::From(isolate(), owner_window());
1492   else
1493     return v8::Null(isolate());
1494 }
1495
1496 int32_t WebContents::ID() const {
1497   return weak_map_id();
1498 }
1499
1500 v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
1501   return v8::Local<v8::Value>::New(isolate, session_);
1502 }
1503
1504 content::WebContents* WebContents::HostWebContents() {
1505   if (!embedder_)
1506     return nullptr;
1507   return embedder_->web_contents();
1508 }
1509
1510 void WebContents::SetEmbedder(const WebContents* embedder) {
1511   if (embedder) {
1512     NativeWindow* owner_window = nullptr;
1513     auto relay = NativeWindowRelay::FromWebContents(embedder->web_contents());
1514     if (relay) {
1515       owner_window = relay->window.get();
1516     }
1517     if (owner_window)
1518       SetOwnerWindow(owner_window);
1519
1520     content::RenderWidgetHostView* rwhv =
1521         web_contents()->GetRenderWidgetHostView();
1522     if (rwhv) {
1523       rwhv->Hide();
1524       rwhv->Show();
1525     }
1526   }
1527 }
1528
1529 v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
1530   if (devtools_web_contents_.IsEmpty())
1531     return v8::Null(isolate);
1532   else
1533     return v8::Local<v8::Value>::New(isolate, devtools_web_contents_);
1534 }
1535
1536 v8::Local<v8::Value> WebContents::Debugger(v8::Isolate* isolate) {
1537   if (debugger_.IsEmpty()) {
1538     auto handle = atom::api::Debugger::Create(isolate, web_contents());
1539     debugger_.Reset(isolate, handle.ToV8());
1540   }
1541   return v8::Local<v8::Value>::New(isolate, debugger_);
1542 }
1543
1544 // static
1545 void WebContents::BuildPrototype(v8::Isolate* isolate,
1546                                  v8::Local<v8::FunctionTemplate> prototype) {
1547   prototype->SetClassName(mate::StringToV8(isolate, "WebContents"));
1548   mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
1549       .MakeDestroyable()
1550       .SetMethod("getId", &WebContents::GetID)
1551       .SetMethod("getProcessId", &WebContents::GetProcessID)
1552       .SetMethod("equal", &WebContents::Equal)
1553       .SetMethod("_loadURL", &WebContents::LoadURL)
1554       .SetMethod("downloadURL", &WebContents::DownloadURL)
1555       .SetMethod("_getURL", &WebContents::GetURL)
1556       .SetMethod("getTitle", &WebContents::GetTitle)
1557       .SetMethod("isLoading", &WebContents::IsLoading)
1558       .SetMethod("isLoadingMainFrame", &WebContents::IsLoadingMainFrame)
1559       .SetMethod("isWaitingForResponse", &WebContents::IsWaitingForResponse)
1560       .SetMethod("_stop", &WebContents::Stop)
1561       .SetMethod("_goBack", &WebContents::GoBack)
1562       .SetMethod("_goForward", &WebContents::GoForward)
1563       .SetMethod("_goToOffset", &WebContents::GoToOffset)
1564       .SetMethod("isCrashed", &WebContents::IsCrashed)
1565       .SetMethod("setUserAgent", &WebContents::SetUserAgent)
1566       .SetMethod("getUserAgent", &WebContents::GetUserAgent)
1567       .SetMethod("savePage", &WebContents::SavePage)
1568       .SetMethod("openDevTools", &WebContents::OpenDevTools)
1569       .SetMethod("closeDevTools", &WebContents::CloseDevTools)
1570       .SetMethod("isDevToolsOpened", &WebContents::IsDevToolsOpened)
1571       .SetMethod("isDevToolsFocused", &WebContents::IsDevToolsFocused)
1572       .SetMethod("enableDeviceEmulation",
1573                  &WebContents::EnableDeviceEmulation)
1574       .SetMethod("disableDeviceEmulation",
1575                  &WebContents::DisableDeviceEmulation)
1576       .SetMethod("toggleDevTools", &WebContents::ToggleDevTools)
1577       .SetMethod("inspectElement", &WebContents::InspectElement)
1578       .SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
1579       .SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
1580       .SetMethod("undo", &WebContents::Undo)
1581       .SetMethod("redo", &WebContents::Redo)
1582       .SetMethod("cut", &WebContents::Cut)
1583       .SetMethod("copy", &WebContents::Copy)
1584       .SetMethod("paste", &WebContents::Paste)
1585       .SetMethod("pasteAndMatchStyle", &WebContents::PasteAndMatchStyle)
1586       .SetMethod("delete", &WebContents::Delete)
1587       .SetMethod("selectAll", &WebContents::SelectAll)
1588       .SetMethod("unselect", &WebContents::Unselect)
1589       .SetMethod("replace", &WebContents::Replace)
1590       .SetMethod("replaceMisspelling", &WebContents::ReplaceMisspelling)
1591       .SetMethod("findInPage", &WebContents::FindInPage)
1592       .SetMethod("stopFindInPage", &WebContents::StopFindInPage)
1593       .SetMethod("focus", &WebContents::Focus)
1594       .SetMethod("isFocused", &WebContents::IsFocused)
1595       .SetMethod("tabTraverse", &WebContents::TabTraverse)
1596       .SetMethod("_send", &WebContents::SendIPCMessage)
1597       .SetMethod("sendInputEvent", &WebContents::SendInputEvent)
1598       .SetMethod("beginFrameSubscription",
1599                  &WebContents::BeginFrameSubscription)
1600       .SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
1601       .SetMethod("startDrag", &WebContents::StartDrag)
1602       .SetMethod("setSize", &WebContents::SetSize)
1603       .SetMethod("isGuest", &WebContents::IsGuest)
1604       .SetMethod("isOffscreen", &WebContents::IsOffScreen)
1605       .SetMethod("startPainting", &WebContents::StartPainting)
1606       .SetMethod("stopPainting", &WebContents::StopPainting)
1607       .SetMethod("isPainting", &WebContents::IsPainting)
1608       .SetMethod("setFrameRate", &WebContents::SetFrameRate)
1609       .SetMethod("getFrameRate", &WebContents::GetFrameRate)
1610       .SetMethod("invalidate", &WebContents::Invalidate)
1611       .SetMethod("getType", &WebContents::GetType)
1612       .SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
1613       .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
1614       .SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
1615       .SetMethod("unregisterServiceWorker",
1616                  &WebContents::UnregisterServiceWorker)
1617       .SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
1618       .SetMethod("print", &WebContents::Print)
1619       .SetMethod("_printToPDF", &WebContents::PrintToPDF)
1620       .SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
1621       .SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
1622       .SetMethod("showDefinitionForSelection",
1623                  &WebContents::ShowDefinitionForSelection)
1624       .SetMethod("copyImageAt", &WebContents::CopyImageAt)
1625       .SetMethod("capturePage", &WebContents::CapturePage)
1626       .SetMethod("setEmbedder", &WebContents::SetEmbedder)
1627       .SetProperty("id", &WebContents::ID)
1628       .SetProperty("session", &WebContents::Session)
1629       .SetProperty("hostWebContents", &WebContents::HostWebContents)
1630       .SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
1631       .SetProperty("debugger", &WebContents::Debugger);
1632 }
1633
1634 AtomBrowserContext* WebContents::GetBrowserContext() const {
1635   return static_cast<AtomBrowserContext*>(web_contents()->GetBrowserContext());
1636 }
1637
1638 void WebContents::OnRendererMessage(const base::string16& channel,
1639                                     const base::ListValue& args) {
1640   // webContents.emit(channel, new Event(), args...);
1641   Emit(base::UTF16ToUTF8(channel), args);
1642 }
1643
1644 void WebContents::OnRendererMessageSync(const base::string16& channel,
1645                                         const base::ListValue& args,
1646                                         IPC::Message* message) {
1647   // webContents.emit(channel, new Event(sender, message), args...);
1648   EmitWithSender(base::UTF16ToUTF8(channel), web_contents(), message, args);
1649 }
1650
1651 // static
1652 mate::Handle<WebContents> WebContents::CreateFrom(
1653     v8::Isolate* isolate, content::WebContents* web_contents) {
1654   // We have an existing WebContents object in JS.
1655   auto existing = TrackableObject::FromWrappedClass(isolate, web_contents);
1656   if (existing)
1657     return mate::CreateHandle(isolate, static_cast<WebContents*>(existing));
1658
1659   // Otherwise create a new WebContents wrapper object.
1660   return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
1661         REMOTE));
1662 }
1663
1664 mate::Handle<WebContents> WebContents::CreateFrom(
1665     v8::Isolate* isolate, content::WebContents* web_contents, Type type) {
1666   // Otherwise create a new WebContents wrapper object.
1667   return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
1668         type));
1669 }
1670
1671 // static
1672 mate::Handle<WebContents> WebContents::Create(
1673     v8::Isolate* isolate, const mate::Dictionary& options) {
1674   return mate::CreateHandle(isolate, new WebContents(isolate, options));
1675 }
1676
1677 }  // namespace api
1678
1679 }  // namespace atom
1680
1681 namespace {
1682
1683 using atom::api::WebContents;
1684
1685 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
1686                 v8::Local<v8::Context> context, void* priv) {
1687   v8::Isolate* isolate = context->GetIsolate();
1688   mate::Dictionary dict(isolate, exports);
1689   dict.Set("WebContents", WebContents::GetConstructor(isolate)->GetFunction());
1690   dict.SetMethod("create", &WebContents::Create);
1691   dict.SetMethod("fromId", &mate::TrackableObject<WebContents>::FromWeakMapID);
1692   dict.SetMethod("getAllWebContents",
1693                  &mate::TrackableObject<WebContents>::GetAll);
1694 }
1695
1696 }  // namespace
1697
1698 NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_web_contents, Initialize)