From 2b547bd44a8eca9bd2d0c7872f0a49906d808af7 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 17 Feb 2016 22:33:27 +0530 Subject: [PATCH 1/4] webContents: provide responses for executeJavscript method --- atom/browser/api/lib/ipc-main.js | 4 ++ atom/browser/api/lib/web-contents.js | 14 ++++++- .../native_mate_converters/blink_converter.h | 14 +++++++ atom/renderer/api/atom_api_web_frame.cc | 41 +++++++++++++++++-- atom/renderer/lib/init.js | 10 +++++ atom/renderer/lib/web-view/web-view.js | 17 +++++--- docs/api/web-contents.md | 4 +- docs/api/web-view-tag.md | 4 +- spec/webview-spec.js | 18 +++++++- 9 files changed, 113 insertions(+), 13 deletions(-) diff --git a/atom/browser/api/lib/ipc-main.js b/atom/browser/api/lib/ipc-main.js index e253e03eaa..d6a043a92b 100644 --- a/atom/browser/api/lib/ipc-main.js +++ b/atom/browser/api/lib/ipc-main.js @@ -1,3 +1,7 @@ const EventEmitter = require('events').EventEmitter; module.exports = new EventEmitter; + +// Every webContents would add a listenter to the +// WEB_FRAME_RESPONSE event, so ignore the listenters warning. +module.exports.setMaxListeners(0); diff --git a/atom/browser/api/lib/web-contents.js b/atom/browser/api/lib/web-contents.js index e0c16999c7..c2376af53a 100644 --- a/atom/browser/api/lib/web-contents.js +++ b/atom/browser/api/lib/web-contents.js @@ -11,6 +11,7 @@ const debuggerBinding = process.atomBinding('debugger'); let slice = [].slice; let nextId = 0; +let responseCallback = {}; let getNextId = function() { return ++nextId; @@ -109,13 +110,24 @@ let wrapWebContents = function(webContents) { // Make sure webContents.executeJavaScript would run the code only when the // webContents has been loaded. const executeJavaScript = webContents.executeJavaScript; - webContents.executeJavaScript = function(code, hasUserGesture) { + webContents.executeJavaScript = function(code, hasUserGesture, callback) { + if (typeof hasUserGesture === "function") { + callback = hasUserGesture; + hasUserGesture = false; + } + if (callback !== null) + responseCallback["executeJavaScript"] = callback; if (this.getURL() && !this.isLoading()) return executeJavaScript.call(this, code, hasUserGesture); else return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture)); }; + ipcMain.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_RESPONSE', function(event, method, result) { + if (responseCallback[method]) + responseCallback[method].apply(null, [result]); + }); + // Dispatch IPC messages to the ipc module. webContents.on('ipc-message', function(event, packed) { var args, channel; diff --git a/atom/common/native_mate_converters/blink_converter.h b/atom/common/native_mate_converters/blink_converter.h index 6a36019292..f066ea29cc 100644 --- a/atom/common/native_mate_converters/blink_converter.h +++ b/atom/common/native_mate_converters/blink_converter.h @@ -6,6 +6,7 @@ #define ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_ #include "native_mate/converter.h" +#include "third_party/WebKit/public/platform/WebVector.h" namespace blink { class WebInputEvent; @@ -87,6 +88,19 @@ struct Converter { blink::WebFindOptions* out); }; +template +struct Converter > { + static v8::Local ToV8(v8::Isolate* isolate, + const blink::WebVector& val) { + v8::Local result( + MATE_ARRAY_NEW(isolate, static_cast(val.size()))); + for (size_t i = 0; i < val.size(); ++i) { + result->Set(static_cast(i), Converter::ToV8(isolate, val[i])); + } + return result; + } +}; + } // namespace mate #endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_ diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index c72882886b..06c9621b82 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -8,6 +8,7 @@ #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/gfx_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" +#include "atom/common/native_mate_converters/blink_converter.h" #include "atom/renderer/api/atom_api_spell_check_client.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" @@ -15,7 +16,7 @@ #include "native_mate/object_template_builder.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" -#include "third_party/WebKit/public/web/WebScopedUserGesture.h" +#include "third_party/WebKit/public/web/WebScriptExecutionCallback.h" #include "third_party/WebKit/public/web/WebScriptSource.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "third_party/WebKit/public/web/WebView.h" @@ -26,6 +27,33 @@ namespace atom { namespace api { +namespace { + +class ScriptExecutionCallback : public blink::WebScriptExecutionCallback { + public: + using CompletionCallback = + base::Callback>& result)>; + + explicit ScriptExecutionCallback(const CompletionCallback& callback) + : callback_(callback) {} + ~ScriptExecutionCallback() {} + + void completed( + const blink::WebVector>& result) override { + if (!callback_.is_null()) + callback_.Run(result); + delete this; + } + + private: + CompletionCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(ScriptExecutionCallback); +}; + +} // namespace + WebFrame::WebFrame() : web_frame_(blink::WebLocalFrame::frameForCurrentContext()) { } @@ -124,9 +152,14 @@ void WebFrame::ExecuteJavaScript(const base::string16& code, mate::Arguments* args) { bool has_user_gesture = false; args->GetNext(&has_user_gesture); - scoped_ptr gesture( - has_user_gesture ? new blink::WebScopedUserGesture : nullptr); - web_frame_->executeScriptAndReturnValue(blink::WebScriptSource(code)); + ScriptExecutionCallback::CompletionCallback completion_callback; + args->GetNext(&completion_callback); + scoped_ptr callback( + new ScriptExecutionCallback(completion_callback)); + web_frame_->requestExecuteScriptAndReturnValue( + blink::WebScriptSource(code), + has_user_gesture, + callback.release()); } mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder( diff --git a/atom/renderer/lib/init.js b/atom/renderer/lib/init.js index 340a1ef505..ab03a9055c 100644 --- a/atom/renderer/lib/init.js +++ b/atom/renderer/lib/init.js @@ -32,7 +32,17 @@ v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter); const electron = require('electron'); // Call webFrame method. +const asyncWebFrameMethods = [ + 'executeJavaScript' +]; + electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => { + if (asyncWebFrameMethods.includes(method)) { + const responseCallback = function(result) { + event.sender.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_RESPONSE', method, result); + }; + args.push(responseCallback); + } electron.webFrame[method].apply(electron.webFrame, args); }); diff --git a/atom/renderer/lib/web-view/web-view.js b/atom/renderer/lib/web-view/web-view.js index 44fc104216..892ecf5619 100644 --- a/atom/renderer/lib/web-view/web-view.js +++ b/atom/renderer/lib/web-view/web-view.js @@ -307,7 +307,7 @@ var registerBrowserPluginElement = function() { // Registers custom element. var registerWebViewElement = function() { - var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, proto; + var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, webFrameMethods, proto; proto = Object.create(HTMLObjectElement.prototype); proto.createdCallback = function() { return new WebViewImpl(this); @@ -391,14 +391,16 @@ var registerWebViewElement = function() { 'printToPDF', ]; nonblockMethods = [ - 'executeJavaScript', 'insertCSS', - 'insertText', 'send', - 'sendInputEvent', + 'sendInputEvent' + ]; + webFrameMethods = [ + 'executeJavaScript', + 'insertText', 'setZoomFactor', 'setZoomLevel', - 'setZoomLevelLimits', + 'setZoomLevelLimits' ]; // Forward proto.foo* method calls to WebViewImpl.foo*. @@ -430,6 +432,11 @@ var registerWebViewElement = function() { proto[m] = createNonBlockHandler(m); } + // Forward proto.foo* webframe method calls to WebFrame.foo*. + for (let method of webFrameMethods) { + proto[method] = webFrame[method].bind(webFrame); + } + // WebContents associated with this webview. proto.getWebContents = function() { var internal = v8Util.getHiddenValue(this, 'internal'); diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index dde701d7f4..376860b17f 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -425,10 +425,12 @@ Returns a `String` representing the user agent for this web page. Injects CSS into the current web page. -### `webContents.executeJavaScript(code[, userGesture])` +### `webContents.executeJavaScript(code[, userGesture, callback])` * `code` String * `userGesture` Boolean (optional) +* `callback` Function (optional) - Called after script has been executed. + * `result` Array Evaluates `code` in page. diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 4a0697ad84..3bc4f0e9f6 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -279,10 +279,12 @@ Returns a `String` representing the user agent for guest page. Injects CSS into the guest page. -### `.executeJavaScript(code, userGesture)` +### `.executeJavaScript(code, userGesture, callback)` * `code` String * `userGesture` Boolean - Default `false`. +* `callback` Function (optional) - Called after script has been executed. + * `result` Array Evaluates `code` in page. If `userGesture` is set, it will create the user gesture context in the page. HTML APIs like `requestFullScreen`, which require diff --git a/spec/webview-spec.js b/spec/webview-spec.js index d73a177d09..fef49de454 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -194,6 +194,7 @@ describe(' tag', function() { document.body.appendChild(webview); }); }); + describe('partition attribute', function() { it('inserts no node symbols when not set', function(done) { webview.addEventListener('console-message', function(e) { @@ -356,6 +357,7 @@ describe(' tag', function() { document.body.appendChild(webview); }); }); + describe('did-navigate-in-page event', function() { it('emits when an anchor link is clicked', function(done) { var p = path.join(fixtures, 'pages', 'webview-did-navigate-in-page.html'); @@ -556,7 +558,7 @@ describe(' tag', function() { done(); }; var listener2 = function() { - var jsScript = 'document.getElementsByTagName("video")[0].webkitRequestFullScreen()'; + var jsScript = "document.querySelector('video').webkitRequestFullscreen()"; webview.executeJavaScript(jsScript, true); webview.removeEventListener('did-finish-load', listener2); }; @@ -565,6 +567,20 @@ describe(' tag', function() { webview.src = "file://" + fixtures + "/pages/fullscreen.html"; document.body.appendChild(webview); }); + + it('can return the result of the executed script', function(done) { + var listener = function() { + var jsScript = "'4'+2"; + webview.executeJavaScript(jsScript, false, function(result) { + assert.equal(result[0], '42'); + done(); + }); + webview.removeEventListener('did-finish-load', listener); + }; + webview.addEventListener('did-finish-load', listener); + webview.src = "about:blank"; + document.body.appendChild(webview); + }); }); describe('sendInputEvent', function() { From a734326907d4a23c328cf602dabe62d677ec695b Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 22 Feb 2016 19:30:21 +0530 Subject: [PATCH 2/4] track async api requests --- atom/browser/api/lib/web-contents.js | 29 ++++++++++++++----- .../native_mate_converters/blink_converter.h | 14 --------- atom/renderer/api/atom_api_web_frame.cc | 6 ++-- atom/renderer/lib/init.js | 19 ++++++------ atom/renderer/lib/web-view/web-view.js | 4 +-- docs/api/web-contents.md | 2 +- docs/api/web-view-tag.md | 2 +- spec/webview-spec.js | 2 +- 8 files changed, 39 insertions(+), 39 deletions(-) diff --git a/atom/browser/api/lib/web-contents.js b/atom/browser/api/lib/web-contents.js index c2376af53a..ed8afef7a5 100644 --- a/atom/browser/api/lib/web-contents.js +++ b/atom/browser/api/lib/web-contents.js @@ -11,6 +11,8 @@ const debuggerBinding = process.atomBinding('debugger'); let slice = [].slice; let nextId = 0; + +// Map of requestId and response callback. let responseCallback = {}; let getNextId = function() { @@ -59,13 +61,16 @@ let PDFPageSize = { // Following methods are mapped to webFrame. const webFrameMethods = [ - 'executeJavaScript', 'insertText', 'setZoomFactor', 'setZoomLevel', 'setZoomLevelLimits' ]; +const asyncWebFrameMethods = [ + 'executeJavaScript', +]; + let wrapWebContents = function(webContents) { // webContents is an EventEmitter. var controller, method, name, ref1; @@ -107,25 +112,35 @@ let wrapWebContents = function(webContents) { }; } + for (let method of asyncWebFrameMethods) { + webContents[method] = function() { + let args = Array.prototype.slice.call(arguments); + this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', method, args); + }; + } + // Make sure webContents.executeJavaScript would run the code only when the // webContents has been loaded. const executeJavaScript = webContents.executeJavaScript; webContents.executeJavaScript = function(code, hasUserGesture, callback) { + let requestId = getNextId(); if (typeof hasUserGesture === "function") { callback = hasUserGesture; hasUserGesture = false; } if (callback !== null) - responseCallback["executeJavaScript"] = callback; + responseCallback[requestId] = callback; if (this.getURL() && !this.isLoading()) - return executeJavaScript.call(this, code, hasUserGesture); + return executeJavaScript.call(this, requestId, code, hasUserGesture); else - return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture)); + return this.once('did-finish-load', executeJavaScript.bind(this, requestId, code, hasUserGesture)); }; - ipcMain.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_RESPONSE', function(event, method, result) { - if (responseCallback[method]) - responseCallback[method].apply(null, [result]); + ipcMain.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE', function(event, id, result) { + if (responseCallback[id]) { + responseCallback[id].apply(null, [result]); + delete responseCallback[id]; + } }); // Dispatch IPC messages to the ipc module. diff --git a/atom/common/native_mate_converters/blink_converter.h b/atom/common/native_mate_converters/blink_converter.h index f066ea29cc..6a36019292 100644 --- a/atom/common/native_mate_converters/blink_converter.h +++ b/atom/common/native_mate_converters/blink_converter.h @@ -6,7 +6,6 @@ #define ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_ #include "native_mate/converter.h" -#include "third_party/WebKit/public/platform/WebVector.h" namespace blink { class WebInputEvent; @@ -88,19 +87,6 @@ struct Converter { blink::WebFindOptions* out); }; -template -struct Converter > { - static v8::Local ToV8(v8::Isolate* isolate, - const blink::WebVector& val) { - v8::Local result( - MATE_ARRAY_NEW(isolate, static_cast(val.size()))); - for (size_t i = 0; i < val.size(); ++i) { - result->Set(static_cast(i), Converter::ToV8(isolate, val[i])); - } - return result; - } -}; - } // namespace mate #endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_ diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index 06c9621b82..af9a3c4162 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -8,7 +8,6 @@ #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/gfx_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" -#include "atom/common/native_mate_converters/blink_converter.h" #include "atom/renderer/api/atom_api_spell_check_client.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" @@ -33,7 +32,7 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback { public: using CompletionCallback = base::Callback>& result)>; + const v8::Local& result)>; explicit ScriptExecutionCallback(const CompletionCallback& callback) : callback_(callback) {} @@ -42,7 +41,8 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback { void completed( const blink::WebVector>& result) override { if (!callback_.is_null()) - callback_.Run(result); + // Right now only single results per frame is supported. + callback_.Run(result[0]); delete this; } diff --git a/atom/renderer/lib/init.js b/atom/renderer/lib/init.js index ab03a9055c..f7f78e84a1 100644 --- a/atom/renderer/lib/init.js +++ b/atom/renderer/lib/init.js @@ -32,17 +32,16 @@ v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter); const electron = require('electron'); // Call webFrame method. -const asyncWebFrameMethods = [ - 'executeJavaScript' -]; - electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => { - if (asyncWebFrameMethods.includes(method)) { - const responseCallback = function(result) { - event.sender.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_RESPONSE', method, result); - }; - args.push(responseCallback); - } + electron.webFrame[method].apply(electron.webFrame, args); +}); + +electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, method, args) => { + let requestId = args.shift(); + const responseCallback = function(result) { + event.sender.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE', requestId, result); + }; + args.push(responseCallback); electron.webFrame[method].apply(electron.webFrame, args); }); diff --git a/atom/renderer/lib/web-view/web-view.js b/atom/renderer/lib/web-view/web-view.js index 892ecf5619..25e5eead2d 100644 --- a/atom/renderer/lib/web-view/web-view.js +++ b/atom/renderer/lib/web-view/web-view.js @@ -393,14 +393,14 @@ var registerWebViewElement = function() { nonblockMethods = [ 'insertCSS', 'send', - 'sendInputEvent' + 'sendInputEvent', ]; webFrameMethods = [ 'executeJavaScript', 'insertText', 'setZoomFactor', 'setZoomLevel', - 'setZoomLevelLimits' + 'setZoomLevelLimits', ]; // Forward proto.foo* method calls to WebViewImpl.foo*. diff --git a/docs/api/web-contents.md b/docs/api/web-contents.md index 376860b17f..e095213bd9 100644 --- a/docs/api/web-contents.md +++ b/docs/api/web-contents.md @@ -430,7 +430,7 @@ Injects CSS into the current web page. * `code` String * `userGesture` Boolean (optional) * `callback` Function (optional) - Called after script has been executed. - * `result` Array + * `result` Evaluates `code` in page. diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index 3bc4f0e9f6..fa48ef60f3 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -284,7 +284,7 @@ Injects CSS into the guest page. * `code` String * `userGesture` Boolean - Default `false`. * `callback` Function (optional) - Called after script has been executed. - * `result` Array + * `result` Evaluates `code` in page. If `userGesture` is set, it will create the user gesture context in the page. HTML APIs like `requestFullScreen`, which require diff --git a/spec/webview-spec.js b/spec/webview-spec.js index fef49de454..cd2be69d73 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -572,7 +572,7 @@ describe(' tag', function() { var listener = function() { var jsScript = "'4'+2"; webview.executeJavaScript(jsScript, false, function(result) { - assert.equal(result[0], '42'); + assert.equal(result, '42'); done(); }); webview.removeEventListener('did-finish-load', listener); From 8386baf267545157584588f244bdfa8a75652555 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 24 Feb 2016 15:41:09 +0530 Subject: [PATCH 3/4] add spec --- atom/browser/api/lib/ipc-main.js | 4 ---- atom/browser/api/lib/web-contents.js | 33 ++++++++++------------------ atom/renderer/lib/init.js | 5 ++--- spec/api-browser-window-spec.js | 19 ++++++++++++++++ spec/static/main.js | 11 ++++++++++ 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/atom/browser/api/lib/ipc-main.js b/atom/browser/api/lib/ipc-main.js index d6a043a92b..e253e03eaa 100644 --- a/atom/browser/api/lib/ipc-main.js +++ b/atom/browser/api/lib/ipc-main.js @@ -1,7 +1,3 @@ const EventEmitter = require('events').EventEmitter; module.exports = new EventEmitter; - -// Every webContents would add a listenter to the -// WEB_FRAME_RESPONSE event, so ignore the listenters warning. -module.exports.setMaxListeners(0); diff --git a/atom/browser/api/lib/web-contents.js b/atom/browser/api/lib/web-contents.js index ed8afef7a5..7cd54bfddf 100644 --- a/atom/browser/api/lib/web-contents.js +++ b/atom/browser/api/lib/web-contents.js @@ -67,10 +67,6 @@ const webFrameMethods = [ 'setZoomLevelLimits' ]; -const asyncWebFrameMethods = [ - 'executeJavaScript', -]; - let wrapWebContents = function(webContents) { // webContents is an EventEmitter. var controller, method, name, ref1; @@ -112,37 +108,32 @@ let wrapWebContents = function(webContents) { }; } - for (let method of asyncWebFrameMethods) { - webContents[method] = function() { - let args = Array.prototype.slice.call(arguments); - this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', method, args); - }; - } + const asyncWebFrameMethods = function(requestId, method, ...args) { + this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args); + ipcMain.once('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE_' + requestId, function(event, result) { + if (responseCallback[requestId]) { + responseCallback[requestId](result); + delete responseCallback[requestId]; + } + }); + }; // Make sure webContents.executeJavaScript would run the code only when the // webContents has been loaded. - const executeJavaScript = webContents.executeJavaScript; webContents.executeJavaScript = function(code, hasUserGesture, callback) { let requestId = getNextId(); if (typeof hasUserGesture === "function") { callback = hasUserGesture; hasUserGesture = false; } - if (callback !== null) + if (callback != null) responseCallback[requestId] = callback; if (this.getURL() && !this.isLoading()) - return executeJavaScript.call(this, requestId, code, hasUserGesture); + return asyncWebFrameMethods.call(this, requestId, "executeJavaScript", code, hasUserGesture); else - return this.once('did-finish-load', executeJavaScript.bind(this, requestId, code, hasUserGesture)); + return this.once('did-finish-load', asyncWebFrameMethods.bind(this, requestId, "executeJavaScript", code, hasUserGesture)); }; - ipcMain.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE', function(event, id, result) { - if (responseCallback[id]) { - responseCallback[id].apply(null, [result]); - delete responseCallback[id]; - } - }); - // Dispatch IPC messages to the ipc module. webContents.on('ipc-message', function(event, packed) { var args, channel; diff --git a/atom/renderer/lib/init.js b/atom/renderer/lib/init.js index f7f78e84a1..cf42aa2722 100644 --- a/atom/renderer/lib/init.js +++ b/atom/renderer/lib/init.js @@ -36,10 +36,9 @@ electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, m electron.webFrame[method].apply(electron.webFrame, args); }); -electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, method, args) => { - let requestId = args.shift(); +electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { const responseCallback = function(result) { - event.sender.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE', requestId, result); + event.sender.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE_' + requestId, result); }; args.push(responseCallback); electron.webFrame[method].apply(electron.webFrame, args); diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index 58cf57cc8d..25e92382cc 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -10,6 +10,7 @@ const screen = require('electron').screen; const app = remote.require('electron').app; const ipcMain = remote.require('electron').ipcMain; +const ipcRenderer = require('electron').ipcRenderer; const BrowserWindow = remote.require('electron').BrowserWindow; const isCI = remote.getGlobal('isCi'); @@ -690,4 +691,22 @@ describe('browser-window module', function() { assert.equal(fs.existsSync(serializedPath), false); }); }); + + describe('window.webContents.executeJavaScript', function() { + var expected = 'hello, world!'; + var code = '(() => \"' + expected + '\")()'; + + it('doesnt throw when no calback is provided', function() { + const result = ipcRenderer.sendSync('executeJavaScript', code, false); + assert.equal(result, 'success'); + }); + + it('returns result when calback is provided', function(done) { + ipcRenderer.send('executeJavaScript', code, true); + ipcRenderer.once('executeJavaScript-response', function(event, result) { + assert.equal(result, expected); + done(); + }); + }); + }); }); diff --git a/spec/static/main.js b/spec/static/main.js index 48fdf17c3d..9a049e3f10 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -138,4 +138,15 @@ app.on('ready', function() { }); event.returnValue = "done"; }); + + ipcMain.on('executeJavaScript', function(event, code, hasCallback) { + if (hasCallback) { + window.webContents.executeJavaScript(code, (result) => { + window.webContents.send('executeJavaScript-response', result); + }); + } else { + window.webContents.executeJavaScript(code); + event.returnValue = "success"; + } + }); }); From 12359078357f0d0227864a9700e660a673f764e3 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 25 Feb 2016 23:35:01 +0530 Subject: [PATCH 4/4] run webframe methods for webview in its context --- atom/browser/api/lib/web-contents.js | 19 ++++++------------ atom/browser/lib/rpc-server.js | 10 ++++++++-- atom/renderer/api/atom_api_web_frame.cc | 2 +- atom/renderer/lib/init.js | 2 +- atom/renderer/lib/web-view/web-view.js | 26 +++++++++++++++---------- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/atom/browser/api/lib/web-contents.js b/atom/browser/api/lib/web-contents.js index 7cd54bfddf..9486378264 100644 --- a/atom/browser/api/lib/web-contents.js +++ b/atom/browser/api/lib/web-contents.js @@ -12,9 +12,6 @@ const debuggerBinding = process.atomBinding('debugger'); let slice = [].slice; let nextId = 0; -// Map of requestId and response callback. -let responseCallback = {}; - let getNextId = function() { return ++nextId; }; @@ -108,13 +105,11 @@ let wrapWebContents = function(webContents) { }; } - const asyncWebFrameMethods = function(requestId, method, ...args) { + const asyncWebFrameMethods = function(requestId, method, callback, ...args) { this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args); - ipcMain.once('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE_' + requestId, function(event, result) { - if (responseCallback[requestId]) { - responseCallback[requestId](result); - delete responseCallback[requestId]; - } + ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function(event, result) { + if (callback) + callback(result); }); }; @@ -126,12 +121,10 @@ let wrapWebContents = function(webContents) { callback = hasUserGesture; hasUserGesture = false; } - if (callback != null) - responseCallback[requestId] = callback; if (this.getURL() && !this.isLoading()) - return asyncWebFrameMethods.call(this, requestId, "executeJavaScript", code, hasUserGesture); + return asyncWebFrameMethods.call(this, requestId, "executeJavaScript", callback, code, hasUserGesture); else - return this.once('did-finish-load', asyncWebFrameMethods.bind(this, requestId, "executeJavaScript", code, hasUserGesture)); + return this.once('did-finish-load', asyncWebFrameMethods.bind(this, requestId, "executeJavaScript", callback, code, hasUserGesture)); }; // Dispatch IPC messages to the ipc module. diff --git a/atom/browser/lib/rpc-server.js b/atom/browser/lib/rpc-server.js index 20ee8fbdea..a31c6e146e 100644 --- a/atom/browser/lib/rpc-server.js +++ b/atom/browser/lib/rpc-server.js @@ -354,11 +354,17 @@ ipcMain.on('ATOM_BROWSER_GUEST_WEB_CONTENTS', function(event, guestInstanceId) { } }); -ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function(event, guestInstanceId, method, ...args) { +ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function(event, requestId, guestInstanceId, method, ...args) { try { let guestViewManager = require('./guest-view-manager'); let guest = guestViewManager.getGuest(guestInstanceId); - return guest[method].apply(guest, args); + if (requestId) { + const responseCallback = function(result) { + event.sender.send(`ATOM_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, result); + }; + args.push(responseCallback); + } + guest[method].apply(guest, args); } catch (error) { return event.returnValue = exceptionToMeta(error); } diff --git a/atom/renderer/api/atom_api_web_frame.cc b/atom/renderer/api/atom_api_web_frame.cc index af9a3c4162..e00b901bff 100644 --- a/atom/renderer/api/atom_api_web_frame.cc +++ b/atom/renderer/api/atom_api_web_frame.cc @@ -40,7 +40,7 @@ class ScriptExecutionCallback : public blink::WebScriptExecutionCallback { void completed( const blink::WebVector>& result) override { - if (!callback_.is_null()) + if (!callback_.is_null() && !result.isEmpty() && !result[0].IsEmpty()) // Right now only single results per frame is supported. callback_.Run(result[0]); delete this; diff --git a/atom/renderer/lib/init.js b/atom/renderer/lib/init.js index cf42aa2722..166e64237d 100644 --- a/atom/renderer/lib/init.js +++ b/atom/renderer/lib/init.js @@ -38,7 +38,7 @@ electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, m electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => { const responseCallback = function(result) { - event.sender.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_RESPONSE_' + requestId, result); + event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, result); }; args.push(responseCallback); electron.webFrame[method].apply(electron.webFrame, args); diff --git a/atom/renderer/lib/web-view/web-view.js b/atom/renderer/lib/web-view/web-view.js index 25e5eead2d..bd128f6fff 100644 --- a/atom/renderer/lib/web-view/web-view.js +++ b/atom/renderer/lib/web-view/web-view.js @@ -307,7 +307,7 @@ var registerBrowserPluginElement = function() { // Registers custom element. var registerWebViewElement = function() { - var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, webFrameMethods, proto; + var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, proto; proto = Object.create(HTMLObjectElement.prototype); proto.createdCallback = function() { return new WebViewImpl(this); @@ -392,12 +392,9 @@ var registerWebViewElement = function() { ]; nonblockMethods = [ 'insertCSS', + 'insertText', 'send', 'sendInputEvent', - ]; - webFrameMethods = [ - 'executeJavaScript', - 'insertText', 'setZoomFactor', 'setZoomLevel', 'setZoomLevelLimits', @@ -424,7 +421,7 @@ var registerWebViewElement = function() { var args, internal; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; internal = v8Util.getHiddenValue(this, 'internal'); - return ipcRenderer.send.apply(ipcRenderer, ['ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', internal.guestInstanceId, m].concat(slice.call(args))); + return ipcRenderer.send.apply(ipcRenderer, ['ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', null, internal.guestInstanceId, m].concat(slice.call(args))); }; }; for (j = 0, len1 = nonblockMethods.length; j < len1; j++) { @@ -432,10 +429,19 @@ var registerWebViewElement = function() { proto[m] = createNonBlockHandler(m); } - // Forward proto.foo* webframe method calls to WebFrame.foo*. - for (let method of webFrameMethods) { - proto[method] = webFrame[method].bind(webFrame); - } + proto.executeJavaScript = function(code, hasUserGesture, callback) { + var internal = v8Util.getHiddenValue(this, 'internal'); + if (typeof hasUserGesture === "function") { + callback = hasUserGesture; + hasUserGesture = false; + } + let requestId = getNextId(); + ipcRenderer.send('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, internal.guestInstanceId, "executeJavaScript", code, hasUserGesture); + ipcRenderer.once(`ATOM_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function(event, result) { + if (callback) + callback(result); + }); + }; // WebContents associated with this webview. proto.getWebContents = function() {