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