diff --git a/atom/browser/lib/guest-view-manager.coffee b/atom/browser/lib/guest-view-manager.coffee index dc2469776..bbabf3c5d 100644 --- a/atom/browser/lib/guest-view-manager.coffee +++ b/atom/browser/lib/guest-view-manager.coffee @@ -66,6 +66,10 @@ createGuest = (embedder, params) -> guest.on event, (_, args...) -> embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{guest.viewInstanceId}", event, args... + # Dispatch guest's IPC messages to embedder. + guest.on 'ipc-message-host', (_, channel, args...) -> + embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{guest.viewInstanceId}", channel, args... + # Autosize. guest.on 'size-changed', (_, args...) -> embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{guest.viewInstanceId}", args... diff --git a/atom/renderer/api/lib/ipc.coffee b/atom/renderer/api/lib/ipc.coffee index cf9e7af18..1dfb9b408 100644 --- a/atom/renderer/api/lib/ipc.coffee +++ b/atom/renderer/api/lib/ipc.coffee @@ -16,6 +16,9 @@ class Ipc extends EventEmitter sendSync: (args...) -> JSON.parse ipc.sendSync('ipc-message-sync', [args...]) + sendToHost: (args...) -> + ipc.send 'ipc-message-host', [args...] + # Discarded sendChannel: -> @send.apply this, arguments sendChannelSync: -> @sendSync.apply this, arguments diff --git a/atom/renderer/api/lib/remote.coffee b/atom/renderer/api/lib/remote.coffee index 0f4579091..84ec432f8 100644 --- a/atom/renderer/api/lib/remote.coffee +++ b/atom/renderer/api/lib/remote.coffee @@ -40,7 +40,7 @@ metaToValue = (meta) -> constructor: -> if @constructor == RemoteFunction # Constructor call. - obj = ipc.sendChannelSync 'ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments) + obj = ipc.sendSync 'ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments) # Returning object in constructor will replace constructed object # with the returned object. @@ -48,7 +48,7 @@ metaToValue = (meta) -> return metaToValue obj else # Function call. - ret = ipc.sendChannelSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments) + ret = ipc.sendSync 'ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments) return metaToValue ret else ret = v8Util.createObjectWithName meta.name @@ -62,27 +62,27 @@ metaToValue = (meta) -> constructor: -> if @constructor is RemoteMemberFunction # Constructor call. - obj = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', meta.id, member.name, wrapArgs(arguments) + obj = ipc.sendSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', meta.id, member.name, wrapArgs(arguments) return metaToValue obj else # Call member function. - ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments) + ret = ipc.sendSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments) return metaToValue ret else ret.__defineSetter__ member.name, (value) -> # Set member data. - ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_SET', meta.id, member.name, value + ipc.sendSync 'ATOM_BROWSER_MEMBER_SET', meta.id, member.name, value value ret.__defineGetter__ member.name, -> # Get member data. - ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_GET', meta.id, member.name + ret = ipc.sendSync 'ATOM_BROWSER_MEMBER_GET', meta.id, member.name metaToValue ret # Track delegate object's life time, and tell the browser to clean up # when the object is GCed. v8Util.setDestructor ret, -> - ipc.sendChannel 'ATOM_BROWSER_DEREFERENCE', meta.storeId + ipc.send 'ATOM_BROWSER_DEREFERENCE', meta.storeId # Remember object's id. v8Util.setHiddenValue ret, 'atomId', meta.id @@ -104,19 +104,19 @@ moduleCache = {} exports.require = (module) -> return moduleCache[module] if moduleCache[module]? - meta = ipc.sendChannelSync 'ATOM_BROWSER_REQUIRE', module + meta = ipc.sendSync 'ATOM_BROWSER_REQUIRE', module moduleCache[module] = metaToValue meta # Get current window object. windowCache = null exports.getCurrentWindow = -> return windowCache if windowCache? - meta = ipc.sendChannelSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId + meta = ipc.sendSync 'ATOM_BROWSER_CURRENT_WINDOW', process.guestInstanceId windowCache = metaToValue meta # Get a global object in browser. exports.getGlobal = (name) -> - meta = ipc.sendChannelSync 'ATOM_BROWSER_GLOBAL', name + meta = ipc.sendSync 'ATOM_BROWSER_GLOBAL', name metaToValue meta # Get the process object in browser. @@ -133,5 +133,5 @@ exports.createFunctionWithReturnValue = (returnValue) -> # Get the guest WebContents from guestInstanceId. exports.getGuestWebContents = (guestInstanceId) -> - meta = ipc.sendChannelSync 'ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId + meta = ipc.sendSync 'ATOM_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId metaToValue meta diff --git a/atom/renderer/lib/web-view/guest-view-internal.coffee b/atom/renderer/lib/web-view/guest-view-internal.coffee index e93cac61f..4c81fb5b9 100644 --- a/atom/renderer/lib/web-view/guest-view-internal.coffee +++ b/atom/renderer/lib/web-view/guest-view-internal.coffee @@ -28,6 +28,12 @@ module.exports = ipc.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{viewInstanceId}", (event, args...) -> dispatchEvent webView, event, args... + ipc.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{viewInstanceId}", (channel, args...) -> + domEvent = new Event('ipc-message') + domEvent.channel = channel + domEvent.args = [args...] + webView.dispatchEvent domEvent + ipc.on "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{viewInstanceId}", (args...) -> domEvent = new Event('size-changed') for f, i in ['oldWidth', 'oldHeight', 'newWidth', 'newHeight'] diff --git a/docs/api/ipc-renderer.md b/docs/api/ipc-renderer.md index b5f1157e6..54dd34888 100644 --- a/docs/api/ipc-renderer.md +++ b/docs/api/ipc-renderer.md @@ -20,3 +20,10 @@ the `channel` event of `ipc` module, and returns by setting `event.returnValue`. **Note:** Usually developers should never use this API, since sending synchronous message would block the whole web page. + +## ipc.sendToHost(channel[, args...]) + +Like `ipc.send` but the message will be sent to the host page instead of the +browser process. + +This is mainly used by the page in `` to communicate with host page. diff --git a/docs/api/web-view-tag.md b/docs/api/web-view-tag.md index d4650b683..bde67ed96 100644 --- a/docs/api/web-view-tag.md +++ b/docs/api/web-view-tag.md @@ -295,7 +295,7 @@ webview.addEventListener('new-window', function(e) { ### close -Fired when the guest window attempts to close itself. +Fired when the guest page attempts to close itself. The following example code navigates the `webview` to `about:blank` when the guest attempts to close itself. @@ -306,6 +306,33 @@ webview.addEventListener('close', function() { }); ``` +### ipc-message + +* `channel` String +* `args` Array + +Fired when the guest page has sent an asynchronous message to embedder page. + +With `sendToHost` method and `ipc-message` event you can easily communicate +between guest page and embedder page: + +```javascript +// In embedder page. +webview.addEventListener('ipc-message', function(event) { + console.log(event.channel); + // Prints "pong" +}); +webview.send('ping'); +``` + +```javascript +// In guest page. +var ipc = require('ipc'); +ipc.on('ping', function() { + ipc.sendToHost('pong'); +}) +``` + ### crashed Fired when the renderer process is crashed. diff --git a/spec/fixtures/pages/ipc-message.html b/spec/fixtures/pages/ipc-message.html new file mode 100644 index 000000000..15bfef49c --- /dev/null +++ b/spec/fixtures/pages/ipc-message.html @@ -0,0 +1,7 @@ + + + + + diff --git a/spec/webview-spec.coffee b/spec/webview-spec.coffee index f14cee53c..dbbae3941 100644 --- a/spec/webview-spec.coffee +++ b/spec/webview-spec.coffee @@ -86,3 +86,13 @@ describe ' tag', -> done() webview.src = "file://#{fixtures}/pages/target-name.html" document.body.appendChild webview + + describe 'ipc-message event', -> + it 'emits when guest sends a ipc message to browser', (done) -> + webview.addEventListener 'ipc-message', (e) -> + assert.equal e.channel, 'channel' + assert.deepEqual e.args, ['arg1', 'arg2'] + done() + webview.src = "file://#{fixtures}/pages/ipc-message.html" + webview.setAttribute 'nodeintegration', 'on' + document.body.appendChild webview