diff --git a/src/client/sandbox/event/message.ts b/src/client/sandbox/event/message.ts index 14b914d9c..f778b4562 100644 --- a/src/client/sandbox/event/message.ts +++ b/src/client/sandbox/event/message.ts @@ -213,22 +213,37 @@ export default class MessageSandbox extends SandboxBase { } postMessage (contentWindow: Window, args) { - const targetUrl = args[1] || destLocation.getOriginHeader(); + if (!args[1] || typeof args[1] === 'string') + return this._postMessage(contentWindow, args); + else if (typeof args[1] === 'object') + return this._postMessageWithOptions(contentWindow, args, args[1]); - // NOTE: We do NOT support the postMessage(message, options) overload. - // The second argument is expected to be `targetOrigin` (string). - // If an options object is provided instead, the call is considered invalid and will be aborted. - if (typeof targetUrl !== 'string') { - nativeMethods.consoleMeths.log(`testcafe-hammerhead: postMessage called with invalid targetOrigin; aborting call (type: ${typeof targetUrl})`); - return null; - } + nativeMethods.consoleMeths.log(`testcafe-hammerhead: postMessage called with invalid targetOrigin; aborting call (type: ${typeof args[1]})`); + + return null; + } + + private _postMessageWithOptions (contentWindow: Window, args, options) { + const resolvedTargetUrl = typeof options.targetOrigin === 'string' + ? options.targetOrigin + : destLocation.getOriginHeader(); + + const originalMessage = args[0]; + + args[0] = MessageSandbox._wrapMessage(MessageType.User, originalMessage, resolvedTargetUrl); + args[1] = nativeMethods.objectAssign({}, options, { targetOrigin: '*' }); + + return fastApply(contentWindow, 'postMessage', args); + } + + private _postMessage (contentWindow: Window, args) { + const targetUrl = args[1] || destLocation.getOriginHeader(); // NOTE: Here, we pass all messages as "no preference" ("*"). // We do an origin check in "_onWindowMessage" to access the target origin. args[1] = '*'; args[0] = MessageSandbox._wrapMessage(MessageType.User, args[0], targetUrl); - return fastApply(contentWindow, 'postMessage', args); } diff --git a/test/client/fixtures/sandbox/event/message-test.js b/test/client/fixtures/sandbox/event/message-test.js index 6d2e6cc2d..dcf582fbd 100644 --- a/test/client/fixtures/sandbox/event/message-test.js +++ b/test/client/fixtures/sandbox/event/message-test.js @@ -35,20 +35,46 @@ asyncTest('should pass "transfer" argument for "postMessage" (GH-1535)', functio callMethod(window, 'postMessage', ['test', '*', [channel.port1]]); }); -asyncTest('should not accept an object as "targetOrigin"', function () { - var called = false; - var handler = function () { - called = true; +asyncTest('should support postMessage(message, options) overload', function () { + var eventHandlerObject = { + handleEvent: function (e) { + strictEqual(e.data, 'options-overload-test'); + window.removeEventListener('message', eventHandlerObject); + start(); + }, }; - window.addEventListener('message', handler); - callMethod(window, 'postMessage', ['message', { test: 1 }]); + window.addEventListener('message', eventHandlerObject); + callMethod(window, 'postMessage', ['options-overload-test', { targetOrigin: '*' }]); +}); - window.setTimeout(function () { - ok(!called, 'message should not be delivered'); - window.removeEventListener('message', handler); - start(); - }, 100); +asyncTest('should support postMessage(message, options) overload with transfer', function () { + var channel = new MessageChannel(); + + var eventHandlerObject = { + handleEvent: function (e) { + strictEqual(e.data, 'options-transfer-test'); + strictEqual(e.ports.length, 1); + window.removeEventListener('message', eventHandlerObject); + start(); + }, + }; + + window.addEventListener('message', eventHandlerObject); + callMethod(window, 'postMessage', ['options-transfer-test', { targetOrigin: '*', transfer: [channel.port1] }]); +}); + +asyncTest('should deliver message when postMessage is called with an object as second argument', function () { + var handler = function (e) { + if (e.data === 'object-arg-test') { + ok(true, 'message should be delivered via options overload'); + window.removeEventListener('message', handler); + start(); + } + }; + + window.addEventListener('message', handler); + callMethod(window, 'postMessage', ['object-arg-test', { test: 1 }]); }); asyncTest('onmessage event', function () {