Commit e7387117 authored by Ayu Ishii's avatar Ayu Ishii Committed by Commit Bot

[sms] Allow SMS Receiver for same origin iframes

This change allows this API to be used by same origin iframes
in addition to top-level frames.

Bug: 989184
Change-Id: I9c402fb4ce3188ebadbc647c9f781a40a8a3b63e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1762917
Commit-Queue: Ayu Ishii <ayui@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#693203}
parent 0173c183
...@@ -6337,8 +6337,11 @@ blink::mojom::FileChooserPtr RenderFrameHostImpl::BindFileChooserForTesting() { ...@@ -6337,8 +6337,11 @@ blink::mojom::FileChooserPtr RenderFrameHostImpl::BindFileChooserForTesting() {
void RenderFrameHostImpl::BindSmsReceiverReceiver( void RenderFrameHostImpl::BindSmsReceiverReceiver(
mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) { mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) {
if (GetParent()) { if (GetParent() && !WebContents::FromRenderFrameHost(this)
mojo::ReportBadMessage("Must be in top-level browser context."); ->GetMainFrame()
->GetLastCommittedOrigin()
.IsSameOriginWith(GetLastCommittedOrigin())) {
mojo::ReportBadMessage("Must have the same origin as the top-level frame.");
return; return;
} }
auto* provider = BrowserMainLoop::GetInstance()->GetSmsProvider(); auto* provider = BrowserMainLoop::GetInstance()->GetSmsProvider();
......
...@@ -31,11 +31,11 @@ ScriptPromise SMSReceiver::receive(ScriptState* script_state, ...@@ -31,11 +31,11 @@ ScriptPromise SMSReceiver::receive(ScriptState* script_state,
DCHECK(context->IsContextThread()); DCHECK(context->IsContextThread());
LocalFrame* frame = GetFrame(); LocalFrame* frame = GetFrame();
if (!frame->IsMainFrame()) { if (!frame->IsMainFrame() && frame->IsCrossOriginSubframe()) {
return ScriptPromise::RejectWithDOMException( return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>( script_state, MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, DOMExceptionCode::kNotAllowedError,
"Must be in top-level browsing context.")); "Must have the same origin as the top-level frame."));
} }
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
......
'use strict';
const SmsProvider = (() => {
class MockSmsReceiver {
constructor() {
this.mojoReceiver_ = new blink.mojom.SmsReceiverReceiver(this);
this.interceptor_ = new MojoInterfaceInterceptor(
blink.mojom.SmsReceiver.$interfaceName)
this.interceptor_.oninterfacerequest = (e) => {
this.mojoReceiver_.$.bindHandle(e.handle);
}
this.interceptor_.start();
this.returnValues_ = {};
}
receive() {
let call = this.returnValues_.receive ?
this.returnValues_.receive.shift() : null;
if (!call) {
throw new Error("Unexpected call.");
}
return call();
}
pushReturnValuesForTesting(callName, value) {
this.returnValues_[callName] = this.returnValues_[callName] || [];
this.returnValues_[callName].push(value);
return this;
}
}
const mockSmsReceiver = new MockSmsReceiver();
class SmsProviderChromium {
constructor() {
Object.freeze(this); // Make it immutable.
}
pushReturnValuesForTesting(callName, callback) {
mockSmsReceiver.pushReturnValuesForTesting(callName, callback);
}
}
return SmsProviderChromium;
})();
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
Usage: Usage:
1) Include <script src="./sms_provider.js"></script> in your test. 1) Include <script src="./resources/helper.js"></script> in your test.
2) Set expectations 2) Set expectations
await expect(receive).andReturn(() => { await expect(receive).andReturn(() => {
// mock behavior // mock behavior
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
- enum State {kSuccess, kTimeout}: allows you to mock success/failures. - enum State {kSuccess, kTimeout}: allows you to mock success/failures.
--> -->
<script src="./sms_provider.js"></script> <script src="./resources/helper.js"></script>
<script> <script>
'use strict'; 'use strict';
......
let interceptor = (async function() { 'use strict';
let load = Promise.resolve();
[ // These tests rely on the User Agent providing an implementation of
// the sms retriever.
//
// In Chromium-based browsers this implementation is provided by a polyfill
// in order to reduce the amount of test-only code shipped to users. To enable
// these tests the browser must be run with these options:
//
// --enable-blink-features=MojoJS,MojoJSTest
const loadChromiumResources = async () => {
if (!window.MojoInterfaceInterceptor) {
// Do nothing on non-Chromium-based browsers or when the Mojo bindings are
// not present in the global namespace.
return;
}
const resources = [
'/gen/layout_test_data/mojo/public/js/mojo_bindings_lite.js', '/gen/layout_test_data/mojo/public/js/mojo_bindings_lite.js',
'/gen/mojo/public/mojom/base/big_buffer.mojom-lite.js',
'/gen/mojo/public/mojom/base/string16.mojom-lite.js',
'/gen/mojo/public/mojom/base/time.mojom-lite.js', '/gen/mojo/public/mojom/base/time.mojom-lite.js',
'/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js', '/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js',
].forEach(path => { '/resources/chromium/sms_mock.js',
let script = document.createElement('script'); ];
await Promise.all(resources.map(path => {
const script = document.createElement('script');
script.src = path; script.src = path;
script.async = false; script.async = false;
load = load.then(() => new Promise(resolve => { const promise = new Promise((resolve, reject) => {
script.onload = resolve; script.onload = resolve;
})); script.onerror = reject;
document.head.appendChild(script);
}); });
document.head.appendChild(script);
return promise;
}));
return load.then(intercept); Status.kSuccess = blink.mojom.SmsStatus.kSuccess;
})(); Status.kTimeout = blink.mojom.SmsStatus.kTimeout;
Status.kCancelled = blink.mojom.SmsStatus.kCancelled;
// Fake implementation of blink.mojom.SmsReceiver. };
class FakeSmsReceiverImpl {
constructor() {
this.returnValues = {}
}
bindHandleToMojoReceiver(handle) {
this.mojoReceiver_ = new blink.mojom.SmsReceiverReceiver(this);
this.mojoReceiver_.$.bindHandle(handle);
}
pushReturnValuesForTesting(callName, returnValues) { const Status = {};
this.returnValues[callName] = this.returnValues[callName] || [];
this.returnValues[callName].push(returnValues);
return this;
}
receive() { async function create_sms_provider() {
let call = this.returnValues.receive.shift(); if (typeof SmsProvider === 'undefined') {
if (!call) { await loadChromiumResources();
throw new Error("Unexpected call.");
} }
return call(); if (typeof SmsProvider == 'undefined') {
throw new Error('Mojo testing interface is not available.');
} }
return new SmsProvider();
} }
function receive(callback) { function receive() {
throw new Error("expected to be overriden by tests"); throw new Error("expected to be overriden by tests");
} }
function expect(call) { function expect(call) {
return { return {
async andReturn(callback) { async andReturn(callback) {
let smsReceiverImpl = await interceptor; const mock = await create_sms_provider();
smsReceiverImpl.pushReturnValuesForTesting(call.name, callback); mock.pushReturnValuesForTesting(call.name, callback);
} }
} }
} }
const Status = {};
function intercept() {
let smsReceiverImpl = new FakeSmsReceiverImpl();
let interceptor = new MojoInterfaceInterceptor(
blink.mojom.SmsReceiver.$interfaceName);
interceptor.oninterfacerequest = (e) => {
smsReceiverImpl.bindHandleToMojoReceiver(e.handle);
}
interceptor.start();
Status.kSuccess = blink.mojom.SmsStatus.kSuccess;
Status.kTimeout = blink.mojom.SmsStatus.kTimeout;
Status.kCancelled = blink.mojom.SmsStatus.kCancelled;
return smsReceiverImpl;
}
<script src="./helper.js"></script>
<script> <script>
'use strict'; 'use strict';
navigator.sms.receive().catch(error => { // Intercept successful calls and return mocked value.
window.parent.postMessage({errorType: error.name}, '*'); (async function() {
}); await expect(receive).andReturn(() => {
return Promise.resolve({
status: Status.kSuccess,
message: "hello",
});
});
}());
window.onload = function() {
navigator.sms.receive()
.then(sms => {
window.parent.postMessage({result: "Pass", sms: sms.content}, '*');
})
.catch(error => {
window.parent.postMessage({result: "Fail", errorType: error.name}, '*');
});
}
</script> </script>
<!DOCTYPE html> <!DOCTYPE html>
<script src=/resources/testharness.js></script> <script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script> <script src=/resources/testharnessreport.js></script>
<script src="/common/get-host-info.sub.js"></script>
<body> <body>
<script> <script>
'use strict'; 'use strict';
const host = get_host_info();
const remoteBaseURL = host.HTTPS_REMOTE_ORIGIN + window.location.pathname.replace(/\/[^\/]*$/, '/') ;
const localBaseURL = host.HTTPS_ORIGIN + window.location.pathname.replace(/\/[^\/]*$/, '/') ;
promise_test(async t => {
const messageWatcher = new EventWatcher(t, window, "message");
var iframe = document.createElement("iframe");
iframe.src = localBaseURL + "resources/iframe.html";
document.body.appendChild(iframe);
const message = await messageWatcher.wait_for("message");
assert_equals(message.data.result, "Pass");
assert_equals(message.data.sms, "hello");
}, "Test SMSReceiver API enabled in same origin iframes");
promise_test(async t => { promise_test(async t => {
const messageWatcher = new EventWatcher(t, window, "message"); const messageWatcher = new EventWatcher(t, window, "message");
var iframe = document.createElement("iframe"); var iframe = document.createElement("iframe");
iframe.src = "resources/iframe.html" iframe.src = remoteBaseURL + "resources/iframe.html"
document.body.appendChild(iframe); document.body.appendChild(iframe);
const message = await messageWatcher.wait_for("message"); const message = await messageWatcher.wait_for("message");
assert_equals(message.data.result, "Fail");
assert_equals(message.data.errorType, "NotAllowedError"); assert_equals(message.data.errorType, "NotAllowedError");
}, "Test SMSReceiver API disabled in iframes"); }, "Test SMSReceiver API disabled in cross origin iframes");
</script> </script>
</body> </body>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment