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