Backed out 3 changesets (bug 1667471) for perma failures on NavigationDelegateTest. CLOSED TREE

Backed out changeset cfd11957a8a0 (bug 1667471)
Backed out changeset fdc015927934 (bug 1667471)
Backed out changeset ad8b66ec5171 (bug 1667471)
This commit is contained in:
Razvan Maries 2020-10-15 01:53:02 +03:00
Родитель 87e692949a
Коммит 99ae18f920
9 изменённых файлов: 153 добавлений и 591 удалений

Просмотреть файл

@ -694,15 +694,6 @@ nsDOMWindowUtils::SendMouseEventCommon(
aPreventDefault, aIsDOMEventSynthesized, aIsWidgetEventSynthesized);
}
NS_IMETHODIMP
nsDOMWindowUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
const nsACString& aValue,
bool* aRetVal) {
NS_ENSURE_ARG_POINTER(aRetVal);
*aRetVal = nsContentUtils::IsCORSSafelistedRequestHeader(aName, aValue);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SendWheelEvent(float aX, float aY, double aDeltaX,
double aDeltaY, double aDeltaZ,

Просмотреть файл

@ -266,12 +266,6 @@ interface nsIDOMWindowUtils : nsISupports {
uint32_t getPresShellId();
/**
* Returns whether a given header and value is a CORS-safelisted request
* header per https://fetch.spec.whatwg.org/#cors-safelisted-request-header
*/
boolean isCORSSafelistedRequestHeader(in ACString name, in ACString value);
/**
* Following modifiers are for sent*Event() except sendNative*Event().
* NOTE: MODIFIER_ALT, MODIFIER_CONTROL, MODIFIER_SHIFT and MODIFIER_META

Просмотреть файл

@ -697,8 +697,8 @@ package org.mozilla.geckoview {
method @UiThread @NonNull public GeckoDisplay acquireDisplay();
method @UiThread public void autofill(@NonNull SparseArray<CharSequence>);
method @UiThread public void close();
method @AnyThread @Deprecated @NonNull public static String createDataUri(@NonNull byte[], @Nullable String);
method @AnyThread @Deprecated @NonNull public static String createDataUri(@NonNull String, @Nullable String);
method @AnyThread @NonNull public static String createDataUri(@NonNull byte[], @Nullable String);
method @AnyThread @NonNull public static String createDataUri(@NonNull String, @Nullable String);
method @AnyThread public void exitFullScreen();
method @UiThread @NonNull public SessionAccessibility getAccessibility();
method @UiThread @Nullable public Autofill.Delegate getAutofillDelegate();
@ -733,21 +733,20 @@ package org.mozilla.geckoview {
method @AnyThread public void goForward();
method @AnyThread public void gotoHistoryIndex(int);
method @AnyThread public boolean isOpen();
method @AnyThread public void load(@NonNull GeckoSession.Loader);
method @AnyThread @Deprecated public void loadData(@NonNull byte[], @Nullable String);
method @AnyThread @Deprecated public void loadString(@NonNull String, @Nullable String);
method @AnyThread public void loadData(@NonNull byte[], @Nullable String);
method @AnyThread public void loadString(@NonNull String, @Nullable String);
method @AnyThread public void loadUri(@NonNull String);
method @AnyThread @Deprecated public void loadUri(@NonNull String, @Nullable Map<String,String>);
method @AnyThread @Deprecated public void loadUri(@NonNull String, int);
method @AnyThread @Deprecated public void loadUri(@NonNull String, @Nullable String, int);
method @AnyThread @Deprecated public void loadUri(@NonNull String, @Nullable String, int, @Nullable Map<String,String>);
method @AnyThread @Deprecated public void loadUri(@NonNull String, @Nullable GeckoSession, int);
method @AnyThread @Deprecated public void loadUri(@NonNull String, @Nullable GeckoSession, int, @Nullable Map<String,String>);
method @AnyThread @Deprecated public void loadUri(@NonNull Uri);
method @AnyThread @Deprecated public void loadUri(@NonNull Uri, @Nullable Map<String,String>);
method @AnyThread @Deprecated public void loadUri(@NonNull Uri, int);
method @AnyThread @Deprecated public void loadUri(@NonNull Uri, @Nullable Uri, int);
method @AnyThread @Deprecated public void loadUri(@NonNull Uri, @Nullable Uri, int, @Nullable Map<String,String>);
method @AnyThread public void loadUri(@NonNull String, @Nullable Map<String,String>);
method @AnyThread public void loadUri(@NonNull String, int);
method @AnyThread public void loadUri(@NonNull String, @Nullable String, int);
method @AnyThread public void loadUri(@NonNull String, @Nullable String, int, @Nullable Map<String,String>);
method @AnyThread public void loadUri(@NonNull String, @Nullable GeckoSession, int);
method @AnyThread public void loadUri(@NonNull String, @Nullable GeckoSession, int, @Nullable Map<String,String>);
method @AnyThread public void loadUri(@NonNull Uri);
method @AnyThread public void loadUri(@NonNull Uri, @Nullable Map<String,String>);
method @AnyThread public void loadUri(@NonNull Uri, int);
method @AnyThread public void loadUri(@NonNull Uri, @Nullable Uri, int);
method @AnyThread public void loadUri(@NonNull Uri, @Nullable Uri, int, @Nullable Map<String,String>);
method @UiThread public void open(@NonNull GeckoRuntime);
method @AnyThread public void purgeHistory();
method @UiThread public void releaseDisplay(@NonNull GeckoDisplay);
@ -777,8 +776,6 @@ package org.mozilla.geckoview {
field public static final int FINDER_FIND_LINKS_ONLY = 8;
field public static final int FINDER_FIND_MATCH_CASE = 2;
field public static final int FINDER_FIND_WHOLE_WORD = 4;
field public static final int HEADER_FILTER_CORS_SAFELISTED = 1;
field public static final int HEADER_FILTER_UNRESTRICTED_UNSAFE = 2;
field public static final int LOAD_FLAGS_ALLOW_POPUPS = 8;
field public static final int LOAD_FLAGS_BYPASS_CACHE = 1;
field public static final int LOAD_FLAGS_BYPASS_CLASSIFIER = 16;
@ -855,20 +852,6 @@ package org.mozilla.geckoview {
method @AnyThread default public int getCurrentIndex();
}
@AnyThread public static class GeckoSession.Loader {
ctor public Loader();
method @NonNull public GeckoSession.Loader additionalHeaders(@NonNull Map<String,String>);
method @NonNull public GeckoSession.Loader data(@NonNull byte[], @Nullable String);
method @NonNull public GeckoSession.Loader data(@NonNull String, @Nullable String);
method @NonNull public GeckoSession.Loader flags(int);
method @NonNull public GeckoSession.Loader headerFilter(int);
method @NonNull public GeckoSession.Loader referrer(@NonNull GeckoSession);
method @NonNull public GeckoSession.Loader referrer(@NonNull Uri);
method @NonNull public GeckoSession.Loader referrer(@NonNull String);
method @NonNull public GeckoSession.Loader uri(@NonNull String);
method @NonNull public GeckoSession.Loader uri(@NonNull Uri);
}
public static interface GeckoSession.MediaDelegate {
method @UiThread default public void onMediaAdd(@NonNull GeckoSession, @NonNull MediaElement);
method @UiThread default public void onMediaRemove(@NonNull GeckoSession, @NonNull MediaElement);

Просмотреть файл

@ -4,21 +4,26 @@
package org.mozilla.geckoview.test
import android.util.Base64
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.AssertCalled
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.NullDelegate
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDisplay
import org.mozilla.geckoview.test.util.Callbacks
import androidx.test.filters.MediumTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.MatcherAssert
import org.hamcrest.Matchers.*
import org.json.JSONObject
import org.junit.Assert
import org.junit.Assume.assumeThat
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.geckoview.*
import org.mozilla.geckoview.GeckoSession.Loader
import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.*
import org.mozilla.geckoview.test.util.Callbacks
import org.mozilla.geckoview.test.util.UiThreadUtils
@RunWith(AndroidJUnit4::class)
@ -462,9 +467,8 @@ class NavigationDelegateTest : BaseSessionTest() {
sessionRule.runtime.settings.contentBlocking.setSafeBrowsing(category)
sessionRule.session.load(Loader()
.uri(phishingUri + "?bypass=true")
.flags(GeckoSession.LOAD_FLAGS_BYPASS_CLASSIFIER))
sessionRule.session.loadUri(phishingUri + "?bypass=true",
GeckoSession.LOAD_FLAGS_BYPASS_CLASSIFIER)
sessionRule.session.waitForPageStop()
sessionRule.forCallbacksDuringWait(
@ -859,7 +863,7 @@ class NavigationDelegateTest : BaseSessionTest() {
@Test fun loadString() {
val dataString = "<html><head><title>TheTitle</title></head><body>TheBody</body></html>"
val mimeType = "text/html"
sessionRule.session.load(Loader().data(dataString, mimeType))
sessionRule.session.loadString(dataString, mimeType)
sessionRule.waitForPageStop()
sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate, Callbacks.ContentDelegate {
@ -871,7 +875,7 @@ class NavigationDelegateTest : BaseSessionTest() {
@AssertCalled(count = 1)
override fun onLocationChange(session: GeckoSession, url: String?) {
assertThat("URL should be a data URL", url,
equalTo(createDataUri(dataString, mimeType)))
equalTo(GeckoSession.createDataUri(dataString, mimeType)))
}
@AssertCalled(count = 1)
@ -882,7 +886,7 @@ class NavigationDelegateTest : BaseSessionTest() {
}
@Test fun loadString_noMimeType() {
sessionRule.session.load(Loader().data("Hello, World!", null))
sessionRule.session.loadString("Hello, World!", null)
sessionRule.waitForPageStop()
sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
@ -902,7 +906,7 @@ class NavigationDelegateTest : BaseSessionTest() {
val bytes = getTestBytes(HELLO_HTML_PATH)
assertThat("test html should have data", bytes.size, greaterThan(0))
sessionRule.session.load(Loader().data(bytes, "text/html"))
sessionRule.session.loadData(bytes, "text/html")
sessionRule.waitForPageStop()
sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate, Callbacks.ContentDelegate {
@ -913,7 +917,7 @@ class NavigationDelegateTest : BaseSessionTest() {
@AssertCalled(count = 1)
override fun onLocationChange(session: GeckoSession, url: String?) {
assertThat("URL should match", url, equalTo(createDataUri(bytes, "text/html")))
assertThat("URL should match", url, equalTo(GeckoSession.createDataUri(bytes, "text/html")))
}
@AssertCalled(count = 1)
@ -923,28 +927,17 @@ class NavigationDelegateTest : BaseSessionTest() {
})
}
private fun createDataUri(data: String,
mimeType: String?): String {
return String.format("data:%s,%s", mimeType ?: "", data)
}
private fun createDataUri(bytes: ByteArray,
mimeType: String?): String {
return String.format("data:%s;base64,%s", mimeType ?: "",
Base64.encodeToString(bytes, Base64.NO_WRAP))
}
fun loadDataHelper(assetPath: String, mimeType: String? = null) {
val bytes = getTestBytes(assetPath)
assertThat("test data should have bytes", bytes.size, greaterThan(0))
sessionRule.session.load(Loader().data(bytes, mimeType))
sessionRule.session.loadData(bytes, mimeType)
sessionRule.waitForPageStop()
sessionRule.forCallbacksDuringWait(object : Callbacks.NavigationDelegate, Callbacks.ProgressDelegate {
@AssertCalled(count = 1)
override fun onLocationChange(session: GeckoSession, url: String?) {
assertThat("URL should match", url, equalTo(createDataUri(bytes, mimeType)))
assertThat("URL should match", url, equalTo(GeckoSession.createDataUri(bytes, mimeType)))
}
@AssertCalled(count = 1)
@ -1321,10 +1314,7 @@ class NavigationDelegateTest : BaseSessionTest() {
val uri = "https://example.com"
val referrer = "https://foo.org/"
sessionRule.session.load(Loader()
.uri(uri)
.referrer(referrer)
.flags(GeckoSession.LOAD_FLAGS_NONE))
sessionRule.session.loadUri(uri, referrer, GeckoSession.LOAD_FLAGS_NONE)
sessionRule.session.waitForPageStop()
assertThat("Referrer should match",
@ -1340,10 +1330,7 @@ class NavigationDelegateTest : BaseSessionTest() {
sessionRule.session.waitForPageStop()
val newSession = sessionRule.createOpenSession()
newSession.load(Loader()
.uri(uri)
.referrer(sessionRule.session)
.flags(GeckoSession.LOAD_FLAGS_NONE))
newSession.loadUri(uri, sessionRule.session, GeckoSession.LOAD_FLAGS_NONE)
newSession.waitForPageStop()
assertThat("Referrer should match",
@ -1359,10 +1346,7 @@ class NavigationDelegateTest : BaseSessionTest() {
sessionRule.session.waitForPageStop()
val newSession = sessionRule.createOpenSession()
newSession.load(Loader()
.uri(uri)
.referrer(sessionRule.session)
.flags(GeckoSession.LOAD_FLAGS_NONE))
newSession.loadUri(uri, sessionRule.session, GeckoSession.LOAD_FLAGS_NONE)
newSession.waitUntilCalled(object : Callbacks.NavigationDelegate {
@AssertCalled
override fun onLoadError(session: GeckoSession, uri: String?, error: WebRequestError): GeckoResult<String>? {
@ -1372,8 +1356,7 @@ class NavigationDelegateTest : BaseSessionTest() {
}
private fun loadUriHeaderTest(headers: Map<String?,String?>,
additional: Map<String?, String?>,
filter: Int = GeckoSession.HEADER_FILTER_CORS_SAFELISTED) {
additional: Map<String?, String?>) {
// First collect default headers with no override
sessionRule.session.loadUri("$TEST_ENDPOINT/anything")
sessionRule.session.waitForPageStop()
@ -1385,10 +1368,7 @@ class NavigationDelegateTest : BaseSessionTest() {
val expected = defaultHeaders.plus(additional)
// Now load the page with the header override
sessionRule.session.load(Loader()
.uri("$TEST_ENDPOINT/anything")
.additionalHeaders(headers)
.headerFilter(filter))
sessionRule.session.loadUri("$TEST_ENDPOINT/anything", headers)
sessionRule.session.waitForPageStop()
val content = sessionRule.session.evaluateJS("document.body.children[0].innerHTML") as String
@ -1402,12 +1382,7 @@ class NavigationDelegateTest : BaseSessionTest() {
// Basic test
loadUriHeaderTest(
mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
mapOf("Header1" to "Value", "Header2" to "Value1, Value2")
)
// Empty value headers are ignored
@ -1433,34 +1408,16 @@ class NavigationDelegateTest : BaseSessionTest() {
"what" to "what\r\nhost:amazon.com",
"Header3" to "Value1, Value2, Value3"
),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1" to "Value",
"Header2" to "Value1, Value2",
"this\r\nis invalid" to "test value",
"test key" to "this\r\n is a no-no",
"what" to "what\r\nhost:amazon.com",
"Header3" to "Value1, Value2, Value3"
),
mapOf("Header1" to "Value",
"Header2" to "Value1, Value2",
"Header3" to "Value1, Value2, Value3"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
"Header2" to "Value1, Value2",
"Header3" to "Value1, Value2, Value3")
)
loadUriHeaderTest(
mapOf("Header1" to "Value",
"Header2" to "Value1, Value2",
"what" to "what\r\nhost:amazon.com"),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1" to "Value",
"Header2" to "Value1, Value2",
"what" to "what\r\nhost:amazon.com"),
mapOf("Header1" to "Value", "Header2" to "Value1, Value2"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
mapOf("Header1" to "Value", "Header2" to "Value1, Value2")
)
loadUriHeaderTest(
@ -1476,42 +1433,22 @@ class NavigationDelegateTest : BaseSessionTest() {
// Connection and Host cannot be overriden, no matter the case spelling
loadUriHeaderTest(
mapOf("Header1" to "Value1", "ConnEction" to "test", "connection" to "test2"),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1" to "Value1", "ConnEction" to "test", "connection" to "test2"),
mapOf("Header1" to "Value1"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
mapOf("Header1" to "Value1")
)
loadUriHeaderTest(
mapOf("Header1" to "Value1", "connection" to "test2"),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1" to "Value1", "connection" to "test2"),
mapOf("Header1" to "Value1"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
mapOf("Header1" to "Value1")
)
loadUriHeaderTest(
mapOf("Header1 " to "Value1", "host" to "test2"),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1 " to "Value1", "host" to "test2"),
mapOf("Header1" to "Value1"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
mapOf("Header1" to "Value1")
)
loadUriHeaderTest(
mapOf("Header1" to "Value1", "host" to "test2"),
mapOf()
)
loadUriHeaderTest(
mapOf("Header1" to "Value1", "host" to "test2"),
mapOf("Header1" to "Value1"),
GeckoSession.HEADER_FILTER_UNRESTRICTED_UNSAFE
mapOf("Header1" to "Value1")
)
// Adding white space at the end of a forbidden header still prevents override
@ -1528,20 +1465,6 @@ class NavigationDelegateTest : BaseSessionTest() {
mapOf("abc\ra\n" to "amazon.com"),
mapOf()
)
// CORS Safelist test
loadUriHeaderTest(
mapOf("Content-Language" to "de-DE, en-CA",
"Content-Type" to "multipart/form-data; boundary=something",
"Accept" to "text/html",
"Accept-Language" to "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5"),
mapOf("Content-Type" to "multipart/form-data; boundary=something",
"Content-Language" to "de-DE, en-CA",
// TODO: Bug 1671294, headers should be replaced, not appended
"Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8, text/html",
// TODO: Bug 1671294, headers should be replaced, not appended
"Accept-Language" to "en-US, fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5")
)
}
@Test(expected = GeckoResult.UncaughtException::class)

Просмотреть файл

@ -4,7 +4,6 @@
package org.mozilla.geckoview.test
import android.util.Base64
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.MediumTest
@ -429,12 +428,6 @@ class ProgressDelegateTest : BaseSessionTest() {
})
}
private fun createDataUri(bytes: ByteArray,
mimeType: String?): String {
return String.format("data:%s;base64,%s", mimeType ?: "",
Base64.encodeToString(bytes, Base64.NO_WRAP))
}
@Test(expected = UiThreadUtils.TimeoutException::class)
fun handlingLargeDataURIs() {
sessionRule.delegateUntilTestEnd(object : Callbacks.ProgressDelegate {
@ -444,7 +437,7 @@ class ProgressDelegateTest : BaseSessionTest() {
});
val dataBytes = ByteArray(3 * 1024 * 1024)
val uri = createDataUri(dataBytes, "*/*")
val uri = GeckoSession.createDataUri(dataBytes, "*/*")
sessionRule.session.loadTestPath(DATA_URI_PATH)
sessionRule.session.waitForPageStop()
@ -462,7 +455,7 @@ class ProgressDelegateTest : BaseSessionTest() {
});
val dataBytes = this.getTestBytes("/assets/www/images/test.gif")
val uri = createDataUri(dataBytes, "image/*")
val uri = GeckoSession.createDataUri(dataBytes, "image/*")
sessionRule.session.loadTestPath(DATA_URI_PATH)
sessionRule.session.waitForPageStop()

Просмотреть файл

@ -1460,315 +1460,35 @@ public class GeckoSession {
*/
public static final int LOAD_FLAGS_REPLACE_HISTORY = 1 << 6;
/**
* Filter headers according to the CORS safelisted rules.
*
* See <a href="https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header">
* CORS-safelisted request header
* </a>.
*/
public static final int HEADER_FILTER_CORS_SAFELISTED = 1;
/**
* Allows most headers.
*
* Note: the <code>Host</code> and <code>Connection</code>
* headers are still ignored.
*
* This should only be used when input is hard-coded from the app or when
* properly sanitized, as some headers could cause unexpected consequences
* and security issues.
*
* Only use this if you know what you're doing.
*/
public static final int HEADER_FILTER_UNRESTRICTED_UNSAFE = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {HEADER_FILTER_CORS_SAFELISTED, HEADER_FILTER_UNRESTRICTED_UNSAFE})
/* package */ @interface HeaderFilter {}
/**
* Main entry point for loading URIs into a {@link GeckoSession}.
*
* The simplest use case is loading a URIs with no extra options, this can
* be accomplished by specifying the URI in {@link #uri} and then calling
* {@link #load}, e.g.
*
* <pre><code>
* session.load(new Loader().uri("http://mozilla.org"));
* </code></pre>
*
* This class can also be used to load <code>data:</code> URIs, either from
* a <code>byte[]</code> array or a <code>String</code> using {@link
* #data}, e.g.
*
* <pre><code>
* session.load(new Loader().data("the data:1234,5678", "text/plain"));
* </code></pre>
*
* This class also allows you to specify some extra data, e.g. you can set
* a referrer using {@link #referrer} which can either be a {@link
* GeckoSession} or a plain URL string. You can also specify some Load
* Flags using {@link #flags}.
*
* The class is structured as a Builder, so method calls can be easily
* chained, e.g.
*
* <pre><code>
* session.load(new Loader()
* .url("http://mozilla.org")
* .referrer("http://my-referrer.com")
* .flags(...));
* </code></pre>
*/
@AnyThread
public static class Loader {
private String mUri;
private GeckoSession mReferrerSession;
private String mReferrerUri;
private GeckoBundle mHeaders;
private @LoadFlags int mLoadFlags = LOAD_FLAGS_NONE;
private boolean mIsDataUri;
private @HeaderFilter int mHeaderFilter = HEADER_FILTER_CORS_SAFELISTED;
private static @NonNull String createDataUri(@NonNull final byte[] bytes,
@Nullable final String mimeType) {
return String.format("data:%s;base64,%s", mimeType != null ? mimeType : "",
Base64.encodeToString(bytes, Base64.NO_WRAP));
}
private static @NonNull String createDataUri(@NonNull final String data,
@Nullable final String mimeType) {
return String.format("data:%s,%s", mimeType != null ? mimeType : "", data);
}
/**
* Set the URI of the resource to load.
* @param uri a String containg the URI
* @return this {@link Loader} instance.
*/
@NonNull
public Loader uri(final @NonNull String uri) {
mUri = uri;
mIsDataUri = false;
return this;
}
/**
* Set the URI of the resource to load.
* @param uri a {@link Uri} instance
* @return this {@link Loader} instance.
*/
@NonNull
public Loader uri(final @NonNull Uri uri) {
mUri = uri.toString();
mIsDataUri = false;
return this;
}
/**
* Set the data URI of the resource to load.
* @param bytes a <code>byte</code> array containing the data to load.
* @param mimeType a <code>String</code> containing the mime type for this
* data URI, e.g. "text/plain"
* @return this {@link Loader} instance.
*/
@NonNull
public Loader data(final @NonNull byte[] bytes, final @Nullable String mimeType) {
mUri = createDataUri(bytes, mimeType);
mIsDataUri = true;
return this;
}
/**
* Set the data URI of the resource to load.
* @param data a <code>String</code> array containing the data to load.
* @param mimeType a <code>String</code> containing the mime type for this
* data URI, e.g. "text/plain"
* @return this {@link Loader} instance.
*/
@NonNull
public Loader data(final @NonNull String data, final @Nullable String mimeType) {
mUri = createDataUri(data, mimeType);
mIsDataUri = true;
return this;
}
/**
* Set the referrer for this load.
* @param referrer a <code>GeckoSession</code> that will be used as the referrer
* @return this {@link Loader} instance.
*/
@NonNull
public Loader referrer(final @NonNull GeckoSession referrer) {
mReferrerSession = referrer;
return this;
}
/**
* Set the referrer for this load.
* @param referrerUri a {@link Uri} that will be used as the referrer
* @return this {@link Loader} instance.
*/
@NonNull
public Loader referrer(final @NonNull Uri referrerUri) {
mReferrerUri = referrerUri != null ? referrerUri.toString() : null;
return this;
}
/**
* Set the referrer for this load.
* @param referrerUri a <code>String</code> containing the URI
* that will be used as the referrer
* @return this {@link Loader} instance.
*/
@NonNull
public Loader referrer(final @NonNull String referrerUri) {
mReferrerUri = referrerUri;
return this;
}
/**
* Add headers for this load.
*
* Note: only CORS safelisted headers are allowed by default. To modify this
* behavior use {@link #headerFilter}.
*
* See <a href="https://developer.mozilla.org/en-US/docs/Glossary/CORS-safelisted_request_header">
* CORS-safelisted request header
* </a>.
*
* @param headers a <code>Map</code> containing headers that will
* be added to this load.
* @return this {@link Loader} instance.
*/
@NonNull
public Loader additionalHeaders(final @NonNull Map<String, String> headers) {
final GeckoBundle bundle = new GeckoBundle(headers.size());
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey() == null) {
// Ignore null keys
continue;
}
bundle.putString(entry.getKey(), entry.getValue());
private GeckoBundle additionalHeadersToBundle(final Map<String, String> additionalHeaders) {
final GeckoBundle bundle = new GeckoBundle(additionalHeaders.size());
for (Map.Entry<String, String> entry : additionalHeaders.entrySet()) {
if (entry.getKey() == null) {
// Ignore null keys
continue;
}
mHeaders = bundle;
return this;
bundle.putString(entry.getKey(), entry.getValue());
}
/**
* Modify the header filter behavior. By default only CORS safelisted headers are allowed.
*
* @param filter one of the
* {@link GeckoSession#HEADER_FILTER_CORS_SAFELISTED HEADER_FILTER_*}
* constants.
* @return this {@link Loader} instance.
*/
@NonNull
public Loader headerFilter(final @HeaderFilter int filter) {
mHeaderFilter = filter;
return this;
}
/**
* Set the load flags for this load.
* @param flags the load flags to use, an OR-ed value of
* {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*} that will be used as the referrer
* @return this {@link Loader} instance.
*/
@NonNull
public Loader flags(final @LoadFlags int flags) {
mLoadFlags = flags;
return this;
}
}
/**
* Load page using the {@link Loader} specified.
*
* @param request Loader for this request.
* @see Loader
*/
@AnyThread
public void load(final @NonNull Loader request) {
if (request.mUri == null) {
throw new IllegalArgumentException(
"You need to specify at least one between `uri` and `data`.");
}
if (request.mReferrerUri != null && request.mReferrerSession != null) {
throw new IllegalArgumentException(
"Cannot specify both a referrer session and a referrer URI.");
}
final int loadFlags = request.mIsDataUri
// If this is a data: load then we need to force allow it.
? request.mLoadFlags | LOAD_FLAGS_FORCE_ALLOW_DATA_URI
: request.mLoadFlags;
// For performance reasons we short-circuit the delegate here
// instead of making Gecko call it for direct loadUri calls.
final NavigationDelegate.LoadRequest loadRequest =
new NavigationDelegate.LoadRequest(
request.mUri,
null, /* triggerUri */
1, /* geckoTarget: OPEN_CURRENTWINDOW */
0, /* flags */
false, /* hasUserGesture */
true /* isDirectNavigation */);
shouldLoadUri(loadRequest).getOrAccept(allowOrDeny -> {
if (allowOrDeny == AllowOrDeny.DENY) {
return;
}
final GeckoBundle msg = new GeckoBundle();
msg.putString("uri", request.mUri);
msg.putInt("flags", loadFlags);
msg.putInt("headerFilter", request.mHeaderFilter);
if (request.mReferrerUri != null) {
msg.putString("referrerUri", request.mReferrerUri);
}
if (request.mReferrerSession != null) {
msg.putString("referrerSessionId", request.mReferrerSession.mId);
}
if (request.mHeaders != null) {
msg.putBundle("headers", request.mHeaders);
}
mEventDispatcher.dispatch("GeckoView:LoadUri", msg);
});
return bundle;
}
/**
* Load the given URI.
*
* Convenience method for <pre><code>
* session.load(new Loader().uri(uri));
* </code></pre>
*
* @param uri The URI of the resource to load.
*/
@AnyThread
public void loadUri(final @NonNull String uri) {
load(new Loader().uri(uri));
loadUri(uri, (GeckoSession)null, LOAD_FLAGS_NONE, (Map<String, String>) null);
}
/**
* Load the given URI with specified HTTP request headers.
* @param uri The URI of the resource to load.
* @param additionalHeaders any additional request headers used with the load
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull String uri, final @Nullable Map<String, String> additionalHeaders) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.additionalHeaders(additionalHeaders));
loadUri(uri, (GeckoSession)null, LOAD_FLAGS_NONE, additionalHeaders);
}
/**
@ -1776,15 +1496,10 @@ public class GeckoSession {
*
* @param uri the URI to load
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull String uri, final @LoadFlags int flags) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.flags(flags));
loadUri(uri, (GeckoSession)null, flags, (Map<String, String>) null);
}
/**
@ -1793,17 +1508,11 @@ public class GeckoSession {
* @param uri the URI to load
* @param referrer the referrer, may be null
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull String uri, final @Nullable String referrer,
final @LoadFlags int flags) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.referrer(referrer)
.flags(flags));
loadUri(uri, referrer, flags, (Map<String, String>) null);
}
/**
@ -1813,18 +1522,22 @@ public class GeckoSession {
* @param referrer the referrer, may be null
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @param additionalHeaders any additional request headers used with the load
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull String uri, final @Nullable String referrer,
final @LoadFlags int flags, final @Nullable Map<String, String> additionalHeaders) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.referrer(referrer)
.flags(flags)
.additionalHeaders(additionalHeaders));
final GeckoBundle msg = new GeckoBundle();
msg.putString("uri", uri);
msg.putInt("flags", flags);
if (referrer != null) {
msg.putString("referrerUri", referrer);
}
if (additionalHeaders != null) {
msg.putBundle("headers", additionalHeadersToBundle(additionalHeaders));
}
mEventDispatcher.dispatch("GeckoView:LoadUri", msg);
}
/**
@ -1835,17 +1548,11 @@ public class GeckoSession {
* @param uri the URI to load
* @param referrer the referring GeckoSession, may be null
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull String uri, final @Nullable GeckoSession referrer,
final @LoadFlags int flags) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.referrer(referrer)
.flags(flags));
loadUri(uri, referrer, flags, (Map<String, String>) null);
}
/**
@ -1857,18 +1564,40 @@ public class GeckoSession {
* @param referrer the referring GeckoSession, may be null
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @param additionalHeaders any additional request headers used with the load
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull String uri, final @Nullable GeckoSession referrer,
final @LoadFlags int flags, final @Nullable Map<String, String> additionalHeaders) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.referrer(referrer)
.additionalHeaders(additionalHeaders)
.flags(flags));
// For performance reasons we short-circuit the delegate here
// instead of making Gecko call it for direct loadUri calls.
final NavigationDelegate.LoadRequest request =
new NavigationDelegate.LoadRequest(
uri,
null, /* triggerUri */
1, /* geckoTarget: OPEN_CURRENTWINDOW */
0, /* flags */
false, /* hasUserGesture */
true /* isDirectNavigation */);
shouldLoadUri(request).getOrAccept(allowOrDeny -> {
if (allowOrDeny == AllowOrDeny.DENY) {
return;
}
final GeckoBundle msg = new GeckoBundle();
msg.putString("uri", uri);
msg.putInt("flags", flags);
if (referrer != null) {
msg.putString("referrerSessionId", referrer.mId);
}
if (additionalHeaders != null) {
msg.putBundle("headers", additionalHeadersToBundle(additionalHeaders));
}
mEventDispatcher.dispatch("GeckoView:LoadUri", msg);
});
}
private GeckoResult<AllowOrDeny> shouldLoadUri(final NavigationDelegate.LoadRequest request) {
@ -1899,44 +1628,30 @@ public class GeckoSession {
/**
* Load the given URI.
* @param uri The URI of the resource to load.
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull Uri uri) {
load(new Loader()
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.uri(uri));
loadUri(uri.toString(), (GeckoSession)null, LOAD_FLAGS_NONE, (Map<String, String>) null);
}
/**
* Load the given URI with specified HTTP request headers.
* @param uri The URI of the resource to load.
* @param additionalHeaders any additional request headers used with the load
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull Uri uri, final @Nullable Map<String, String> additionalHeaders) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.additionalHeaders(additionalHeaders));
loadUri(uri.toString(), (GeckoSession)null, LOAD_FLAGS_NONE, additionalHeaders);
}
/**
* Load the given URI with the specified referrer and load type.
* @param uri the URI to load
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull Uri uri, final @LoadFlags int flags) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.flags(flags));
loadUri(uri.toString(), (GeckoSession)null, flags, (Map<String, String>) null);
}
/**
@ -1944,17 +1659,11 @@ public class GeckoSession {
* @param uri the URI to load
* @param referrer the Uri to use as the referrer
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull Uri uri, final @Nullable Uri referrer,
final @LoadFlags int flags) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.referrer(referrer)
.flags(flags));
loadUri(uri.toString(), referrer != null ? referrer.toString() : null, flags, (Map<String, String>) null);
}
/**
@ -1963,18 +1672,11 @@ public class GeckoSession {
* @param referrer the Uri to use as the referrer
* @param flags the load flags to use, an OR-ed value of {@link #LOAD_FLAGS_NONE LOAD_FLAGS_*}
* @param additionalHeaders any additional request headers used with the load
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadUri(final @NonNull Uri uri, final @Nullable Uri referrer,
final @LoadFlags int flags, final @Nullable Map<String, String> additionalHeaders) {
load(new Loader()
.uri(uri)
.headerFilter(HEADER_FILTER_UNRESTRICTED_UNSAFE)
.referrer(referrer)
.flags(flags)
.additionalHeaders(additionalHeaders));
loadUri(uri.toString(), referrer != null ? referrer.toString() : null, flags, additionalHeaders);
}
/**
@ -1983,12 +1685,15 @@ public class GeckoSession {
* @param data a String representing the data
* @param mimeType the mime type of the data, e.g. "text/plain". Maybe be null, in
* which case the type is guessed.
* @deprecated Use {@link #load} instead.
*
*/
@AnyThread
@Deprecated
public void loadString(@NonNull final String data, @Nullable final String mimeType) {
load(new Loader().data(data, mimeType));
if (data == null) {
throw new IllegalArgumentException("data cannot be null");
}
loadUri(createDataUri(data, mimeType), (GeckoSession)null, LOAD_FLAGS_NONE);
}
/**
@ -1997,12 +1702,14 @@ public class GeckoSession {
* @param bytes the data to load
* @param mimeType the mime type of the data, e.g. video/mp4. May be null, in which
* case the type is guessed.
* @deprecated Use {@link #load} instead.
*/
@AnyThread
@Deprecated
public void loadData(@NonNull final byte[] bytes, @Nullable final String mimeType) {
load(new Loader().data(bytes, mimeType));
if (bytes == null) {
throw new IllegalArgumentException("data cannot be null");
}
loadUri(createDataUri(bytes, mimeType), (GeckoSession)null, LOAD_FLAGS_FORCE_ALLOW_DATA_URI);
}
/**
@ -2012,10 +1719,10 @@ public class GeckoSession {
* @return a URI String
*/
@AnyThread
@Deprecated
public static @NonNull String createDataUri(@NonNull final byte[] bytes,
@Nullable final String mimeType) {
return Loader.createDataUri(bytes, mimeType);
return String.format("data:%s;base64,%s", mimeType != null ? mimeType : "",
Base64.encodeToString(bytes, Base64.NO_WRAP));
}
/**
@ -2025,10 +1732,9 @@ public class GeckoSession {
* @return a URI String
*/
@AnyThread
@Deprecated
public static @NonNull String createDataUri(@NonNull final String data,
@Nullable final String mimeType) {
return Loader.createDataUri(data, mimeType);
return String.format("data:%s,%s", mimeType != null ? mimeType : "", data);
}
/**

Просмотреть файл

@ -478,10 +478,10 @@ public class WebExtension {
public final Boolean pinned;
/**
* The url that the tab will be navigated to. This url is provided just
* for informational purposes, there is no need to load the URL manually.
* The corresponding {@link GeckoSession} will be navigated to the
* right URL after returning <code>GeckoResult.ALLOW</code> from {@link
* SessionTabDelegate#onUpdateTab}
* for informational purposes, there is no need to call
* <code>loadUri</code> on it. The corresponding {@link GeckoSession} will be
* navigated to the right URL after returning
* <code>GeckoResult.ALLOW</code> from {@link SessionTabDelegate#onUpdateTab}
*/
@Nullable
public final String url;
@ -553,10 +553,10 @@ public class WebExtension {
public final Boolean pinned;
/**
* The url that the tab will be navigated to. This url is provided just
* for informational purposes, there is no need to load the URL
* manually. The corresponding {@link GeckoSession} will be navigated
* to the right URL after returning <code>GeckoResult.ALLOW</code> from
* {@link TabDelegate#onNewTab}
* for informational purposes, there is no need to call
* <code>loadUri</code> on it. The corresponding {@link GeckoSession} will be
* navigated to the right URL after returning
* <code>GeckoResult.ALLOW</code> from {@link TabDelegate#onNewTab}
*/
@Nullable
public final String url;

Просмотреть файл

@ -31,22 +31,12 @@ exclude: true
FIDO support for WebAuthn.
- Added ['GeckoWebExecutor#FETCH_FLAG_PRIVATE'][83.5]. This new flag allows for private browsing downloads using WebExecutor.
([bug 1665426]({{bugzilla}}1665426))
- ⚠️ Deprecated [`GeckoSession#loadUri`][83.6] variants in favor of
[`GeckoSession#load`][83.7]. See docs for [`Loader`][83.8].
([bug 1667471]({{bugzilla}}1667471))
- Added [`Loader#headerFilter`][83.9] to override the default header filtering
behavior.
([bug 1667471]({{bugzilla}}1667471))
[83.1]: {{javadoc_uri}}/WebExtension.MetaData.html#temporary
[83.2]: {{javadoc_uri}}/MediaSession.Delegate.html#onMetadata-org.mozilla.geckoview.GeckoSession-org.mozilla.geckoview.MediaSession-org.mozilla.geckoview.MediaSession.Metadata-
[83.3]: {{javadoc_uri}}/ContentBlocking.SafeBrowsingProvider.html
[83.4]: {{javadoc_uri}}/GeckoRuntime.ActivityDelegate.html
[83.5]: {{javadoc_uri}}/GeckoWebExecutor.html#FETCH_FLAG_PRIVATE
[83.6]: {{javadoc_uri}}/GeckoSession.html#loadUri-java.lang.String-org.mozilla.geckoview.GeckoSession-int-java.util.Map-
[83.7]: {{javadoc_uri}}/GeckoSession.html#load-org.mozilla.geckoview.GeckoSession.Loader-
[83.8]: {{javadoc_uri}}/GeckoSession.Loader.html
[83.9]: {{javadoc_uri}}/GeckoSession.Loader.html#headerFilter-int-
## v82
- ⚠️ [`WebNotification.source`][79.2] is now `@Nullable` to account for
@ -836,4 +826,4 @@ to allow adding gecko profiler markers.
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: cd2ba68c049db26ce20ba332ef2ba46cbde4910a
[api-version]: a2b63f41870a698bfe884cc415427dfb8c6fb471

Просмотреть файл

@ -37,9 +37,24 @@ const BAD_HEADERS = ["connection", "host"];
// in the header name or value
const FORBIDDEN_HEADER_CHARACTERS = ["\n", "\r"];
// Keep in sync with GeckoSession.java
const HEADER_FILTER_CORS_SAFELISTED = 1;
const HEADER_FILTER_UNRESTRICTED_UNSAFE = 2;
function validateHeader(key, value) {
if (!key) {
// Key cannot be empty
return false;
}
for (const c of FORBIDDEN_HEADER_CHARACTERS) {
if (key.includes(c) || value?.includes(c)) {
return false;
}
}
if (BAD_HEADERS.includes(key.toLowerCase().trim())) {
return false;
}
return true;
}
// Create default ReferrerInfo instance for the given referrer URI string.
const createReferrerInfo = aReferrer => {
@ -128,32 +143,6 @@ class GeckoViewNavigation extends GeckoViewModule {
this._initialAboutBlank = true;
}
validateHeader(key, value, filter) {
if (!key) {
// Key cannot be empty
return false;
}
for (const c of FORBIDDEN_HEADER_CHARACTERS) {
if (key.includes(c) || value?.includes(c)) {
return false;
}
}
if (BAD_HEADERS.includes(key.toLowerCase().trim())) {
return false;
}
if (
filter == HEADER_FILTER_CORS_SAFELISTED &&
!this.window.windowUtils.isCORSSafelistedRequestHeader(key, value)
) {
return false;
}
return true;
}
// Bundle event handler.
async onEvent(aEvent, aData, aCallback) {
debug`onEvent: event=${aEvent}, data=${aData}`;
@ -169,14 +158,7 @@ class GeckoViewNavigation extends GeckoViewModule {
this.browser.gotoIndex(aData.index);
break;
case "GeckoView:LoadUri":
const {
uri,
referrerUri,
referrerSessionId,
flags,
headers,
headerFilter,
} = aData;
const { uri, referrerUri, referrerSessionId, flags, headers } = aData;
let navFlags = convertFlags(flags);
// For performance reasons we don't call the LoadUriDelegate.loadUri
@ -233,7 +215,7 @@ class GeckoViewNavigation extends GeckoViewModule {
if (headers) {
additionalHeaders = "";
for (const [key, value] of Object.entries(headers)) {
if (!this.validateHeader(key, value, headerFilter)) {
if (!validateHeader(key, value)) {
Cu.reportError(`Ignoring invalid header '${key}'='${value}'.`);
continue;
}