Skip to content

Commit

Permalink
webview: Add top level origin to web messages in java
Browse files Browse the repository at this point in the history
We are exploring the utility of platform specific APIs for WebView.
One issue we are running into is that we can't fully partition data
like Chrome because the web message only contains the source origin
but does not contain the top level origin.

Ideally we'd like to use the storage key for this but it has two
problems:
1. Third party storage partitioning isn't enabled for WebView
2. If we decide to make this part of the public API it would be
very specific

Solving this issue by introducing the top level origin and only
making it available internally for the moment.

Bug: 1493959
Change-Id: Iabba121e53026d5aeb857627e2ae7784c56f5619
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4949955
Reviewed-by: Peter Pakkenberg <pbirk@chromium.org>
Reviewed-by: Peter Beverloo <peter@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Commit-Queue: Rupert Wiser <bewise@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1212672}
  • Loading branch information
Rupert Ben Wiser authored and Chromium LUCI CQ committed Oct 20, 2023
1 parent f4a85b2 commit defb138
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ class AwWebMessageHost : public js_injection::WebMessageHost {
public:
AwWebMessageHost(js_injection::WebMessageReplyProxy* reply_proxy,
const base::android::ScopedJavaGlobalRef<jobject>& listener,
const std::string& top_level_origin_string,
const std::string& origin_string,
bool is_main_frame)
: reply_proxy_(reply_proxy),
listener_(listener),
top_level_origin_string_(top_level_origin_string),
origin_string_(origin_string),
is_main_frame_(is_main_frame) {}

Expand All @@ -45,13 +47,15 @@ class AwWebMessageHost : public js_injection::WebMessageHost {
Java_WebMessageListenerHolder_onPostMessage(
env, listener_,
content::android::ConvertWebMessagePayloadToJava(message->message),
base::android::ConvertUTF8ToJavaString(env, top_level_origin_string_),
base::android::ConvertUTF8ToJavaString(env, origin_string_),
is_main_frame_, jports, reply_proxy_.GetJavaPeer());
}

private:
JsReplyProxy reply_proxy_;
base::android::ScopedJavaGlobalRef<jobject> listener_;
const std::string top_level_origin_string_;
const std::string origin_string_;
const bool is_main_frame_;
};
Expand Down Expand Up @@ -90,11 +94,12 @@ AwWebMessageHostFactory::GetWebMessageListenerInfo(
}

std::unique_ptr<js_injection::WebMessageHost>
AwWebMessageHostFactory::CreateHost(const std::string& origin_string,
AwWebMessageHostFactory::CreateHost(const std::string& top_level_origin_string,
const std::string& origin_string,
bool is_main_frame,
js_injection::WebMessageReplyProxy* proxy) {
return std::make_unique<AwWebMessageHost>(proxy, listener_, origin_string,
is_main_frame);
return std::make_unique<AwWebMessageHost>(
proxy, listener_, top_level_origin_string, origin_string, is_main_frame);
}

} // namespace android_webview
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class AwWebMessageHostFactory : public js_injection::WebMessageHostFactory {

// js_injection::WebMessageConnection:
std::unique_ptr<js_injection::WebMessageHost> CreateHost(
const std::string& top_level_origin_string,
const std::string& origin_string,
bool is_main_frame,
js_injection::WebMessageReplyProxy* proxy) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@
public interface WebMessageListener {
/**
* Receives postMessage information.
* @param payload The message payload from JavaScript.
*
* @param payload The message payload from JavaScript.
* @param topLevelOrigin The origin of the top level frame where the message is from.
* @param sourceOrigin The origin of the frame where the message is from.
* @param isMainFrame If the message is from a main frame.
* @param isMainFrame If the message is from a main frame.
* @param jsReplyProxy Used for reply message to the injected JavaScript object.
* @param ports JavaScript code could post message with additional message ports. Receive
* ports to establish new communication channels. Could be empty array but
* won't be null.
* @param ports JavaScript code could post message with additional message ports. Receive ports
* to establish new communication channels. Could be empty array but won't be null.
*/
void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
JsReplyProxy jsReplyProxy, MessagePort[] ports);
void onPostMessage(
MessagePayload payload,
Uri topLevelOrigin,
Uri sourceOrigin,
boolean isMainFrame,
JsReplyProxy jsReplyProxy,
MessagePort[] ports);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@ public WebMessageListenerHolder(@NonNull WebMessageListener listener) {
}

@CalledByNative
public void onPostMessage(MessagePayload payload, String sourceOrigin, boolean isMainFrame,
MessagePort[] ports, JsReplyProxy replyProxy) {
AwThreadUtils.postToCurrentLooper(() -> {
mListener.onPostMessage(
payload, Uri.parse(sourceOrigin), isMainFrame, replyProxy, ports);
});
public void onPostMessage(
MessagePayload payload,
String topLevelOrigin,
String sourceOrigin,
boolean isMainFrame,
MessagePort[] ports,
JsReplyProxy replyProxy) {
AwThreadUtils.postToCurrentLooper(
() -> {
mListener.onPostMessage(
payload,
Uri.parse(topLevelOrigin),
Uri.parse(sourceOrigin),
isMainFrame,
replyProxy,
ports);
});
}

public WebMessageListener getListener() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,13 @@ private static class IFrameLoadedListener implements WebMessageListener {
private volatile String mResult;

@Override
public void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
JsReplyProxy replyProxy, MessagePort[] ports) {
public void onPostMessage(
MessagePayload payload,
Uri topLevelOrigin,
Uri sourceOrigin,
boolean isMainFrame,
JsReplyProxy replyProxy,
MessagePort[] ports) {
mResult = payload.getAsString();
mCallbackHelper.notifyCalled();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,21 @@ private static class TestWebMessageListener implements WebMessageListener {

public static class Data {
private MessagePayload mPayload;
public Uri mTopLevelOrigin;
public Uri mSourceOrigin;
public boolean mIsMainFrame;
public JsReplyProxy mReplyProxy;
public MessagePort[] mPorts;

public Data(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
JsReplyProxy replyProxy, MessagePort[] ports) {
public Data(
MessagePayload payload,
Uri topLevelOrigin,
Uri sourceOrigin,
boolean isMainFrame,
JsReplyProxy replyProxy,
MessagePort[] ports) {
mPayload = payload;
mTopLevelOrigin = topLevelOrigin;
mSourceOrigin = sourceOrigin;
mIsMainFrame = isMainFrame;
mReplyProxy = replyProxy;
Expand All @@ -110,9 +117,16 @@ public byte[] getAsArrayBuffer() {
}

@Override
public void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
JsReplyProxy replyProxy, MessagePort[] ports) {
mQueue.add(new Data(payload, sourceOrigin, isMainFrame, replyProxy, ports));
public void onPostMessage(
MessagePayload payload,
Uri topLevelOrigin,
Uri sourceOrigin,
boolean isMainFrame,
JsReplyProxy replyProxy,
MessagePort[] ports) {
mQueue.add(
new Data(
payload, topLevelOrigin, sourceOrigin, isMainFrame, replyProxy, ports));
}

public Data waitForOnPostMessage() throws Exception {
Expand Down Expand Up @@ -145,6 +159,7 @@ public void testPostMessageSimple() throws Throwable {

TestWebMessageListener.Data data = mListener.waitForOnPostMessage();

assertUrlHasOrigin(url, data.mTopLevelOrigin);
assertUrlHasOrigin(url, data.mSourceOrigin);
Assert.assertEquals(HELLO, data.getAsString());
Assert.assertTrue(data.mIsMainFrame);
Expand Down Expand Up @@ -232,13 +247,20 @@ public void testPostMessageFromIframeWorks() throws Throwable {
final String frameUrl = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML);
final String html = createCrossOriginAccessTestPageHtml(frameUrl);

final String baseUrl = "http://www.google.com";
// Load a cross origin iframe page.
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
mContentsClient.getOnPageFinishedHelper(), html, "text/html", false,
"http://www.google.com", null);
mActivityTestRule.loadDataWithBaseUrlSync(
mAwContents,
mContentsClient.getOnPageFinishedHelper(),
html,
"text/html",
false,
baseUrl,
null);

TestWebMessageListener.Data data = mListener.waitForOnPostMessage();

assertUrlHasOrigin(baseUrl, data.mTopLevelOrigin);
assertUrlHasOrigin(frameUrl, data.mSourceOrigin);
Assert.assertEquals(HELLO, data.getAsString());
Assert.assertFalse(data.mIsMainFrame);
Expand Down Expand Up @@ -1431,6 +1453,11 @@ private String loadUrlFromPath(String path) throws Exception {
}

private static void assertUrlHasOrigin(final String url, final Uri origin) {
Assert.assertEquals("The origin URI must not contain a path", "", origin.getPath());
Assert.assertEquals("The origin URI must not contain any queries", null, origin.getQuery());
Assert.assertEquals(
"The origin URI must not contain a fragment", null, origin.getFragment());

Uri uriFromServer = Uri.parse(url);
Assert.assertEquals(uriFromServer.getScheme(), origin.getScheme());
Assert.assertEquals(uriFromServer.getHost(), origin.getHost());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,10 @@ public void testInjectedJavascriptIsTransferredWhenProfileChanges() throws Throw
CallbackHelper testDoneHelper = new CallbackHelper();

final WebMessageListener injectedListener =
(payload, sourceOrigin, isMainFrame, jsReplyProxy, ports) -> {
Assert.assertEquals("success", payload.getAsString());
testDoneHelper.notifyCalled();
};
(payload, topLevelOrigin, sourceOrigin, isMainFrame, jsReplyProxy, ports) -> {
Assert.assertEquals("success", payload.getAsString());
testDoneHelper.notifyCalled();
};

// Setup a message listener and a startup script to post on to the listener.
mRule.runOnUiThread(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,13 @@ public Data(String message, boolean isMainFrame, JsReplyProxy replyProxy) {
}

@Override
public void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
JsReplyProxy replyProxy, MessagePort[] ports) {
public void onPostMessage(
MessagePayload payload,
Uri topLevelOrigin,
Uri sourceOrigin,
boolean isMainFrame,
JsReplyProxy replyProxy,
MessagePort[] ports) {
mQueue.add(new Data(payload.getAsString(), isMainFrame, replyProxy));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,13 @@ public SupportLibWebMessageListenerAdapter(
}

@Override
public void onPostMessage(final MessagePayload payload, final Uri sourceOrigin,
final boolean isMainFrame, final JsReplyProxy replyProxy, final MessagePort[] ports) {
public void onPostMessage(
final MessagePayload payload,
final Uri topLevelOrigin,
final Uri sourceOrigin,
final boolean isMainFrame,
final JsReplyProxy replyProxy,
final MessagePort[] ports) {
if (!BoundaryInterfaceReflectionUtil.containsFeature(
mSupportedFeatures, Features.WEB_MESSAGE_LISTENER)) {
Log.e(TAG, "The AndroidX doesn't have feature: " + Features.WEB_MESSAGE_LISTENER);
Expand Down
11 changes: 9 additions & 2 deletions components/js_injection/browser/js_to_browser_messaging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ void JsToBrowserMessaging::PostMessage(
if (!web_contents)
return;

const url::Origin top_level_origin =
render_frame_host_->GetMainFrame()->GetLastCommittedOrigin();
// |source_origin| has no race with this PostMessage call, because of
// associated mojo channel, the committed origin message and PostMessage are
// in sequence.
Expand All @@ -112,12 +114,16 @@ void JsToBrowserMessaging::PostMessage(
DCHECK(reply_proxy_);

if (!host_) {
const std::string top_level_origin_string =
GetOriginString(top_level_origin);
const std::string origin_string = GetOriginString(source_origin);
const bool is_main_frame = render_frame_host_->IsInPrimaryMainFrame();

host_ = connection_factory_->CreateHost(origin_string, is_main_frame,
reply_proxy_.get());
host_ =
connection_factory_->CreateHost(top_level_origin_string, origin_string,
is_main_frame, reply_proxy_.get());
#if DCHECK_IS_ON()
top_level_origin_string_ = top_level_origin_string;
origin_string_ = origin_string;
is_main_frame_ = is_main_frame;
#endif
Expand All @@ -127,6 +133,7 @@ void JsToBrowserMessaging::PostMessage(
// The origin and whether this is the main frame should not change once
// PostMessage() has been received.
#if DCHECK_IS_ON()
DCHECK_EQ(GetOriginString(top_level_origin), top_level_origin_string_);
DCHECK_EQ(GetOriginString(source_origin), origin_string_);
DCHECK_EQ(is_main_frame_, render_frame_host_->IsInPrimaryMainFrame());
#endif
Expand Down
1 change: 1 addition & 0 deletions components/js_injection/browser/js_to_browser_messaging.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class JsToBrowserMessaging : public mojom::JsToBrowserMessaging {
mojo::AssociatedReceiver<mojom::JsToBrowserMessaging> receiver_{this};
std::unique_ptr<WebMessageHost> host_;
#if DCHECK_IS_ON()
std::string top_level_origin_string_;
std::string origin_string_;
bool is_main_frame_;
#endif
Expand Down
1 change: 1 addition & 0 deletions components/js_injection/browser/web_message_host_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class WebMessageHostFactory {
// Creates a WebMessageHost for the specified page. |proxy| is valid for
// the life of the host and may be used to send messages back to the page.
virtual std::unique_ptr<WebMessageHost> CreateHost(
const std::string& top_level_origin_string,
const std::string& origin_string,
bool is_main_frame,
WebMessageReplyProxy* proxy) = 0;
Expand Down

0 comments on commit defb138

Please sign in to comment.