From ac794c80856285453042566dc651487277d2b876 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 3 Apr 2014 20:20:42 +0800 Subject: [PATCH 01/11] Enable intercepting devtools window. --- atom/renderer/atom_renderer_client.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 2ac028f279..0b8c474076 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -28,9 +28,6 @@ const char* kSecurityManualEnableIframe = "manual-enable-iframe"; const char* kSecurityDisable = "disable"; const char* kSecurityEnableNodeIntegration = "enable-node-integration"; -// Scheme used by devtools -const char* kChromeDevToolsScheme = "chrome-devtools"; - } // namespace AtomRendererClient::AtomRendererClient() @@ -159,10 +156,6 @@ bool AtomRendererClient::ShouldFork(WebKit::WebFrame* frame, bool AtomRendererClient::IsNodeBindingEnabled(WebKit::WebFrame* frame) { if (node_integration_ == DISABLE) return false; - // Do not pollute devtools. - else if (frame != NULL && - GURL(frame->document().url()).SchemeIs(kChromeDevToolsScheme)) - return false; // Node integration is enabled in main frame unless explictly disabled. else if (frame == main_frame_) return true; From b1f0c2d1741ac15491136ab42415dbdcfd97efc1 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Thu, 3 Apr 2014 20:54:42 +0800 Subject: [PATCH 02/11] Override web inspector's context menu. --- atom.gyp | 1 + atom/renderer/lib/init.coffee | 9 +++++++-- atom/renderer/lib/inspector.coffee | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 atom/renderer/lib/inspector.coffee diff --git a/atom.gyp b/atom.gyp index cb5256c631..621555149d 100644 --- a/atom.gyp +++ b/atom.gyp @@ -32,6 +32,7 @@ 'atom/common/api/lib/shell.coffee', 'atom/common/lib/init.coffee', 'atom/renderer/lib/init.coffee', + 'atom/renderer/lib/inspector.coffee', 'atom/renderer/lib/override.coffee', 'atom/renderer/api/lib/ipc.coffee', 'atom/renderer/api/lib/remote.coffee', diff --git a/atom/renderer/lib/init.coffee b/atom/renderer/lib/init.coffee index 33d7d20024..e781ac4175 100644 --- a/atom/renderer/lib/init.coffee +++ b/atom/renderer/lib/init.coffee @@ -1,4 +1,5 @@ path = require 'path' +url = require 'url' Module = require 'module' # Expose information of current process. @@ -42,5 +43,9 @@ else global.__filename = __filename global.__dirname = __dirname -# Override default web functions. -require path.join(__dirname, 'override') +if location.protocol is 'chrome-devtools:' + # Override some inspector APIs. + require path.join(__dirname, 'inspector') +else + # Override default web functions. + require path.join(__dirname, 'override') diff --git a/atom/renderer/lib/inspector.coffee b/atom/renderer/lib/inspector.coffee new file mode 100644 index 0000000000..7030fdd369 --- /dev/null +++ b/atom/renderer/lib/inspector.coffee @@ -0,0 +1,30 @@ +# Use menu API to show context menu. +window.onload = -> + WebInspector.ContextMenu.prototype.show = -> + menuObject = @_buildDescriptor() + if menuObject.length + WebInspector._contextMenu = this + createMenu(menuObject, @_event) + @_event.consume() + +convertToMenuTemplate = (items) -> + template = [] + for item in items + if item.type is 'subMenu' + template.push + type: 'submenu' + label: item.label + submenu: convertToMenuTemplate item.subItems + else + template.push + type: 'normal' + label: item.label + template + +createMenu = (items, event) -> + remote = require 'remote' + Menu = remote.require 'menu' + + menu = Menu.buildFromTemplate convertToMenuTemplate(items.subItems) + menu.popup() + event.consume true From a80fe40f562a2243f34d5514117c018c4c835441 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 4 Apr 2014 22:04:42 +0800 Subject: [PATCH 03/11] Synchronous event should be bound to WebContents. This allows us to reply to synchronous message for arbitrary WebContents. --- atom/browser/api/atom_api_event.cc | 11 +++++---- atom/browser/api/atom_api_event.h | 16 ++++++-------- atom/browser/api/atom_browser_bindings.cc | 2 +- atom/browser/api/atom_browser_bindings.h | 8 ++++--- atom/browser/native_window.cc | 27 ++++++++++++++--------- atom/browser/native_window.h | 10 ++++----- 6 files changed, 39 insertions(+), 35 deletions(-) diff --git a/atom/browser/api/atom_api_event.cc b/atom/browser/api/atom_api_event.cc index ec074bb28c..b1f1406677 100644 --- a/atom/browser/api/atom_api_event.cc +++ b/atom/browser/api/atom_api_event.cc @@ -4,10 +4,10 @@ #include "atom/browser/api/atom_api_event.h" -#include "atom/browser/native_window.h" #include "atom/common/api/api_messages.h" #include "atom/common/v8/node_common.h" #include "atom/common/v8/native_type_conversions.h" +#include "content/public/browser/web_contents.h" namespace atom { @@ -22,8 +22,6 @@ Event::Event() } Event::~Event() { - if (sender_ != NULL) - sender_->RemoveObserver(this); } // static @@ -44,16 +42,17 @@ v8::Handle Event::CreateV8Object() { return t->NewInstance(0, NULL); } -void Event::SetSenderAndMessage(NativeWindow* sender, IPC::Message* message) { +void Event::SetSenderAndMessage(content::WebContents* sender, + IPC::Message* message) { DCHECK(!sender_); DCHECK(!message_); sender_ = sender; message_ = message; - sender_->AddObserver(this); + Observe(sender); } -void Event::OnWindowClosed() { +void Event::WebContentsDestroyed(content::WebContents* web_contents) { sender_ = NULL; message_ = NULL; } diff --git a/atom/browser/api/atom_api_event.h b/atom/browser/api/atom_api_event.h index 315b395392..1bbcb4a2df 100644 --- a/atom/browser/api/atom_api_event.h +++ b/atom/browser/api/atom_api_event.h @@ -5,11 +5,11 @@ #ifndef ATOM_BROWSER_API_ATOM_API_EVENT_H_ #define ATOM_BROWSER_API_ATOM_API_EVENT_H_ +#include "atom/common/v8/scoped_persistent.h" #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/strings/string16.h" -#include "atom/browser/native_window_observer.h" -#include "atom/common/v8/scoped_persistent.h" +#include "content/public/browser/web_contents_observer.h" #include "vendor/node/src/node_object_wrap.h" namespace IPC { @@ -18,12 +18,10 @@ class Message; namespace atom { -class NativeWindow; - namespace api { class Event : public node::ObjectWrap, - public NativeWindowObserver { + public content::WebContentsObserver { public: virtual ~Event(); @@ -31,7 +29,7 @@ class Event : public node::ObjectWrap, static v8::Handle CreateV8Object(); // Pass the sender and message to be replied. - void SetSenderAndMessage(NativeWindow* sender, IPC::Message* message); + void SetSenderAndMessage(content::WebContents* sender, IPC::Message* message); // Whether event.preventDefault() is called. bool prevent_default() const { return prevent_default_; } @@ -39,8 +37,8 @@ class Event : public node::ObjectWrap, protected: Event(); - // NativeWindowObserver implementations: - virtual void OnWindowClosed() OVERRIDE; + // content::WebContentsObserver implementations: + virtual void WebContentsDestroyed(content::WebContents*) OVERRIDE; private: static void New(const v8::FunctionCallbackInfo& args); @@ -52,7 +50,7 @@ class Event : public node::ObjectWrap, static ScopedPersistent constructor_template_; // Replyer for the synchronous messages. - NativeWindow* sender_; + content::WebContents* sender_; IPC::Message* message_; bool prevent_default_; diff --git a/atom/browser/api/atom_browser_bindings.cc b/atom/browser/api/atom_browser_bindings.cc index 825fefa629..94c925f8a0 100644 --- a/atom/browser/api/atom_browser_bindings.cc +++ b/atom/browser/api/atom_browser_bindings.cc @@ -57,7 +57,7 @@ void AtomBrowserBindings::OnRendererMessageSync( int routing_id, const string16& channel, const base::ListValue& args, - NativeWindow* sender, + content::WebContents* sender, IPC::Message* message) { v8::Locker locker(node_isolate); v8::HandleScope handle_scope(node_isolate); diff --git a/atom/browser/api/atom_browser_bindings.h b/atom/browser/api/atom_browser_bindings.h index 026cbaf40f..e015206322 100644 --- a/atom/browser/api/atom_browser_bindings.h +++ b/atom/browser/api/atom_browser_bindings.h @@ -13,14 +13,16 @@ namespace base { class ListValue; } +namespace content { +class WebContents; +} + namespace IPC { class Message; } namespace atom { -class NativeWindow; - class AtomBrowserBindings : public AtomBindings { public: AtomBrowserBindings(); @@ -37,7 +39,7 @@ class AtomBrowserBindings : public AtomBindings { int routing_id, const string16& channel, const base::ListValue& args, - NativeWindow* sender, + content::WebContents* sender, IPC::Message* message); private: diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 5c50d1f75a..6f6f163a1e 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -8,6 +8,17 @@ #include #include +#include "atom/browser/api/atom_browser_bindings.h" +#include "atom/browser/atom_browser_context.h" +#include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/atom_javascript_dialog_manager.h" +#include "atom/browser/browser.h" +#include "atom/browser/devtools_delegate.h" +#include "atom/browser/devtools_web_contents_observer.h" +#include "atom/browser/window_list.h" +#include "atom/common/api/api_messages.h" +#include "atom/common/atom_version.h" +#include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/prefs/pref_service.h" @@ -15,13 +26,6 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" -#include "atom/browser/api/atom_browser_bindings.h" -#include "atom/browser/atom_browser_context.h" -#include "atom/browser/atom_browser_main_parts.h" -#include "atom/browser/atom_javascript_dialog_manager.h" -#include "atom/browser/browser.h" -#include "atom/browser/devtools_delegate.h" -#include "atom/browser/window_list.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_entry.h" @@ -33,9 +37,6 @@ #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents_view.h" #include "content/public/common/renderer_preferences.h" -#include "atom/common/api/api_messages.h" -#include "atom/common/atom_version.h" -#include "atom/common/options_switches.h" #include "ipc/ipc_message_macros.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/point.h" @@ -194,8 +195,12 @@ bool NativeWindow::HasModalDialog() { void NativeWindow::OpenDevTools() { if (devtools_window_) { devtools_window_->Focus(true); + devtools_web_contents_observer_.reset(new DevToolsWebContentsObserver( + this, devtools_window_->GetWebContents())); } else { inspectable_web_contents()->ShowDevTools(); + devtools_web_contents_observer_.reset(new DevToolsWebContentsObserver( + this, GetDevToolsWebContents())); #if defined(OS_MACOSX) // Temporary fix for flashing devtools, try removing this after upgraded to // Chrome 32. @@ -559,7 +564,7 @@ void NativeWindow::OnRendererMessageSync(const string16& channel, GetWebContents()->GetRoutingID(), channel, args, - this, + GetWebContents(), reply_msg); } diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index c77549cb21..3028da12d5 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -41,14 +41,11 @@ class Rect; class Size; } -namespace IPC { -class Message; -} - namespace atom { class AtomJavaScriptDialogManager; class DevToolsDelegate; +class DevToolsWebContentsObserver; struct DraggableRegion; class NativeWindow : public brightray::DefaultWebContentsDelegate, @@ -288,8 +285,11 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, base::WeakPtrFactory weak_factory_; base::WeakPtr devtools_window_; - scoped_ptr devtools_delegate_; + + // WebContentsObserver for the WebContents of devtools. + scoped_ptr devtools_web_contents_observer_; + scoped_ptr dialog_manager_; scoped_ptr inspectable_web_contents_; From 86ebd6e8e3c58bfa26987b6c51bc76d1c284e9ff Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 4 Apr 2014 22:05:43 +0800 Subject: [PATCH 04/11] Allow sending ipc messages to devtools. --- atom.gyp | 2 + .../browser/devtools_web_contents_observer.cc | 64 +++++++++++++++++++ atom/browser/devtools_web_contents_observer.h | 41 ++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 atom/browser/devtools_web_contents_observer.cc create mode 100644 atom/browser/devtools_web_contents_observer.h diff --git a/atom.gyp b/atom.gyp index 621555149d..dd634957e3 100644 --- a/atom.gyp +++ b/atom.gyp @@ -94,6 +94,8 @@ 'atom/browser/browser_observer.h', 'atom/browser/devtools_delegate.cc', 'atom/browser/devtools_delegate.h', + 'atom/browser/devtools_web_contents_observer.cc', + 'atom/browser/devtools_web_contents_observer.h', 'atom/browser/native_window.cc', 'atom/browser/native_window.h', 'atom/browser/native_window_gtk.cc', diff --git a/atom/browser/devtools_web_contents_observer.cc b/atom/browser/devtools_web_contents_observer.cc new file mode 100644 index 0000000000..331a1253ca --- /dev/null +++ b/atom/browser/devtools_web_contents_observer.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "atom/browser/devtools_web_contents_observer.h" + +#include "atom/browser/api/atom_browser_bindings.h" +#include "atom/browser/atom_browser_main_parts.h" +#include "atom/browser/native_window.h" +#include "atom/common/api/api_messages.h" +#include "base/logging.h" +#include "content/public/browser/render_process_host.h" +#include "ipc/ipc_message_macros.h" + +namespace atom { + +DevToolsWebContentsObserver::DevToolsWebContentsObserver( + NativeWindow* native_window, + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + inspected_window_(native_window) { + DCHECK(native_window); +} + +DevToolsWebContentsObserver::~DevToolsWebContentsObserver() { +} + +bool DevToolsWebContentsObserver::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(DevToolsWebContentsObserver, message) + IPC_MESSAGE_HANDLER(AtomViewHostMsg_Message, OnRendererMessage) + IPC_MESSAGE_HANDLER_DELAY_REPLY(AtomViewHostMsg_Message_Sync, + OnRendererMessageSync) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void DevToolsWebContentsObserver::OnRendererMessage( + const string16& channel, + const base::ListValue& args) { + AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessage( + web_contents()->GetRenderProcessHost()->GetID(), + web_contents()->GetRoutingID(), + channel, + args); +} + +void DevToolsWebContentsObserver::OnRendererMessageSync( + const string16& channel, + const base::ListValue& args, + IPC::Message* reply_msg) { + AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessageSync( + web_contents()->GetRenderProcessHost()->GetID(), + web_contents()->GetRoutingID(), + channel, + args, + web_contents(), + reply_msg); +} + +} // namespace atom diff --git a/atom/browser/devtools_web_contents_observer.h b/atom/browser/devtools_web_contents_observer.h new file mode 100644 index 0000000000..3fc2222a43 --- /dev/null +++ b/atom/browser/devtools_web_contents_observer.h @@ -0,0 +1,41 @@ +// Copyright (c) 2014 GitHub, Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_DEVTOOLS_WEB_CONTENTS_OBSERVER_H_ +#define ATOM_BROWSER_DEVTOOLS_WEB_CONTENTS_OBSERVER_H_ + +#include "content/public/browser/web_contents_observer.h" + +namespace base { +class ListValue; +} + +namespace atom { + +class NativeWindow; + +class DevToolsWebContentsObserver : public content::WebContentsObserver { + public: + DevToolsWebContentsObserver(NativeWindow* native_window, + content::WebContents* web_contents); + virtual ~DevToolsWebContentsObserver(); + + protected: + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + void OnRendererMessage(const string16& channel, + const base::ListValue& args); + void OnRendererMessageSync(const string16& channel, + const base::ListValue& args, + IPC::Message* reply_msg); + + private: + NativeWindow* inspected_window_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_DEVTOOLS_WEB_CONTENTS_OBSERVER_H_ From f5fc26d8fca0b61b8b56127662ef17b7d4207fb6 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 4 Apr 2014 22:28:18 +0800 Subject: [PATCH 05/11] Enable getting a window according to its attached devtools. --- atom/browser/api/atom_api_window.cc | 15 +++++++++++++++ atom/browser/api/atom_api_window.h | 3 +++ atom/browser/api/lib/browser-window.coffee | 7 +++++++ 3 files changed, 25 insertions(+) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index e8a845ff6a..c12effeb35 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -503,6 +503,19 @@ void Window::IsCrashed(const v8::FunctionCallbackInfo& args) { args.GetReturnValue().Set(self->window_->GetWebContents()->IsCrashed()); } +// static +void Window::GetDevTools(const v8::FunctionCallbackInfo& args) { + UNWRAP_WINDOW_AND_CHECK; + + content::WebContents* web_contents = self->window_->GetDevToolsWebContents(); + v8::Local devtools = v8::Object::New(); + devtools->Set(ToV8Value("processId"), + ToV8Value(web_contents->GetRenderProcessHost()->GetID())); + devtools->Set(ToV8Value("routingId"), + ToV8Value(web_contents->GetRoutingID())); + args.GetReturnValue().Set(devtools); +} + // static void Window::LoadURL(const v8::FunctionCallbackInfo& args) { UNWRAP_WINDOW_AND_CHECK; @@ -686,6 +699,8 @@ void Window::Initialize(v8::Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "getProcessId", GetProcessID); NODE_SET_PROTOTYPE_METHOD(t, "isCrashed", IsCrashed); + NODE_SET_PROTOTYPE_METHOD(t, "getDevTools", GetDevTools); + NODE_SET_PROTOTYPE_METHOD(t, "loadUrl", LoadURL); NODE_SET_PROTOTYPE_METHOD(t, "getUrl", GetURL); NODE_SET_PROTOTYPE_METHOD(t, "canGoBack", CanGoBack); diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index bca32f2b3c..b16bb70a2e 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -103,6 +103,9 @@ class Window : public EventEmitter, static void GetProcessID(const v8::FunctionCallbackInfo& args); static void IsCrashed(const v8::FunctionCallbackInfo& args); + // APIs for devtools. + static void GetDevTools(const v8::FunctionCallbackInfo& args); + // APIs for NavigationController. static void LoadURL(const v8::FunctionCallbackInfo& args); static void GetURL(const v8::FunctionCallbackInfo& args); diff --git a/atom/browser/api/lib/browser-window.coffee b/atom/browser/api/lib/browser-window.coffee index 38946c5500..a359b28218 100644 --- a/atom/browser/api/lib/browser-window.coffee +++ b/atom/browser/api/lib/browser-window.coffee @@ -55,4 +55,11 @@ BrowserWindow.fromProcessIdAndRoutingId = (processId, routingId) -> return window for window in windows when window.getProcessId() == processId and window.getRoutingId() == routingId +BrowserWindow.fromDevTools = (processId, routingId) -> + windows = BrowserWindow.getAllWindows() + for window in windows + devtools = window.getDevTools() + return window if devtools.processId == processId and + devtools.routingId == routingId + module.exports = BrowserWindow From 03e6d564d776cd1d2018cdc33c99f41e4553da09 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 4 Apr 2014 22:28:47 +0800 Subject: [PATCH 06/11] Make remote.getCurrentWindow work for devtools. --- atom/browser/lib/rpc-server.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/atom/browser/lib/rpc-server.coffee b/atom/browser/lib/rpc-server.coffee index 6e072b6618..e10f875328 100644 --- a/atom/browser/lib/rpc-server.coffee +++ b/atom/browser/lib/rpc-server.coffee @@ -98,6 +98,7 @@ ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) -> try BrowserWindow = require 'browser-window' window = BrowserWindow.fromProcessIdAndRoutingId processId, routingId + window = BrowserWindow.fromDevTools processId, routingId unless window? event.returnValue = valueToMeta processId, routingId, window catch e event.returnValue = errorToMeta e From d4e7fe3eb8ec41d2425e05fbc88a21a06a2ca099 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 4 Apr 2014 22:37:34 +0800 Subject: [PATCH 07/11] Correctly translate WebInspector menu to native menu. --- atom/renderer/lib/inspector.coffee | 34 ++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/atom/renderer/lib/inspector.coffee b/atom/renderer/lib/inspector.coffee index 7030fdd369..b8aaebf3d4 100644 --- a/atom/renderer/lib/inspector.coffee +++ b/atom/renderer/lib/inspector.coffee @@ -10,21 +10,33 @@ window.onload = -> convertToMenuTemplate = (items) -> template = [] for item in items - if item.type is 'subMenu' - template.push - type: 'submenu' - label: item.label - submenu: convertToMenuTemplate item.subItems - else - template.push - type: 'normal' - label: item.label + do (item) -> + transformed = + if item.type is 'subMenu' + type: 'submenu' + label: item.label + enabled: item.enabled + submenu: convertToMenuTemplate item.subItems + else if item.type is 'separator' + type: 'separator' + else if item.type is 'checkbox' + type: 'checkbox' + label: item.label + enabled: item.enabled + checked: item.checked + else + type: 'normal' + label: item.label + enabled: item.enabled + if item.id? + transformed.click = -> WebInspector.contextMenuItemSelected item.id + template.push transformed template createMenu = (items, event) -> remote = require 'remote' Menu = remote.require 'menu' - menu = Menu.buildFromTemplate convertToMenuTemplate(items.subItems) - menu.popup() + menu = Menu.buildFromTemplate convertToMenuTemplate(items) + menu.popup remote.getCurrentWindow() event.consume true From 877277d837a3927bacf4cc32b6df5ca1c0bdd66f Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 5 Apr 2014 00:10:57 +0800 Subject: [PATCH 08/11] Enable writing files in devtools. --- atom/browser/native_window.cc | 61 +++++++++++++++++++++++++++++++++++ atom/browser/native_window.h | 15 +++++++++ vendor/brightray | 2 +- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 6f6f163a1e..f19bdf0823 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -15,12 +15,14 @@ #include "atom/browser/browser.h" #include "atom/browser/devtools_delegate.h" #include "atom/browser/devtools_web_contents_observer.h" +#include "atom/browser/ui/file_dialog.h" #include "atom/browser/window_list.h" #include "atom/common/api/api_messages.h" #include "atom/common/atom_version.h" #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/file_util.h" +#include "base/json/json_writer.h" #include "base/prefs/pref_service.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" @@ -519,6 +521,41 @@ bool NativeWindow::DevToolsShow(std::string* dock_side) { return false; } +void NativeWindow::DevToolsSaveToFile(const std::string& url, + const std::string& content, + bool save_as) { + base::FilePath path; + PathsMap::iterator it = saved_files_.find(url); + if (it != saved_files_.end() && !save_as) { + path = it->second; + } else { + if (!file_dialog::ShowSaveDialog(this, url, base::FilePath(url), &path)) + return; + } + + saved_files_[url] = path; + file_util::WriteFile(path, content.data(), content.size()); + + // Notify devtools. + base::StringValue url_value(url); + CallDevToolsFunction("InspectorFrontendAPI.savedURL", &url_value); + + // TODO(zcbenz): In later Chrome we need to call canceledSaveURL when the save + // failed. +} + +void NativeWindow::DevToolsAppendToFile(const std::string& url, + const std::string& content) { + PathsMap::iterator it = saved_files_.find(url); + if (it == saved_files_.end()) + return; + file_util::AppendToFile(it->second, content.data(), content.size()); + + // Notify devtools. + base::StringValue url_value(url); + CallDevToolsFunction("InspectorFrontendAPI.appendedToURL", &url_value); +} + void NativeWindow::ScheduleUnresponsiveEvent(int ms) { window_unresposive_closure_.Reset( base::Bind(&NativeWindow::NotifyWindowUnresponsive, @@ -547,6 +584,30 @@ void NativeWindow::OnCapturePageDone(const CapturePageCallback& callback, callback.Run(data); } +void NativeWindow::CallDevToolsFunction(const std::string& function_name, + const base::Value* arg1, + const base::Value* arg2, + const base::Value* arg3) { + std::string params; + if (arg1) { + std::string json; + base::JSONWriter::Write(arg1, &json); + params.append(json); + if (arg2) { + base::JSONWriter::Write(arg2, &json); + params.append(", " + json); + if (arg3) { + base::JSONWriter::Write(arg3, &json); + params.append(", " + json); + } + } + } + base::string16 javascript = + base::ASCIIToUTF16(function_name + "(" + params + ");"); + GetDevToolsWebContents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame( + string16(), javascript); +} + void NativeWindow::OnRendererMessage(const string16& channel, const base::ListValue& args) { AtomBrowserMainParts::Get()->atom_bindings()->OnRendererMessage( diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 3028da12d5..acfd3ca546 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -237,6 +237,11 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, virtual bool DevToolsSetDockSide(const std::string& dock_side, bool* succeed) OVERRIDE; virtual bool DevToolsShow(std::string* dock_side) OVERRIDE; + virtual void DevToolsSaveToFile(const std::string& url, + const std::string& content, + bool save_as) OVERRIDE; + virtual void DevToolsAppendToFile(const std::string& url, + const std::string& content) OVERRIDE; // Whether window has standard frame. bool has_frame_; @@ -251,6 +256,12 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, // Dispatch unresponsive event to observers. void NotifyWindowUnresponsive(); + // Call a function in devtools. + void CallDevToolsFunction(const std::string& function_name, + const base::Value* arg1 = NULL, + const base::Value* arg2 = NULL, + const base::Value* arg3 = NULL); + // Called when CapturePage has done. void OnCapturePageDone(const CapturePageCallback& callback, bool succeed, @@ -293,6 +304,10 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, scoped_ptr dialog_manager_; scoped_ptr inspectable_web_contents_; + // Maps url to file path, used by the file requests sent from devtools. + typedef std::map PathsMap; + PathsMap saved_files_; + DISALLOW_COPY_AND_ASSIGN(NativeWindow); }; diff --git a/vendor/brightray b/vendor/brightray index 794cc6b6a6..dab731f232 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit 794cc6b6a6d269eacce0bbaf930d10f642e7a907 +Subproject commit dab731f2329285a63c0c2cff30793a34fde91c39 From 7a38307d1f8b65b3199b71a577f719360cb361da Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 5 Apr 2014 10:24:46 +0800 Subject: [PATCH 09/11] Use dialog API to override file chooser dialog in web inspector. --- atom/renderer/lib/inspector.coffee | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/atom/renderer/lib/inspector.coffee b/atom/renderer/lib/inspector.coffee index b8aaebf3d4..7a07d51dc0 100644 --- a/atom/renderer/lib/inspector.coffee +++ b/atom/renderer/lib/inspector.coffee @@ -1,5 +1,5 @@ -# Use menu API to show context menu. window.onload = -> + # Use menu API to show context menu. WebInspector.ContextMenu.prototype.show = -> menuObject = @_buildDescriptor() if menuObject.length @@ -7,6 +7,13 @@ window.onload = -> createMenu(menuObject, @_event) @_event.consume() + # Use dialog API to override file chooser dialog. + WebInspector.createFileSelectorElement = (callback) -> + fileSelectorElement = document.createElement 'span' + fileSelectorElement.style.display = 'none' + fileSelectorElement.click = showFileChooserDialog.bind this, callback + return fileSelectorElement + convertToMenuTemplate = (items) -> template = [] for item in items @@ -40,3 +47,15 @@ createMenu = (items, event) -> menu = Menu.buildFromTemplate convertToMenuTemplate(items) menu.popup remote.getCurrentWindow() event.consume true + +showFileChooserDialog = (callback) -> + remote = require 'remote' + dialog = remote.require 'dialog' + dialog.showOpenDialog remote.getCurrentWindow(), null, (files) -> + callback pathToHtml5FileObject(files[0]) if files? + +pathToHtml5FileObject = (path) -> + fs = require 'fs' + blob = new Blob([fs.readFileSync(path)]) + blob.name = path + blob From ff88535cd5ff877025b5b356df74295410034301 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 5 Apr 2014 11:00:35 +0800 Subject: [PATCH 10/11] :lipstick: Fix cpplint warning build/include_what_you_use. --- atom/browser/native_window.h | 1 + 1 file changed, 1 insertion(+) diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index acfd3ca546..c274d5e448 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -5,6 +5,7 @@ #ifndef ATOM_BROWSER_NATIVE_WINDOW_H_ #define ATOM_BROWSER_NATIVE_WINDOW_H_ +#include #include #include From 2369f6cc4154646fb96100977d462bac3a29b255 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 5 Apr 2014 11:05:51 +0800 Subject: [PATCH 11/11] Don't build symbols in CI. --- script/cibuild | 1 - 1 file changed, 1 deletion(-) diff --git a/script/cibuild b/script/cibuild index 1ce51d436d..f400b4e0c8 100755 --- a/script/cibuild +++ b/script/cibuild @@ -27,7 +27,6 @@ def main(): run_script('coffeelint.py') run_script('build.py') run_script('test.py', ['--ci']) - run_script('create-dist.py') def run_script(script, args=[]):