Skip to content

Commit defb138

Browse files
Rupert Ben WiserChromium LUCI CQ
authored andcommitted
webview: Add top level origin to web messages in java
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}
1 parent f4a85b2 commit defb138

File tree

12 files changed

+110
-36
lines changed

12 files changed

+110
-36
lines changed

android_webview/browser/js_java_interaction/aw_web_message_host_factory.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ class AwWebMessageHost : public js_injection::WebMessageHost {
2727
public:
2828
AwWebMessageHost(js_injection::WebMessageReplyProxy* reply_proxy,
2929
const base::android::ScopedJavaGlobalRef<jobject>& listener,
30+
const std::string& top_level_origin_string,
3031
const std::string& origin_string,
3132
bool is_main_frame)
3233
: reply_proxy_(reply_proxy),
3334
listener_(listener),
35+
top_level_origin_string_(top_level_origin_string),
3436
origin_string_(origin_string),
3537
is_main_frame_(is_main_frame) {}
3638

@@ -45,13 +47,15 @@ class AwWebMessageHost : public js_injection::WebMessageHost {
4547
Java_WebMessageListenerHolder_onPostMessage(
4648
env, listener_,
4749
content::android::ConvertWebMessagePayloadToJava(message->message),
50+
base::android::ConvertUTF8ToJavaString(env, top_level_origin_string_),
4851
base::android::ConvertUTF8ToJavaString(env, origin_string_),
4952
is_main_frame_, jports, reply_proxy_.GetJavaPeer());
5053
}
5154

5255
private:
5356
JsReplyProxy reply_proxy_;
5457
base::android::ScopedJavaGlobalRef<jobject> listener_;
58+
const std::string top_level_origin_string_;
5559
const std::string origin_string_;
5660
const bool is_main_frame_;
5761
};
@@ -90,11 +94,12 @@ AwWebMessageHostFactory::GetWebMessageListenerInfo(
9094
}
9195

9296
std::unique_ptr<js_injection::WebMessageHost>
93-
AwWebMessageHostFactory::CreateHost(const std::string& origin_string,
97+
AwWebMessageHostFactory::CreateHost(const std::string& top_level_origin_string,
98+
const std::string& origin_string,
9499
bool is_main_frame,
95100
js_injection::WebMessageReplyProxy* proxy) {
96-
return std::make_unique<AwWebMessageHost>(proxy, listener_, origin_string,
97-
is_main_frame);
101+
return std::make_unique<AwWebMessageHost>(
102+
proxy, listener_, top_level_origin_string, origin_string, is_main_frame);
98103
}
99104

100105
} // namespace android_webview

android_webview/browser/js_java_interaction/aw_web_message_host_factory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class AwWebMessageHostFactory : public js_injection::WebMessageHostFactory {
3434

3535
// js_injection::WebMessageConnection:
3636
std::unique_ptr<js_injection::WebMessageHost> CreateHost(
37+
const std::string& top_level_origin_string,
3738
const std::string& origin_string,
3839
bool is_main_frame,
3940
js_injection::WebMessageReplyProxy* proxy) override;

android_webview/java/src/org/chromium/android_webview/WebMessageListener.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@
1717
public interface WebMessageListener {
1818
/**
1919
* Receives postMessage information.
20-
* @param payload The message payload from JavaScript.
20+
*
21+
* @param payload The message payload from JavaScript.
22+
* @param topLevelOrigin The origin of the top level frame where the message is from.
2123
* @param sourceOrigin The origin of the frame where the message is from.
22-
* @param isMainFrame If the message is from a main frame.
24+
* @param isMainFrame If the message is from a main frame.
2325
* @param jsReplyProxy Used for reply message to the injected JavaScript object.
24-
* @param ports JavaScript code could post message with additional message ports. Receive
25-
* ports to establish new communication channels. Could be empty array but
26-
* won't be null.
26+
* @param ports JavaScript code could post message with additional message ports. Receive ports
27+
* to establish new communication channels. Could be empty array but won't be null.
2728
*/
28-
void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
29-
JsReplyProxy jsReplyProxy, MessagePort[] ports);
29+
void onPostMessage(
30+
MessagePayload payload,
31+
Uri topLevelOrigin,
32+
Uri sourceOrigin,
33+
boolean isMainFrame,
34+
JsReplyProxy jsReplyProxy,
35+
MessagePort[] ports);
3036
}

android_webview/java/src/org/chromium/android_webview/WebMessageListenerHolder.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,23 @@ public WebMessageListenerHolder(@NonNull WebMessageListener listener) {
2929
}
3030

3131
@CalledByNative
32-
public void onPostMessage(MessagePayload payload, String sourceOrigin, boolean isMainFrame,
33-
MessagePort[] ports, JsReplyProxy replyProxy) {
34-
AwThreadUtils.postToCurrentLooper(() -> {
35-
mListener.onPostMessage(
36-
payload, Uri.parse(sourceOrigin), isMainFrame, replyProxy, ports);
37-
});
32+
public void onPostMessage(
33+
MessagePayload payload,
34+
String topLevelOrigin,
35+
String sourceOrigin,
36+
boolean isMainFrame,
37+
MessagePort[] ports,
38+
JsReplyProxy replyProxy) {
39+
AwThreadUtils.postToCurrentLooper(
40+
() -> {
41+
mListener.onPostMessage(
42+
payload,
43+
Uri.parse(topLevelOrigin),
44+
Uri.parse(sourceOrigin),
45+
isMainFrame,
46+
replyProxy,
47+
ports);
48+
});
3849
}
3950

4051
public WebMessageListener getListener() {

android_webview/javatests/src/org/chromium/android_webview/test/AwSupervisedUserTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,13 @@ private static class IFrameLoadedListener implements WebMessageListener {
229229
private volatile String mResult;
230230

231231
@Override
232-
public void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
233-
JsReplyProxy replyProxy, MessagePort[] ports) {
232+
public void onPostMessage(
233+
MessagePayload payload,
234+
Uri topLevelOrigin,
235+
Uri sourceOrigin,
236+
boolean isMainFrame,
237+
JsReplyProxy replyProxy,
238+
MessagePort[] ports) {
234239
mResult = payload.getAsString();
235240
mCallbackHelper.notifyCalled();
236241
}

android_webview/javatests/src/org/chromium/android_webview/test/JsJavaInteractionTest.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,21 @@ private static class TestWebMessageListener implements WebMessageListener {
8686

8787
public static class Data {
8888
private MessagePayload mPayload;
89+
public Uri mTopLevelOrigin;
8990
public Uri mSourceOrigin;
9091
public boolean mIsMainFrame;
9192
public JsReplyProxy mReplyProxy;
9293
public MessagePort[] mPorts;
9394

94-
public Data(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
95-
JsReplyProxy replyProxy, MessagePort[] ports) {
95+
public Data(
96+
MessagePayload payload,
97+
Uri topLevelOrigin,
98+
Uri sourceOrigin,
99+
boolean isMainFrame,
100+
JsReplyProxy replyProxy,
101+
MessagePort[] ports) {
96102
mPayload = payload;
103+
mTopLevelOrigin = topLevelOrigin;
97104
mSourceOrigin = sourceOrigin;
98105
mIsMainFrame = isMainFrame;
99106
mReplyProxy = replyProxy;
@@ -110,9 +117,16 @@ public byte[] getAsArrayBuffer() {
110117
}
111118

112119
@Override
113-
public void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
114-
JsReplyProxy replyProxy, MessagePort[] ports) {
115-
mQueue.add(new Data(payload, sourceOrigin, isMainFrame, replyProxy, ports));
120+
public void onPostMessage(
121+
MessagePayload payload,
122+
Uri topLevelOrigin,
123+
Uri sourceOrigin,
124+
boolean isMainFrame,
125+
JsReplyProxy replyProxy,
126+
MessagePort[] ports) {
127+
mQueue.add(
128+
new Data(
129+
payload, topLevelOrigin, sourceOrigin, isMainFrame, replyProxy, ports));
116130
}
117131

118132
public Data waitForOnPostMessage() throws Exception {
@@ -145,6 +159,7 @@ public void testPostMessageSimple() throws Throwable {
145159

146160
TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
147161

162+
assertUrlHasOrigin(url, data.mTopLevelOrigin);
148163
assertUrlHasOrigin(url, data.mSourceOrigin);
149164
Assert.assertEquals(HELLO, data.getAsString());
150165
Assert.assertTrue(data.mIsMainFrame);
@@ -232,13 +247,20 @@ public void testPostMessageFromIframeWorks() throws Throwable {
232247
final String frameUrl = mTestServer.getURL(POST_MESSAGE_SIMPLE_HTML);
233248
final String html = createCrossOriginAccessTestPageHtml(frameUrl);
234249

250+
final String baseUrl = "http://www.google.com";
235251
// Load a cross origin iframe page.
236-
mActivityTestRule.loadDataWithBaseUrlSync(mAwContents,
237-
mContentsClient.getOnPageFinishedHelper(), html, "text/html", false,
238-
"http://www.google.com", null);
252+
mActivityTestRule.loadDataWithBaseUrlSync(
253+
mAwContents,
254+
mContentsClient.getOnPageFinishedHelper(),
255+
html,
256+
"text/html",
257+
false,
258+
baseUrl,
259+
null);
239260

240261
TestWebMessageListener.Data data = mListener.waitForOnPostMessage();
241262

263+
assertUrlHasOrigin(baseUrl, data.mTopLevelOrigin);
242264
assertUrlHasOrigin(frameUrl, data.mSourceOrigin);
243265
Assert.assertEquals(HELLO, data.getAsString());
244266
Assert.assertFalse(data.mIsMainFrame);
@@ -1431,6 +1453,11 @@ private String loadUrlFromPath(String path) throws Exception {
14311453
}
14321454

14331455
private static void assertUrlHasOrigin(final String url, final Uri origin) {
1456+
Assert.assertEquals("The origin URI must not contain a path", "", origin.getPath());
1457+
Assert.assertEquals("The origin URI must not contain any queries", null, origin.getQuery());
1458+
Assert.assertEquals(
1459+
"The origin URI must not contain a fragment", null, origin.getFragment());
1460+
14341461
Uri uriFromServer = Uri.parse(url);
14351462
Assert.assertEquals(uriFromServer.getScheme(), origin.getScheme());
14361463
Assert.assertEquals(uriFromServer.getHost(), origin.getHost());

android_webview/javatests/src/org/chromium/android_webview/test/MultiProfileTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -433,10 +433,10 @@ public void testInjectedJavascriptIsTransferredWhenProfileChanges() throws Throw
433433
CallbackHelper testDoneHelper = new CallbackHelper();
434434

435435
final WebMessageListener injectedListener =
436-
(payload, sourceOrigin, isMainFrame, jsReplyProxy, ports) -> {
437-
Assert.assertEquals("success", payload.getAsString());
438-
testDoneHelper.notifyCalled();
439-
};
436+
(payload, topLevelOrigin, sourceOrigin, isMainFrame, jsReplyProxy, ports) -> {
437+
Assert.assertEquals("success", payload.getAsString());
438+
testDoneHelper.notifyCalled();
439+
};
440440

441441
// Setup a message listener and a startup script to post on to the listener.
442442
mRule.runOnUiThread(() -> {

android_webview/javatests/src/org/chromium/android_webview/test/PopupWindowTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,13 @@ public Data(String message, boolean isMainFrame, JsReplyProxy replyProxy) {
402402
}
403403

404404
@Override
405-
public void onPostMessage(MessagePayload payload, Uri sourceOrigin, boolean isMainFrame,
406-
JsReplyProxy replyProxy, MessagePort[] ports) {
405+
public void onPostMessage(
406+
MessagePayload payload,
407+
Uri topLevelOrigin,
408+
Uri sourceOrigin,
409+
boolean isMainFrame,
410+
JsReplyProxy replyProxy,
411+
MessagePort[] ports) {
407412
mQueue.add(new Data(payload.getAsString(), isMainFrame, replyProxy));
408413
}
409414

android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibWebMessageListenerAdapter.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,13 @@ public SupportLibWebMessageListenerAdapter(
4848
}
4949

5050
@Override
51-
public void onPostMessage(final MessagePayload payload, final Uri sourceOrigin,
52-
final boolean isMainFrame, final JsReplyProxy replyProxy, final MessagePort[] ports) {
51+
public void onPostMessage(
52+
final MessagePayload payload,
53+
final Uri topLevelOrigin,
54+
final Uri sourceOrigin,
55+
final boolean isMainFrame,
56+
final JsReplyProxy replyProxy,
57+
final MessagePort[] ports) {
5358
if (!BoundaryInterfaceReflectionUtil.containsFeature(
5459
mSupportedFeatures, Features.WEB_MESSAGE_LISTENER)) {
5560
Log.e(TAG, "The AndroidX doesn't have feature: " + Features.WEB_MESSAGE_LISTENER);

components/js_injection/browser/js_to_browser_messaging.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ void JsToBrowserMessaging::PostMessage(
9999
if (!web_contents)
100100
return;
101101

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

114116
if (!host_) {
117+
const std::string top_level_origin_string =
118+
GetOriginString(top_level_origin);
115119
const std::string origin_string = GetOriginString(source_origin);
116120
const bool is_main_frame = render_frame_host_->IsInPrimaryMainFrame();
117121

118-
host_ = connection_factory_->CreateHost(origin_string, is_main_frame,
119-
reply_proxy_.get());
122+
host_ =
123+
connection_factory_->CreateHost(top_level_origin_string, origin_string,
124+
is_main_frame, reply_proxy_.get());
120125
#if DCHECK_IS_ON()
126+
top_level_origin_string_ = top_level_origin_string;
121127
origin_string_ = origin_string;
122128
is_main_frame_ = is_main_frame;
123129
#endif
@@ -127,6 +133,7 @@ void JsToBrowserMessaging::PostMessage(
127133
// The origin and whether this is the main frame should not change once
128134
// PostMessage() has been received.
129135
#if DCHECK_IS_ON()
136+
DCHECK_EQ(GetOriginString(top_level_origin), top_level_origin_string_);
130137
DCHECK_EQ(GetOriginString(source_origin), origin_string_);
131138
DCHECK_EQ(is_main_frame_, render_frame_host_->IsInPrimaryMainFrame());
132139
#endif

components/js_injection/browser/js_to_browser_messaging.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class JsToBrowserMessaging : public mojom::JsToBrowserMessaging {
6565
mojo::AssociatedReceiver<mojom::JsToBrowserMessaging> receiver_{this};
6666
std::unique_ptr<WebMessageHost> host_;
6767
#if DCHECK_IS_ON()
68+
std::string top_level_origin_string_;
6869
std::string origin_string_;
6970
bool is_main_frame_;
7071
#endif

components/js_injection/browser/web_message_host_factory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class WebMessageHostFactory {
2424
// Creates a WebMessageHost for the specified page. |proxy| is valid for
2525
// the life of the host and may be used to send messages back to the page.
2626
virtual std::unique_ptr<WebMessageHost> CreateHost(
27+
const std::string& top_level_origin_string,
2728
const std::string& origin_string,
2829
bool is_main_frame,
2930
WebMessageReplyProxy* proxy) = 0;

0 commit comments

Comments
 (0)