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