Skip to content

Commit

Permalink
[WebShareTargetV2] Support launching the new web share target with PO…
Browse files Browse the repository at this point in the history
…ST request.

This is the second CL for web share target V2.

The goal of this CL is to implement the new web share target launcher according to the new web share target spec.
https://pr-preview.s3.amazonaws.com/ewilligers/web-share-target/pull/53.html#launching-the-web-share-target

There're three main parts of this CL that are put together:
1) Parse the fields received from the android manifest on the server-side
2) Perform the files-matching, which involves matching the share files with
   with share target files entries that accept the same mime type.
3) Launch the web share target. Specifically, this involves the additional step of
   generating the POST request. There're two possible encoding types for the POST request:
   Multipart/form-data encoding and application/x-www-form-urlencoded, both of which
   are implemented inside a new file named webapk_post_share_target_navigator.cc.
   It performs all the encoding/escaping needed for the two encoding methods.

One additional function is added to mime_util.cc in order to support the multipart/form-data POST request.

Change-Id: I719af0dc33fe64561963ae9aecc9d01192f1c17a
Reviewed-on: https://chromium-review.googlesource.com/c/1271776
Commit-Queue: Ethan Xu <xuethan@google.com>
Reviewed-by: Bence Béky <bnc@chromium.org>
Reviewed-by: Peter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: Dominick Ng <dominickn@chromium.org>
Reviewed-by: Eric Willigers <ericwilligers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611877}
  • Loading branch information
Ethan Xu authored and Commit Bot committed Nov 28, 2018
1 parent 43363a6 commit c4740ea
Show file tree
Hide file tree
Showing 23 changed files with 958 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,9 @@ public static void retrieveWebApks(long callbackPointer) {
if (WebApkValidator.isValidWebApk(context, packageInfo.packageName)) {
// Pass non-null URL parameter so that {@link WebApkInfo#create()}
// return value is non-null
WebApkInfo webApkInfo =
WebApkInfo.create(packageInfo.packageName, "", ShortcutSource.UNKNOWN,
false /* forceNavigation */, false /* useTransparentSplash */);
WebApkInfo webApkInfo = WebApkInfo.create(packageInfo.packageName, "",
ShortcutSource.UNKNOWN, false /* forceNavigation */,
false /* useTransparentSplash */, null /* shareData */);
if (webApkInfo != null) {
names.add(webApkInfo.name());
shortNames.add(webApkInfo.shortName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,11 @@ protected void initializeStartupMetrics() {
mWebApkSplashscreenMetrics = new WebApkSplashscreenMetrics();
addSplashscreenObserver(mWebApkSplashscreenMetrics);
}

@Override
protected boolean loadUrlIfPostShareTarget(WebappInfo webappInfo) {
WebApkInfo webApkInfo = (WebApkInfo) webappInfo;
return WebApkPostShareTargetNavigator.navigateIfPostShareTarget(
webApkInfo, getActivityTab().getWebContents());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IntDef;
Expand All @@ -33,6 +34,7 @@

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -41,6 +43,14 @@
* Stores info for WebAPK.
*/
public class WebApkInfo extends WebappInfo {
// A class that stores share information from share intent.
protected static class ShareData {
public String subject;
public String text;
public ArrayList<Uri> files;
public String shareActivityClassName;
}

public static final String RESOURCE_NAME = "name";
public static final String RESOURCE_SHORT_NAME = "short_name";
public static final String RESOURCE_STRING_TYPE = "string";
Expand All @@ -67,6 +77,8 @@ public class WebApkInfo extends WebappInfo {
private Map<String, String> mIconUrlToMurmur2HashMap;
private boolean mUseTransparentSplash;

private ShareData mShareData;

public static WebApkInfo createEmpty() {
return new WebApkInfo();
}
Expand All @@ -79,6 +91,7 @@ public static WebApkInfo createEmpty() {
public static WebApkInfo create(Intent intent) {
String webApkPackageName =
IntentUtils.safeGetStringExtra(intent, WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME);

if (TextUtils.isEmpty(webApkPackageName)) {
return null;
}
Expand All @@ -99,11 +112,32 @@ public static WebApkInfo create(Intent intent) {
boolean forceNavigation = IntentUtils.safeGetBooleanExtra(
intent, ShortcutHelper.EXTRA_FORCE_NAVIGATION, true);

ShareData shareData = null;

String shareActivityClassName = IntentUtils.safeGetStringExtra(
intent, WebApkConstants.EXTRA_WEBAPK_SELECTED_SHARE_TARGET_ACTIVITY_CLASS_NAME);

// Share Target when shareActivityClassName is present.
if (!TextUtils.isEmpty(shareActivityClassName)) {
shareData = new ShareData();
shareData.shareActivityClassName = shareActivityClassName;
shareData.subject = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_SUBJECT);
shareData.text = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_TEXT);
shareData.files = IntentUtils.getParcelableArrayListExtra(intent, Intent.EXTRA_STREAM);
if (shareData.files == null) {
Uri file = IntentUtils.safeGetParcelableExtra(intent, Intent.EXTRA_STREAM);
if (file != null) {
shareData.files = new ArrayList<>();
shareData.files.add(file);
}
}
}
boolean useTransparentSplash = !IntentUtils.isIntentForNewTaskOrNewDocument(intent)
&& IntentUtils.safeGetBooleanExtra(
intent, WebApkConstants.EXTRA_USE_TRANSPARENT_SPLASH, false);

return create(webApkPackageName, url, source, forceNavigation, useTransparentSplash);
return create(
webApkPackageName, url, source, forceNavigation, useTransparentSplash, shareData);
}

private static @WebApkDistributor int getDistributor(Bundle bundle, String packageName) {
Expand Down Expand Up @@ -133,9 +167,10 @@ public static WebApkInfo create(Intent intent) {
* running.
* @param useTransparentSplash Whether the WebApkActivity should be fully transparent while the
* page is loading.
* @param shareData Shared information from the share intent.
*/
public static WebApkInfo create(String webApkPackageName, String url, int source,
boolean forceNavigation, boolean useTransparentSplash) {
boolean forceNavigation, boolean useTransparentSplash, ShareData shareData) {
// Unlike non-WebAPK web apps, WebAPK ids are predictable. A malicious actor may send an
// intent with a valid start URL and arbitrary other data. Only use the start URL, the
// package name and the ShortcutSource from the launch intent and extract the remaining data
Expand Down Expand Up @@ -202,7 +237,7 @@ public static WebApkInfo create(String webApkPackageName, String url, int source
displayMode, orientation, source, themeColor, backgroundColor, webApkPackageName,
shellApkVersion, manifestUrl, manifestStartUrl, distributor,
iconUrlToMurmur2HashMap, serializedShareTarget, forceNavigation,
useTransparentSplash);
useTransparentSplash, shareData);
}

/**
Expand Down Expand Up @@ -236,14 +271,15 @@ public static WebApkInfo create(String webApkPackageName, String url, int source
* WebAPK is already open.
* @param useTransparentSplash Whether the WebApkActivity should be fully transparent while
* the page is loading.
* @param shareData Shared information from the share intent.
*/
public static WebApkInfo create(String id, String url, String scope, Icon primaryIcon,
Icon badgeIcon, Icon splashIcon, String name, String shortName,
@WebDisplayMode int displayMode, int orientation, int source, long themeColor,
long backgroundColor, String webApkPackageName, int shellApkVersion, String manifestUrl,
String manifestStartUrl, @WebApkDistributor int distributor,
Map<String, String> iconUrlToMurmur2HashMap, String serializedShareTarget,
boolean forceNavigation, boolean useTransparentSplash) {
boolean forceNavigation, boolean useTransparentSplash, ShareData shareData) {
if (id == null || url == null || manifestStartUrl == null || webApkPackageName == null) {
Log.e(TAG,
"Incomplete data provided: " + id + ", " + url + ", " + manifestStartUrl + ", "
Expand All @@ -262,7 +298,7 @@ public static WebApkInfo create(String id, String url, String scope, Icon primar
displayMode, orientation, source, themeColor, backgroundColor, webApkPackageName,
shellApkVersion, manifestUrl, manifestStartUrl, distributor,
iconUrlToMurmur2HashMap, serializedShareTarget, forceNavigation,
useTransparentSplash);
useTransparentSplash, shareData);
}

protected WebApkInfo(String id, String url, String scope, Icon primaryIcon, Icon badgeIcon,
Expand All @@ -271,7 +307,7 @@ protected WebApkInfo(String id, String url, String scope, Icon primaryIcon, Icon
String webApkPackageName, int shellApkVersion, String manifestUrl,
String manifestStartUrl, @WebApkDistributor int distributor,
Map<String, String> iconUrlToMurmur2HashMap, String serializedShareTarget,
boolean forceNavigation, boolean useTransparentSplash) {
boolean forceNavigation, boolean useTransparentSplash, ShareData shareData) {
super(id, url, scope, primaryIcon, name, shortName, displayMode, orientation, source,
themeColor, backgroundColor, null /* splash_screen_url */,
false /* isIconGenerated */, forceNavigation);
Expand All @@ -285,6 +321,7 @@ protected WebApkInfo(String id, String url, String scope, Icon primaryIcon, Icon
mIconUrlToMurmur2HashMap = iconUrlToMurmur2HashMap;
mSerializedShareTarget = serializedShareTarget;
mUseTransparentSplash = useTransparentSplash;
mShareData = shareData;
}

protected WebApkInfo() {}
Expand Down Expand Up @@ -345,6 +382,10 @@ public Map<String, String> iconUrlToMurmur2HashMap() {
return mIconUrlToMurmur2HashMap;
}

public ShareData shareData() {
return mShareData;
}

@Override
public void setWebappIntentExtras(Intent intent) {
// For launching a {@link WebApkActivity}.
Expand Down Expand Up @@ -489,19 +530,26 @@ static String extractSerializedShareTarget(String webApkPackageName) {

Bundle metaData = activityInfo.metaData;
return getSerializedShareTarget(metaData.getString(WebApkMetaDataKeys.SHARE_ACTION),
metaData.getString(WebApkMetaDataKeys.SHARE_METHOD),
metaData.getString(WebApkMetaDataKeys.SHARE_ENCTYPE),
metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TITLE),
metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TEXT),
metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_URL));
metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_URL),
metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_NAMES),
metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_ACCEPTS));
}

/**
* Returns the serialized Share Target String.
*/
static String getSerializedShareTarget(String shareAction, String shareParamsTitle,
String shareParamsText, String shareParamsUrl) {
static String getSerializedShareTarget(String shareAction, String shareMethod,
String shareEnctype, String shareParamsTitle, String shareParamsText,
String shareParamsUrl, String shareParamsNames, String shareParamsAccepts) {
if (shareAction == null) return null;

return String.format("action: \"%s\", title: \"%s\", text: \"%s\", url: \"%s\"",
shareAction, shareParamsTitle, shareParamsText, shareParamsUrl);
return String.format("action: \"%s\", method: \"%s\", enctype: \"%s\", title: \"%s\""
+ "text: \"%s\", url: \"%s\", names: \"%s\", accepts: \"%s\"",
shareAction, shareMethod, shareEnctype, shareParamsTitle, shareParamsText,
shareParamsUrl, shareParamsNames, shareParamsAccepts);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.webapps;

import org.chromium.content_public.browser.WebContents;

/**
* Perform navigation for share target with POST request.
*/
public class WebApkPostShareTargetNavigator {
public static boolean navigateIfPostShareTarget(
WebApkInfo webApkInfo, WebContents webContents) {
WebApkShareTargetUtil.PostData postData = WebApkShareTargetUtil.computePostData(
webApkInfo.webApkPackageName(), webApkInfo.shareData());
if (postData == null) {
return false;
}
nativeLoadViewForShareTargetPost(postData.isMultipartEncoding, postData.names,
postData.values, postData.filenames, postData.types, webApkInfo.uri().toString(),
webContents);
return true;
}

private static native void nativeLoadViewForShareTargetPost(boolean isMultipartEncoding,
String[] names, byte[][] values, String[] filenames, String[] types, String startUrl,
WebContents webContents);
}
Loading

0 comments on commit c4740ea

Please sign in to comment.