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() {
void RenderFrameHostImpl::BindSmsReceiverReceiver(
mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) {
if (GetParent()) {
mojo::ReportBadMessage("Must be in top-level browser context.");
if (GetParent() && !WebContents::FromRenderFrameHost(this)
->GetMainFrame()
->GetLastCommittedOrigin()
.IsSameOriginWith(GetLastCommittedOrigin())) {
mojo::ReportBadMessage("Must have the same origin as the top-level frame.");
return;
}
auto* provider = BrowserMainLoop::GetInstance()->GetSmsProvider();
......
......@@ -31,11 +31,11 @@ ScriptPromise SMSReceiver::receive(ScriptState* script_state,
DCHECK(context->IsContextThread());
LocalFrame* frame = GetFrame();
if (!frame->IsMainFrame()) {
if (!frame->IsMainFrame() && frame->IsCrossOriginSubframe()) {
return ScriptPromise::RejectWithDOMException(
script_state, MakeGarbageCollected<DOMException>(
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);
......
'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 @@
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
await expect(receive).andReturn(() => {
// mock behavior
......@@ -29,7 +29,7 @@
- enum State {kSuccess, kTimeout}: allows you to mock success/failures.
-->
<script src="./sms_provider.js"></script>
<script src="./resources/helper.js"></script>
<script>
'use strict';
......
let interceptor = (async function() {
let load = Promise.resolve();
[
'use strict';
// 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/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/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js',
].forEach(path => {
let script = document.createElement('script');
'/resources/chromium/sms_mock.js',
];
await Promise.all(resources.map(path => {
const script = document.createElement('script');
script.src = path;
script.async = false;
load = load.then(() => new Promise(resolve => {
const promise = new Promise((resolve, reject) => {
script.onload = resolve;
}));
script.onerror = reject;
});
document.head.appendChild(script);
});
return load.then(intercept);
})();
return promise;
}));
// Fake implementation of blink.mojom.SmsReceiver.
class FakeSmsReceiverImpl {
constructor() {
this.returnValues = {}
}
Status.kSuccess = blink.mojom.SmsStatus.kSuccess;
Status.kTimeout = blink.mojom.SmsStatus.kTimeout;
Status.kCancelled = blink.mojom.SmsStatus.kCancelled;
};
bindHandleToMojoReceiver(handle) {
this.mojoReceiver_ = new blink.mojom.SmsReceiverReceiver(this);
this.mojoReceiver_.$.bindHandle(handle);
}
const Status = {};
pushReturnValuesForTesting(callName, returnValues) {
this.returnValues[callName] = this.returnValues[callName] || [];
this.returnValues[callName].push(returnValues);
return this;
async function create_sms_provider() {
if (typeof SmsProvider === 'undefined') {
await loadChromiumResources();
}
receive() {
let call = this.returnValues.receive.shift();
if (!call) {
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");
}
function expect(call) {
return {
async andReturn(callback) {
let smsReceiverImpl = await interceptor;
smsReceiverImpl.pushReturnValuesForTesting(call.name, callback);
const mock = await create_sms_provider();
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>
'use strict';
navigator.sms.receive().catch(error => {
window.parent.postMessage({errorType: error.name}, '*');
});
// Intercept successful calls and return mocked value.
(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>
<!DOCTYPE html>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<script>
'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 => {
const messageWatcher = new EventWatcher(t, window, "message");
var iframe = document.createElement("iframe");
iframe.src = "resources/iframe.html"
iframe.src = remoteBaseURL + "resources/iframe.html"
document.body.appendChild(iframe);
const message = await messageWatcher.wait_for("message");
assert_equals(message.data.result, "Fail");
assert_equals(message.data.errorType, "NotAllowedError");
}, "Test SMSReceiver API disabled in iframes");
}, "Test SMSReceiver API disabled in cross origin iframes");
</script>
</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