зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1402369 - Add WebShare support to GeckoView. r=geckoview-reviewers,snorp,marcosc,esawin
Differential Revision: https://phabricator.services.mozilla.com/D49614 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1824e6f651
Коммит
195f623a27
|
@ -1402,7 +1402,7 @@ Promise* Navigator::Share(const ShareData& aData, ErrorResult& aRv) {
|
|||
// The spec does the "triggered by user activation" after the data checks.
|
||||
// Unfortunately, both Chrome and Safari behave this way, so interop wins.
|
||||
// https://github.com/w3c/web-share/pull/118
|
||||
if (!UserActivation::IsHandlingUserInput()) {
|
||||
if (StaticPrefs::dom_webshare_requireinteraction() && !UserActivation::IsHandlingUserInput()) {
|
||||
NS_WARNING("Attempt to share not triggered by user activation");
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
|
|
|
@ -387,3 +387,5 @@ MathML_DeprecatedStyleAttributeWarning=MathML attributes “background”, “co
|
|||
MathML_DeprecatedXLinkAttributeWarning=XLink attributes “href”, “type”, “show” and “actuate” are deprecated on MathML elements and will be removed at a future date.
|
||||
# LOCALIZATION NOTE: Do not translate title, text, url as they are the names of JS properties.
|
||||
WebShareAPI_NeedOneMember=title or text or url member of the ShareData dictionary. At least one of the members is required.
|
||||
WebShareAPI_Failed=The share operation has failed.
|
||||
WebShareAPI_Aborted=The share operation was aborted.
|
||||
|
|
|
@ -38,6 +38,9 @@ pref("geckoview.console.enabled", false);
|
|||
pref("geckoview.logging", "Debug");
|
||||
#endif
|
||||
|
||||
// Enable WebShare support.
|
||||
pref("dom.webshare.enabled", true);
|
||||
|
||||
// Enable capture attribute for file input.
|
||||
pref("dom.capture.enabled", true);
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ component {aa0dd6fc-73dd-4621-8385-c0b377e02cee} GeckoViewPrompt.js process=main
|
|||
contract @mozilla.org/colorpicker;1 {aa0dd6fc-73dd-4621-8385-c0b377e02cee} process=main
|
||||
component {e4565e36-f101-4bf5-950b-4be0887785a9} GeckoViewPrompt.js process=main
|
||||
contract @mozilla.org/filepicker;1 {e4565e36-f101-4bf5-950b-4be0887785a9} process=main
|
||||
component {1201d357-8417-4926-a694-e6408fbedcf8} GeckoViewPrompt.js process=main
|
||||
contract @mozilla.org/sharepicker;1 {1201d357-8417-4926-a694-e6408fbedcf8} process=main
|
||||
|
||||
# GeckoViewExternalAppService.js
|
||||
component {a89eeec6-6608-42ee-a4f8-04d425992f45} GeckoViewExternalAppService.js
|
||||
|
|
|
@ -20,6 +20,10 @@ XPCOMUtils.defineLazyServiceGetter(
|
|||
"nsIUUIDGenerator"
|
||||
);
|
||||
|
||||
const domBundle = Services.strings.createBundle(
|
||||
"chrome://global/locale/dom/dom.properties"
|
||||
);
|
||||
|
||||
function PromptFactory() {
|
||||
this.wrappedJSObject = this;
|
||||
}
|
||||
|
@ -1127,8 +1131,68 @@ ColorPickerDelegate.prototype = {
|
|||
},
|
||||
};
|
||||
|
||||
function ShareDelegate() {}
|
||||
|
||||
ShareDelegate.prototype = {
|
||||
classID: Components.ID("{1201d357-8417-4926-a694-e6408fbedcf8}"),
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsISharePicker]),
|
||||
|
||||
init: function(aParent) {
|
||||
this._openerWindow = aParent;
|
||||
},
|
||||
|
||||
get openerWindow() {
|
||||
return this._openerWindow;
|
||||
},
|
||||
|
||||
async share(aTitle, aText, aUri) {
|
||||
const ABORT = 2;
|
||||
const FAILURE = 1;
|
||||
const SUCCESS = 0;
|
||||
|
||||
const msg = {
|
||||
type: "share",
|
||||
title: aTitle,
|
||||
text: aText,
|
||||
uri: aUri ? aUri.displaySpec : null,
|
||||
};
|
||||
const prompt = new PromptDelegate(this._openerWindow);
|
||||
const result = await new Promise(resolve => {
|
||||
prompt.asyncShowPrompt(msg, resolve);
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
// A null result is treated as a dismissal in PromptDelegate.
|
||||
throw new DOMException(
|
||||
domBundle.GetStringFromName("WebShareAPI_Aborted"),
|
||||
"AbortError"
|
||||
);
|
||||
}
|
||||
|
||||
const res = result && result.response;
|
||||
switch (res) {
|
||||
case FAILURE:
|
||||
throw new DOMException(
|
||||
domBundle.GetStringFromName("WebShareAPI_Failed"),
|
||||
"DataError"
|
||||
);
|
||||
case ABORT: // Handle aborted attempt and invalid responses the same.
|
||||
throw new DOMException(
|
||||
domBundle.GetStringFromName("WebShareAPI_Aborted"),
|
||||
"AbortError"
|
||||
);
|
||||
case SUCCESS:
|
||||
return;
|
||||
default:
|
||||
throw new DOMException("Unknown error.", "UnknownError");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
|
||||
ColorPickerDelegate,
|
||||
FilePickerDelegate,
|
||||
PromptFactory,
|
||||
ShareDelegate,
|
||||
]);
|
||||
|
|
|
@ -809,6 +809,7 @@ package org.mozilla.geckoview {
|
|||
method @UiThread @Nullable default public GeckoResult<GeckoSession.PromptDelegate.PromptResponse> onDateTimePrompt(@NonNull GeckoSession, @NonNull GeckoSession.PromptDelegate.DateTimePrompt);
|
||||
method @UiThread @Nullable default public GeckoResult<GeckoSession.PromptDelegate.PromptResponse> onFilePrompt(@NonNull GeckoSession, @NonNull GeckoSession.PromptDelegate.FilePrompt);
|
||||
method @UiThread @Nullable default public GeckoResult<GeckoSession.PromptDelegate.PromptResponse> onPopupPrompt(@NonNull GeckoSession, @NonNull GeckoSession.PromptDelegate.PopupPrompt);
|
||||
method @UiThread @Nullable default public GeckoResult<GeckoSession.PromptDelegate.PromptResponse> onSharePrompt(@NonNull GeckoSession, @NonNull GeckoSession.PromptDelegate.SharePrompt);
|
||||
method @UiThread @Nullable default public GeckoResult<GeckoSession.PromptDelegate.PromptResponse> onTextPrompt(@NonNull GeckoSession, @NonNull GeckoSession.PromptDelegate.TextPrompt);
|
||||
}
|
||||
|
||||
|
@ -954,6 +955,20 @@ package org.mozilla.geckoview {
|
|||
public static class GeckoSession.PromptDelegate.PromptResponse {
|
||||
}
|
||||
|
||||
public static class GeckoSession.PromptDelegate.SharePrompt extends GeckoSession.PromptDelegate.BasePrompt {
|
||||
ctor protected SharePrompt(@Nullable String, @Nullable String, @Nullable String);
|
||||
method @UiThread @NonNull public GeckoSession.PromptDelegate.PromptResponse confirm(int);
|
||||
field @Nullable public final String text;
|
||||
field @Nullable public final String uri;
|
||||
}
|
||||
|
||||
public static class GeckoSession.PromptDelegate.SharePrompt.Result {
|
||||
ctor protected Result();
|
||||
field public static final int ABORT = 2;
|
||||
field public static final int FAILURE = 1;
|
||||
field public static final int SUCCESS = 0;
|
||||
}
|
||||
|
||||
public static class GeckoSession.PromptDelegate.TextPrompt extends GeckoSession.PromptDelegate.BasePrompt {
|
||||
ctor protected TextPrompt(@Nullable String, @Nullable String, @Nullable String);
|
||||
method @UiThread @NonNull public GeckoSession.PromptDelegate.PromptResponse confirm(@NonNull String);
|
||||
|
|
|
@ -5,12 +5,14 @@ import org.mozilla.geckoview.GeckoResult
|
|||
import org.mozilla.geckoview.GeckoSession
|
||||
import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
|
||||
import org.mozilla.geckoview.GeckoSession.PromptDelegate
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
|
||||
import org.mozilla.geckoview.test.util.Callbacks
|
||||
|
||||
import android.support.test.filters.MediumTest
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.junit.Assert
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -254,5 +256,216 @@ class PromptDelegateTest : BaseSessionTest() {
|
|||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shareTextSucceeds() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareText = "Example share text"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
assertThat("Text field is not null", prompt.text, notNullValue())
|
||||
assertThat("Title field is null", prompt.title, nullValue())
|
||||
assertThat("Url field is null", prompt.uri, nullValue())
|
||||
assertThat("Text field contains correct value", prompt.text, equalTo(shareText))
|
||||
return GeckoResult.fromValue(prompt.confirm(PromptDelegate.SharePrompt.Result.SUCCESS))
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({text: "${shareText}"})""")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
Assert.fail("Share must succeed." + e.reason as String)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shareUrlSucceeds() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareUrl = "https://example.com/"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
assertThat("Text field is null", prompt.text, nullValue())
|
||||
assertThat("Title field is null", prompt.title, nullValue())
|
||||
assertThat("Url field is not null", prompt.uri, notNullValue())
|
||||
assertThat("Text field contains correct value", prompt.uri, equalTo(shareUrl))
|
||||
return GeckoResult.fromValue(prompt.confirm(PromptDelegate.SharePrompt.Result.SUCCESS))
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({url: "${shareUrl}"})""")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
Assert.fail("Share must succeed." + e.reason as String)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shareTitleSucceeds() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareTitle = "Title!"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
assertThat("Text field is null", prompt.text, nullValue())
|
||||
assertThat("Title field is not null", prompt.title, notNullValue())
|
||||
assertThat("Url field is null", prompt.uri, nullValue())
|
||||
assertThat("Text field contains correct value", prompt.title, equalTo(shareTitle))
|
||||
return GeckoResult.fromValue(prompt.confirm(PromptDelegate.SharePrompt.Result.SUCCESS))
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({title: "${shareTitle}"})""")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
Assert.fail("Share must succeed." + e.reason as String)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun failedShareReturnsDataError() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareUrl = "https://www.example.com"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
return GeckoResult.fromValue(prompt.confirm(PromptDelegate.SharePrompt.Result.FAILURE))
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({url: "${shareUrl}"})""")
|
||||
Assert.fail("Request should have failed")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
assertThat("Error should be correct",
|
||||
e.reason as String, containsString("DataError"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun abortedShareReturnsAbortError() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareUrl = "https://www.example.com"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
return GeckoResult.fromValue(prompt.confirm(PromptDelegate.SharePrompt.Result.ABORT))
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({url: "${shareUrl}"})""")
|
||||
Assert.fail("Request should have failed")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
assertThat("Error should be correct",
|
||||
e.reason as String, containsString("AbortError"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun dismissedShareReturnsAbortError() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareUrl = "https://www.example.com"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
return GeckoResult.fromValue(prompt.dismiss())
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({url: "${shareUrl}"})""")
|
||||
Assert.fail("Request should have failed")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
assertThat("Error should be correct",
|
||||
e.reason as String, containsString("AbortError"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun emptyShareReturnsTypeError() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 0)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
return GeckoResult.fromValue(prompt.dismiss())
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({})""")
|
||||
Assert.fail("Request should have failed")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
assertThat("Error should be correct",
|
||||
e.reason as String, containsString("TypeError"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun invalidShareUrlReturnsTypeError() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to false))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
// Invalid port should cause URL parser to fail.
|
||||
val shareUrl = "http://www.example.com:123456"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 0)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
return GeckoResult.fromValue(prompt.dismiss())
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({url: "${shareUrl}"})""")
|
||||
Assert.fail("Request should have failed")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
assertThat("Error should be correct",
|
||||
e.reason as String, containsString("TypeError"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun shareRequiresUserInteraction() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("dom.webshare.requireinteraction" to true))
|
||||
mainSession.loadTestPath(HELLO_HTML_PATH)
|
||||
mainSession.waitForPageStop()
|
||||
|
||||
val shareUrl = "https://www.example.com"
|
||||
|
||||
sessionRule.delegateDuringNextWait(object : Callbacks.PromptDelegate {
|
||||
@AssertCalled(count = 0)
|
||||
override fun onSharePrompt(session: GeckoSession, prompt: PromptDelegate.SharePrompt): GeckoResult<PromptDelegate.PromptResponse>? {
|
||||
return GeckoResult.fromValue(prompt.dismiss())
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
mainSession.waitForJS("""window.navigator.share({url: "${shareUrl}"})""")
|
||||
Assert.fail("Request should have failed")
|
||||
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
|
||||
assertThat("Error should be correct",
|
||||
e.reason as String, containsString("NotAllowedError"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2738,6 +2738,14 @@ public class GeckoSession implements Parcelable {
|
|||
res = delegate.onPopupPrompt(session, prompt);
|
||||
break;
|
||||
}
|
||||
case "share": {
|
||||
final String text = message.getString("text");
|
||||
final String uri = message.getString("uri");
|
||||
final PromptDelegate.SharePrompt prompt =
|
||||
new PromptDelegate.SharePrompt(title, text, uri);
|
||||
res = delegate.onSharePrompt(session, prompt);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
callback.sendError("Invalid type");
|
||||
return;
|
||||
|
@ -4413,6 +4421,82 @@ public class GeckoSession implements Parcelable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SharePrompt contains the information necessary to represent a (v1) WebShare request.
|
||||
*/
|
||||
public class SharePrompt extends BasePrompt {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({Result.SUCCESS, Result.FAILURE, Result.ABORT})
|
||||
/* package */ @interface ShareResult {}
|
||||
|
||||
/**
|
||||
* Possible results to a {@link SharePrompt}.
|
||||
*/
|
||||
public static class Result {
|
||||
/**
|
||||
* The user shared with another app successfully.
|
||||
*/
|
||||
public static final int SUCCESS = 0;
|
||||
|
||||
/**
|
||||
* The user attempted to share with another app, but it failed.
|
||||
*/
|
||||
public static final int FAILURE = 1;
|
||||
|
||||
/**
|
||||
* The user aborted the share.
|
||||
*/
|
||||
public static final int ABORT = 2;
|
||||
|
||||
protected Result() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* The text for the share request.
|
||||
*/
|
||||
public final @Nullable String text;
|
||||
|
||||
/**
|
||||
* The uri for the share request.
|
||||
*/
|
||||
public final @Nullable String uri;
|
||||
|
||||
protected SharePrompt(@Nullable final String title,
|
||||
@Nullable final String text,
|
||||
@Nullable final String uri) {
|
||||
super(title);
|
||||
this.text = text;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms the prompt and either blocks or allows the share request.
|
||||
*
|
||||
* @param response One of {@link Result} specifying the outcome of the
|
||||
* share attempt.
|
||||
*
|
||||
* @return A {@link PromptResponse} which can be used to complete the
|
||||
* {@link GeckoResult} associated with this prompt.
|
||||
*/
|
||||
@UiThread
|
||||
public @NonNull PromptResponse confirm(@ShareResult final int response) {
|
||||
ensureResult().putInt("response", response);
|
||||
return super.confirm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the prompt and returns {@link Result#ABORT} to web content.
|
||||
*
|
||||
* @return A {@link PromptResponse} which can be used to complete the
|
||||
* {@link GeckoResult} associated with this prompt.
|
||||
*/
|
||||
@UiThread
|
||||
public @NonNull PromptResponse dismiss() {
|
||||
ensureResult().putInt("response", Result.ABORT);
|
||||
return super.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate functions.
|
||||
/**
|
||||
* Display an alert prompt.
|
||||
|
@ -4549,6 +4633,23 @@ public class GeckoSession implements Parcelable {
|
|||
@NonNull final PopupPrompt prompt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a share request prompt; this occurs when content attempts to use the
|
||||
* WebShare API.
|
||||
* See: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share
|
||||
*
|
||||
* @param session GeckoSession that triggered the prompt.
|
||||
* @param prompt The {@link SharePrompt} that describes the prompt.
|
||||
*
|
||||
* @return A {@link GeckoResult} resolving to a {@link PromptResponse} which
|
||||
* includes all necessary information to resolve the prompt.
|
||||
*/
|
||||
@UiThread
|
||||
default @Nullable GeckoResult<PromptResponse> onSharePrompt(@NonNull final GeckoSession session,
|
||||
@NonNull final SharePrompt prompt) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,6 +56,8 @@ exclude: true
|
|||
to the session it holds.
|
||||
- Changed [`AutofillElement.children`][71.20] interface to `Collection` to provide
|
||||
an efficient way to pre-allocate memory when filling `ViewStructure`.
|
||||
- Added [`GeckoSession.PromptDelegate.onSharePrompt`][71.22] to support the WebShare API.
|
||||
([bug 1402369]({{bugzilla}}1402369))
|
||||
|
||||
[71.1]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html#onBooleanScalar-org.mozilla.geckoview.RuntimeTelemetry.Metric-
|
||||
[71.2]: {{javadoc_uri}}/RuntimeTelemetry.Delegate.html#onLongScalar-org.mozilla.geckoview.RuntimeTelemetry.Metric-
|
||||
|
@ -77,6 +79,7 @@ exclude: true
|
|||
[71.19]: {{javadoc_uri}}/GeckoSession.html#getAutofillElements--
|
||||
[71.20]: {{javadoc_uri}}/AutofillElement.html
|
||||
[71.21]: {{javadoc_uri}}/GeckoView.html#setAutofillEnabled-boolean-
|
||||
[71.22]: {{javadoc_uri}}/GeckoSession.PromptDelegate.html#onSharePrompt-org.mozilla.geckoview.GeckoSession-org.mozilla.geckoview.GeckoSession.PromptDelegate.SharePrompt-
|
||||
|
||||
## v70
|
||||
- Added API for session context assignment
|
||||
|
@ -399,4 +402,4 @@ exclude: true
|
|||
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
|
||||
[65.25]: {{javadoc_uri}}/GeckoResult.html
|
||||
|
||||
[api-version]: ee3ceb65db78c3a801f525465ff3c6e9eca22ae9
|
||||
[api-version]: 6a71a9226b15eb40fb47f5da7400915f29fb4986
|
||||
|
|
|
@ -114,6 +114,12 @@ final class BasicGeckoViewPrompt implements GeckoSession.PromptDelegate {
|
|||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeckoResult<PromptResponse> onSharePrompt(final GeckoSession session,
|
||||
final SharePrompt prompt) {
|
||||
return GeckoResult.fromValue(prompt.dismiss());
|
||||
}
|
||||
|
||||
private int getViewPadding(final AlertDialog.Builder builder) {
|
||||
final TypedArray attr = builder.getContext().obtainStyledAttributes(
|
||||
new int[] { android.R.attr.listPreferredItemPaddingLeft });
|
||||
|
|
|
@ -2726,6 +2726,12 @@
|
|||
value: false
|
||||
mirror: always
|
||||
|
||||
# WebShare API - allows WebShare without user interaction (for tests only).
|
||||
- name: dom.webshare.requireinteraction
|
||||
type: bool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Prefs starting with "editor"
|
||||
#---------------------------------------------------------------------------
|
||||
|
|
Загрузка…
Ссылка в новой задаче