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);
let slice = [].slice;
let nextId = 0;
+let responseCallback = {};
let getNextId = function() {
return ++nextId;
// 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;
#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;
blink::WebFindOptions* out);
};
+template<typename T>
+struct Converter<blink::WebVector<T> > {
+ static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
+ const blink::WebVector<T>& val) {
+ v8::Local<v8::Array> result(
+ MATE_ARRAY_NEW(isolate, static_cast<int>(val.size())));
+ for (size_t i = 0; i < val.size(); ++i) {
+ result->Set(static_cast<int>(i), Converter<T>::ToV8(isolate, val[i]));
+ }
+ return result;
+ }
+};
+
} // namespace mate
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_BLINK_CONVERTER_H_
#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"
#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"
namespace api {
+namespace {
+
+class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
+ public:
+ using CompletionCallback =
+ base::Callback<void(
+ const blink::WebVector<v8::Local<v8::Value>>& result)>;
+
+ explicit ScriptExecutionCallback(const CompletionCallback& callback)
+ : callback_(callback) {}
+ ~ScriptExecutionCallback() {}
+
+ void completed(
+ const blink::WebVector<v8::Local<v8::Value>>& 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()) {
}
mate::Arguments* args) {
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
- scoped_ptr<blink::WebScopedUserGesture> gesture(
- has_user_gesture ? new blink::WebScopedUserGesture : nullptr);
- web_frame_->executeScriptAndReturnValue(blink::WebScriptSource(code));
+ ScriptExecutionCallback::CompletionCallback completion_callback;
+ args->GetNext(&completion_callback);
+ scoped_ptr<blink::WebScriptExecutionCallback> callback(
+ new ScriptExecutionCallback(completion_callback));
+ web_frame_->requestExecuteScriptAndReturnValue(
+ blink::WebScriptSource(code),
+ has_user_gesture,
+ callback.release());
}
mate::ObjectTemplateBuilder WebFrame::GetObjectTemplateBuilder(
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);
});
// Registers <webview> 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);
'printToPDF',
];
nonblockMethods = [
- 'executeJavaScript',
'insertCSS',
- 'insertText',
'send',
- 'sendInputEvent',
+ 'sendInputEvent'
+ ];
+ webFrameMethods = [
+ 'executeJavaScript',
+ 'insertText',
'setZoomFactor',
'setZoomLevel',
- 'setZoomLevelLimits',
+ 'setZoomLevelLimits'
];
// Forward proto.foo* method calls to WebViewImpl.foo*.
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');
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.
Injects CSS into the guest page.
-### `<webview>.executeJavaScript(code, userGesture)`
+### `<webview>.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
document.body.appendChild(webview);
});
});
+
describe('partition attribute', function() {
it('inserts no node symbols when not set', function(done) {
webview.addEventListener('console-message', function(e) {
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');
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);
};
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() {