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