Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
2026-03-25 07:19:43 +08:00
23 changed files with 1110 additions and 694 deletions
+1
View File
@@ -16,6 +16,7 @@
#include "mozilla/CORSMode.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
+85 -23
View File
@@ -57,7 +57,84 @@ CreateObjectURLInternal(const GlobalObject& aGlobal, T aObject,
CopyASCIItoUTF16(url, aResult);
}
// The URL implementation for the main-thread
bool
IsRootRelativePathInput(const nsAString& aURL)
{
return !aURL.IsEmpty() && aURL.CharAt(0) == '/' &&
(aURL.Length() == 1 || aURL.CharAt(1) != '/');
}
bool
BuildRootRelativeFallbackSpec(const nsAString& aURL, nsIURI* aBase,
nsACString& aFallbackSpec)
{
MOZ_ASSERT(aBase);
MOZ_ASSERT(IsRootRelativePathInput(aURL));
nsAutoCString baseSpec;
if (NS_FAILED(aBase->GetSpec(baseSpec)) || baseSpec.IsEmpty()) {
return false;
}
int32_t colon = baseSpec.FindChar(':');
if (colon <= 0) {
return false;
}
uint32_t afterColon = static_cast<uint32_t>(colon + 1);
if (afterColon >= baseSpec.Length() || baseSpec.CharAt(afterColon) != '/') {
// Opaque paths (for example mailto:test@example.com) are not valid bases
// for root-relative URL input.
return false;
}
nsAutoCString input;
if (!AppendUTF16toUTF8(aURL, input, fallible)) {
return false;
}
// Preserve authority when present (scheme://authority/path -> scheme://authority/input)
if (afterColon + 1 < baseSpec.Length() && baseSpec.CharAt(afterColon + 1) == '/') {
uint32_t authorityEnd = afterColon + 2;
while (authorityEnd < baseSpec.Length()) {
char c = baseSpec.CharAt(authorityEnd);
if (c == '/' || c == '?' || c == '#') {
break;
}
++authorityEnd;
}
aFallbackSpec.Assign(Substring(baseSpec, 0, authorityEnd));
aFallbackSpec.Append(input);
return true;
}
// Single-slash hierarchical base (scheme:/path -> scheme:/input)
aFallbackSpec.Assign(Substring(baseSpec, 0, afterColon));
aFallbackSpec.Append(input);
return true;
}
bool
TryResolveRootRelativeAgainstBase(const nsAString& aURL, nsIURI* aBase,
nsIURI** aOutURI)
{
MOZ_ASSERT(aOutURI);
if (!aBase || !IsRootRelativePathInput(aURL)) {
return false;
}
nsAutoCString fallbackSpec;
if (!BuildRootRelativeFallbackSpec(aURL, aBase, fallbackSpec)) {
return false;
}
nsresult rv = NS_NewURI(aOutURI, fallbackSpec, nullptr, nullptr,
nsContentUtils::GetIOService());
return NS_SUCCEEDED(rv);
}
class URLMainThread final : public URL
{
public:
@@ -229,7 +306,8 @@ URLMainThread::Constructor(nsISupports* aParent, const nsAString& aURL,
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, aBase,
nsContentUtils::GetIOService());
if (NS_FAILED(rv)) {
if (NS_FAILED(rv) &&
!TryResolveRootRelativeAgainstBase(aURL, aBase, getter_AddRefs(uri))) {
// No need to warn in this case. It's common to use the URL constructor
// to determine if a URL is valid and an exception will be propagated.
aRv.ThrowTypeError<MSG_INVALID_URL>(aURL);
@@ -1714,30 +1792,14 @@ URL::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
bool
URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
const Optional<nsAString>& aBase) {
nsCOMPtr<nsIURI> baseUri;
if (aBase.WasPassed()) {
// Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
nsAutoCString base;
if (!AppendUTF16toUTF8(aBase.Value(), base, fallible)) {
// Just return false with OOM errors as no ErrorResult.
return false;
}
nsresult rv = NS_NewURI(getter_AddRefs(baseUri), base);
if (NS_FAILED(rv)) {
// Invalid base URL, return false.
return false;
}
}
nsAutoCString urlStr;
if (!AppendUTF16toUTF8(aURL, urlStr, fallible)) {
// Just return false with OOM errors as no ErrorResult.
ErrorResult rv;
RefPtr<URL> parsed = URL::Constructor(aGlobal, aURL, aBase, rv);
if (rv.Failed() || !parsed) {
rv.SuppressException();
return false;
}
nsCOMPtr<nsIURI> uri;
return NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), urlStr, nullptr, baseUri));
return true;
}
URLSearchParams*
+414 -412
View File
@@ -1,460 +1,462 @@
<!DOCTYPE HTML>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test URL API</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887364">Mozilla Bug 887364</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=991471">Mozilla Bug 991471</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=996055">Mozilla Bug 996055</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe name="x" id="x"></iframe>
<iframe name="y" id="y"></iframe>
</div>
<pre id="test">
</pre>
<script type="application/javascript">
<head>
<meta charset="utf-8" />
<title>Test URL API</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887364">Mozilla Bug 887364</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=991471">Mozilla Bug 991471</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=996055">Mozilla Bug 996055</a>
<p id="display"></p>
<div id="content" style="display: none">
<iframe name="x" id="x"></iframe>
<iframe name="y" id="y"></iframe>
</div>
<pre id="test"></pre>
<script type="application/javascript">
/** Test for Bug 887364 **/
ok("URL" in window, "window.URL exists");
/** Test for Bug 887364 **/
ok("URL" in window, "window.URL exists");
var tests = [
{
url: "http://www.abc.com",
base: undefined,
error: false,
href: "http://www.abc.com/",
origin: "http://www.abc.com",
protocol: "http:",
username: "",
password: "",
host: "www.abc.com",
hostname: "www.abc.com",
port: "",
pathname: "/",
search: "",
hash: "",
},
{
url: "ftp://auser:apw@www.abc.com",
base: undefined,
error: false,
href: "ftp://auser:apw@www.abc.com/",
origin: "ftp://www.abc.com",
protocol: "ftp:",
username: "auser",
password: "apw",
host: "www.abc.com",
hostname: "www.abc.com",
port: "",
pathname: "/",
search: "",
hash: "",
},
{
url: "http://www.abc.com:90/apath/",
base: undefined,
error: false,
href: "http://www.abc.com:90/apath/",
origin: "http://www.abc.com:90",
protocol: "http:",
username: "",
password: "",
host: "www.abc.com:90",
hostname: "www.abc.com",
port: "90",
pathname: "/apath/",
search: "",
hash: "",
},
{
url: "http://www.abc.com/apath/afile.txt#ahash",
base: undefined,
error: false,
href: "http://www.abc.com/apath/afile.txt#ahash",
origin: "http://www.abc.com",
protocol: "http:",
username: "",
password: "",
host: "www.abc.com",
hostname: "www.abc.com",
port: "",
pathname: "/apath/afile.txt",
search: "",
hash: "#ahash",
},
{
url: "http://example.com/?test#hash",
base: undefined,
error: false,
href: "http://example.com/?test#hash",
origin: "http://example.com",
protocol: "http:",
username: "",
password: "",
host: "example.com",
hostname: "example.com",
port: "",
pathname: "/",
search: "?test",
hash: "#hash",
},
{
url: "http://example.com/?test",
base: undefined,
error: false,
href: "http://example.com/?test",
origin: "http://example.com",
protocol: "http:",
username: "",
password: "",
host: "example.com",
hostname: "example.com",
port: "",
pathname: "/",
search: "?test",
hash: "",
},
{ url: "http://example.com/carrot#question%3f", base: undefined, error: false, hash: "#question%3f" },
{
url: "https://example.com:4443?",
base: undefined,
error: false,
protocol: "https:",
port: "4443",
pathname: "/",
hash: "",
search: "",
},
{
url: "http://www.abc.com/apath/afile.txt#ahash?asearch",
base: undefined,
error: false,
href: "http://www.abc.com/apath/afile.txt#ahash?asearch",
protocol: "http:",
pathname: "/apath/afile.txt",
hash: "#ahash?asearch",
search: "",
},
{
url: "http://www.abc.com/apath/afile.txt?asearch#ahash",
base: undefined,
error: false,
href: "http://www.abc.com/apath/afile.txt?asearch#ahash",
protocol: "http:",
pathname: "/apath/afile.txt",
hash: "#ahash",
search: "?asearch",
},
{
url: "http://abc.com/apath/afile.txt?#ahash",
base: undefined,
error: false,
pathname: "/apath/afile.txt",
hash: "#ahash",
search: "",
},
{
url: "http://auser:apassword@www.abc.com:90/apath/afile.txt?asearch#ahash",
base: undefined,
error: false,
protocol: "http:",
username: "auser",
password: "apassword",
host: "www.abc.com:90",
hostname: "www.abc.com",
port: "90",
pathname: "/apath/afile.txt",
hash: "#ahash",
search: "?asearch",
origin: "http://www.abc.com:90",
},
var tests = [
{ url: 'http://www.abc.com',
base: undefined,
error: false,
href: 'http://www.abc.com/',
origin: 'http://www.abc.com',
protocol: 'http:',
username: '',
password: '',
host: 'www.abc.com',
hostname: 'www.abc.com',
port: '',
pathname: '/',
search: '',
hash: ''
},
{ url: 'ftp://auser:apw@www.abc.com',
base: undefined,
error: false,
href: 'ftp://auser:apw@www.abc.com/',
origin: 'ftp://www.abc.com',
protocol: 'ftp:',
username: 'auser',
password: 'apw',
host: 'www.abc.com',
hostname: 'www.abc.com',
port: '',
pathname: '/',
search: '',
hash: ''
},
{ url: 'http://www.abc.com:90/apath/',
base: undefined,
error: false,
href: 'http://www.abc.com:90/apath/',
origin: 'http://www.abc.com:90',
protocol: 'http:',
username: '',
password: '',
host: 'www.abc.com:90',
hostname: 'www.abc.com',
port: '90',
pathname: '/apath/',
search: '',
hash: ''
},
{ url: 'http://www.abc.com/apath/afile.txt#ahash',
base: undefined,
error: false,
href: 'http://www.abc.com/apath/afile.txt#ahash',
origin: 'http://www.abc.com',
protocol: 'http:',
username: '',
password: '',
host: 'www.abc.com',
hostname: 'www.abc.com',
port: '',
pathname: '/apath/afile.txt',
search: '',
hash: '#ahash'
},
{ url: 'http://example.com/?test#hash',
base: undefined,
error: false,
href: 'http://example.com/?test#hash',
origin: 'http://example.com',
protocol: 'http:',
username: '',
password: '',
host: 'example.com',
hostname: 'example.com',
port: '',
pathname: '/',
search: '?test',
hash: '#hash'
},
{ url: 'http://example.com/?test',
base: undefined,
error: false,
href: 'http://example.com/?test',
origin: 'http://example.com',
protocol: 'http:',
username: '',
password: '',
host: 'example.com',
hostname: 'example.com',
port: '',
pathname: '/',
search: '?test',
hash: ''
},
{ url: 'http://example.com/carrot#question%3f',
base: undefined,
error: false,
hash: '#question%3f'
},
{ url: 'https://example.com:4443?',
base: undefined,
error: false,
protocol: 'https:',
port: '4443',
pathname: '/',
hash: '',
search: ''
},
{ url: 'http://www.abc.com/apath/afile.txt#ahash?asearch',
base: undefined,
error: false,
href: 'http://www.abc.com/apath/afile.txt#ahash?asearch',
protocol: 'http:',
pathname: '/apath/afile.txt',
hash: '#ahash?asearch',
search: ''
},
{ url: 'http://www.abc.com/apath/afile.txt?asearch#ahash',
base: undefined,
error: false,
href: 'http://www.abc.com/apath/afile.txt?asearch#ahash',
protocol: 'http:',
pathname: '/apath/afile.txt',
hash: '#ahash',
search: '?asearch'
},
{ url: 'http://abc.com/apath/afile.txt?#ahash',
base: undefined,
error: false,
pathname: '/apath/afile.txt',
hash: '#ahash',
search: ''
},
{ url: 'http://auser:apassword@www.abc.com:90/apath/afile.txt?asearch#ahash',
base: undefined,
error: false,
protocol: 'http:',
username: 'auser',
password: 'apassword',
host: 'www.abc.com:90',
hostname: 'www.abc.com',
port: '90',
pathname: '/apath/afile.txt',
hash: '#ahash',
search: '?asearch',
origin: 'http://www.abc.com:90'
},
{ url: "/foo#bar", base: "www.test.org", error: true },
{ url: "/foo#bar", base: null, error: true },
{ url: "/foo#bar", base: 42, error: true },
{ url: "ftp://ftp.something.net", base: undefined, error: false, protocol: "ftp:" },
{ url: "file:///tmp/file", base: undefined, error: false, protocol: "file:" },
{ url: "gopher://gopher.something.net", base: undefined, error: false, protocol: "gopher:" },
{ url: "ws://ws.something.net", base: undefined, error: false, protocol: "ws:" },
{ url: "wss://ws.something.net", base: undefined, error: false, protocol: "wss:" },
{ url: "foo://foo.something.net", base: undefined, error: false, protocol: "foo:" },
{ url: '/foo#bar',
base: 'www.test.org',
error: true,
},
{ url: '/foo#bar',
base: null,
error: true,
},
{ url: '/foo#bar',
base: 42,
error: true,
},
{ url: 'ftp://ftp.something.net',
base: undefined,
error: false,
protocol: 'ftp:',
},
{ url: 'file:///tmp/file',
base: undefined,
error: false,
protocol: 'file:',
},
{ url: 'gopher://gopher.something.net',
base: undefined,
error: false,
protocol: 'gopher:',
},
{ url: 'ws://ws.something.net',
base: undefined,
error: false,
protocol: 'ws:',
},
{ url: 'wss://ws.something.net',
base: undefined,
error: false,
protocol: 'wss:',
},
{ url: 'foo://foo.something.net',
base: undefined,
error: false,
protocol: 'foo:',
},
{
url: "about:blank",
base: undefined,
error: false,
protocol: "about:",
pathname: "blank",
skip_setters: false,
},
{ url: 'about:blank',
base: undefined,
error: false,
protocol: 'about:',
pathname: 'blank',
skip_setters: false,
},
{
url: "foo:bar?what#yeah",
base: undefined,
error: false,
protocol: "foo:",
pathname: "bar",
search: "?what",
hash: "#yeah",
skip_setters: false,
},
];
{ url: 'foo:bar?what#yeah',
base: undefined,
error: false,
protocol: 'foo:',
pathname: 'bar',
search: '?what',
hash: '#yeah',
skip_setters: false,
},
];
while (tests.length) {
var test = tests.shift();
while(tests.length) {
var test = tests.shift();
var error = false;
var url;
try {
if (test.base) {
url = new URL(test.url, test.base);
} else {
url = new URL(test.url);
}
} catch (e) {
error = true;
}
var error = false;
var url;
try {
if (test.base) {
url = new URL(test.url, test.base);
} else {
url = new URL(test.url);
}
} catch(e) {
error = true;
}
is(test.error, error, "Error creating URL");
if (test.error) {
continue;
}
is(test.error, error, "Error creating URL");
if (test.error) {
continue;
}
if ("href" in test) is(url.href, test.href, "href");
if ("origin" in test) is(url.origin, test.origin, "origin");
if ("protocol" in test) is(url.protocol, test.protocol, "protocol");
if ("username" in test) is(url.username, test.username, "username");
if ("password" in test) is(url.password, test.password, "password");
if ("host" in test) is(url.host, test.host, "host");
if ("hostname" in test) is(url.hostname, test.hostname, "hostname");
if ("port" in test) is(url.port, test.port, "port");
if ("pathname" in test) is(url.pathname, test.pathname, "pathname");
if ("search" in test) is(url.search, test.search, "search");
if ("hash" in test) is(url.hash, test.hash, "hash");
if ('href' in test) is(url.href, test.href, "href");
if ('origin' in test) is(url.origin, test.origin, "origin");
if ('protocol' in test) is(url.protocol, test.protocol, "protocol");
if ('username' in test) is(url.username, test.username, "username");
if ('password' in test) is(url.password, test.password, "password");
if ('host' in test) is(url.host, test.host, "host");
if ('hostname' in test) is(url.hostname, test.hostname, "hostname");
if ('port' in test) is(url.port, test.port, "port");
if ('pathname' in test) is(url.pathname, test.pathname, "pathname");
if ('search' in test) is(url.search, test.search, "search");
if ('hash' in test) is(url.hash, test.hash, "hash");
if ("skip_setters" in test && test.skip_setters == false) {
info("Skip setter methods for URL: " + test);
continue;
}
if ('skip_setters' in test && test.skip_setters == false) {
info("Skip setter methods for URL: " + test);
continue;
}
url = new URL("https://www.example.net/what#foo?bar");
ok(url, "Url exists!");
url = new URL('https://www.example.net/what#foo?bar');
ok(url, "Url exists!");
if ("href" in test) url.href = test.href;
if ("protocol" in test) url.protocol = test.protocol;
if ("username" in test && test.username) url.username = test.username;
if ("password" in test && test.password) url.password = test.password;
if ("host" in test) url.host = test.host;
if ("hostname" in test) url.hostname = test.hostname;
if ("port" in test) url.port = test.port;
if ("pathname" in test) url.pathname = test.pathname;
if ("search" in test) url.search = test.search;
if ("hash" in test) url.hash = test.hash;
if ('href' in test) url.href = test.href;
if ('protocol' in test) url.protocol = test.protocol;
if ('username' in test && test.username) url.username = test.username;
if ('password' in test && test.password) url.password = test.password;
if ('host' in test) url.host = test.host;
if ('hostname' in test) url.hostname = test.hostname;
if ('port' in test) url.port = test.port;
if ('pathname' in test) url.pathname = test.pathname;
if ('search' in test) url.search = test.search;
if ('hash' in test) url.hash = test.hash;
if ("href" in test) is(url.href, test.href, "href");
if ("origin" in test) is(url.origin, test.origin, "origin");
if ("protocol" in test) is(url.protocol, test.protocol, "protocol");
if ("username" in test) is(url.username, test.username, "username");
if ("password" in test) is(url.password, test.password, "password");
if ("host" in test) is(url.host, test.host, "host");
if ("hostname" in test) is(test.hostname, url.hostname, "hostname");
if ("port" in test) is(test.port, url.port, "port");
if ("pathname" in test) is(test.pathname, url.pathname, "pathname");
if ("search" in test) is(test.search, url.search, "search");
if ("hash" in test) is(test.hash, url.hash, "hash");
if ('href' in test) is(url.href, test.href, "href");
if ('origin' in test) is(url.origin, test.origin, "origin");
if ('protocol' in test) is(url.protocol, test.protocol, "protocol");
if ('username' in test) is(url.username, test.username, "username");
if ('password' in test) is(url.password, test.password, "password");
if ('host' in test) is(url.host, test.host, "host");
if ('hostname' in test) is(test.hostname, url.hostname, "hostname");
if ('port' in test) is(test.port, url.port, "port");
if ('pathname' in test) is(test.pathname, url.pathname, "pathname");
if ('search' in test) is(test.search, url.search, "search");
if ('hash' in test) is(test.hash, url.hash, "hash");
if ("href" in test) is(test.href, url + "", "stringify works");
}
</script>
if ('href' in test) is (test.href, url + '', 'stringify works');
}
<script>
/** Test for Bug 991471 **/
var url = new URL("http://localhost/");
url.hostname = "";
url.username = "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt";
url.hostname = "www.mozilla.org";
url.username = "";
url.hostname = "www.mozilla.org";
is(url.href, "http://www.mozilla.org/", "No parsing error with empty host");
</script>
</script>
<script>
/** Test for Bug 996055 **/
var url = new URL("http://localhost/");
url.hostname = "";
is(url.href, "http://localhost/", "Empty hostname is ignored");
</script>
<script>
/** Test for Bug 991471 **/
var url = new URL("http://localhost/");
url.hostname = "";
url.username = "tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt";
url.hostname = "www.mozilla.org";
url.username = "";
url.hostname = "www.mozilla.org";
is(url.href, "http://www.mozilla.org/", "No parsing error with empty host");
</script>
<script>
/** Test for Bug 960014 **/
var url = new URL("http://localhost/");
url.hostname = "[2001::1]";
is(url.hostname, "[2001::1]", "IPv6 hostname");
is(url.href, "http://[2001::1]/");
<script>
/** Test for Bug 996055 **/
var url = new URL("http://localhost/");
url.hostname = "";
is(url.href, "http://localhost/", "Empty hostname is ignored");
</script>
url.hostname = "[::192.9.5.5]";
is(url.hostname, "[::192.9.5.5]", "IPv6 hostname");
is(url.href, "http://[::192.9.5.5]/");
<script>
/** Test for Bug 960014 **/
var url = new URL("http://localhost/");
url.hostname = "[2001::1]";
is(url.hostname, "[2001::1]", "IPv6 hostname");
is(url.href, "http://[2001::1]/");
url = new URL("http://localhost/");
url.hostname = "[::]";
is(url.hostname, "[::]", "IPv6 hostname");
url.hostname = "[::192.9.5.5]";
is(url.hostname, "[::192.9.5.5]", "IPv6 hostname");
is(url.href, "http://[::192.9.5.5]/");
url = new URL("http://localhost/");
url.host = "[2001::1]:30";
is(url.hostname, "[2001::1]", "IPv6 hostname");
is(url.port, "30", "Port");
is(url.host, "[2001::1]:30", "IPv6 host");
url = new URL("http://localhost/");
url.hostname = "[::]";
is(url.hostname, "[::]", "IPv6 hostname");
url = new URL("http://localhost/");
// This should silently fail since it's missing the brackets
url.hostname = "2001::1";
is(url.hostname, "localhost", "Setting bad hostname fails");
</script>
url = new URL("http://localhost/");
url.host = "[2001::1]:30";
is(url.hostname, "[2001::1]", "IPv6 hostname");
is(url.port, "30", "Port");
is(url.host, "[2001::1]:30", "IPv6 host");
<script>
var blob = new Blob(["a"]);
var url = URL.createObjectURL(blob);
url = new URL("http://localhost/");
// This should silently fail since it's missing the brackets
url.hostname = "2001::1";
is(url.hostname, "localhost", "Setting bad hostname fails");
</script>
var u = new URL(url);
ok(u.origin, "http://mochi.test:8888", "The URL generated from a blob URI has an origin");
</script>
<script>
var blob = new Blob(['a']);
var url = URL.createObjectURL(blob);
<script>
var blob = new Blob(["a"]);
var url = URL.createObjectURL(blob);
var u = new URL(url);
ok(u.origin, 'http://mochi.test:8888', "The URL generated from a blob URI has an origin");
</script>
var a = document.createElement("A");
a.href = url;
ok(a.origin, "http://mochi.test:8888", "The 'a' element has the correct origin");
</script>
<script>
var blob = new Blob(['a']);
var url = URL.createObjectURL(blob);
<script>
var blob = new Blob(["a"]);
var url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
URL.revokeObjectURL(url);
ok(true, "Calling revokeObjectURL twice should be ok");
</script>
var a = document.createElement('A');
a.href = url;
ok(a.origin, 'http://mochi.test:8888', "The 'a' element has the correct origin");
</script>
<script>
URL.revokeObjectURL("blob:something");
ok(true, "This should not throw.");
</script>
<script>
var blob = new Blob(['a']);
var url = URL.createObjectURL(blob);
URL.revokeObjectURL(url);
URL.revokeObjectURL(url);
ok(true, "Calling revokeObjectURL twice should be ok");
</script>
<script>
var base = new URL("http:\\\\test.com\\path/to\\file?query\\backslash#hash\\");
is(base.href, "http://test.com/path/to/file?query\\backslash#hash\\");
<script>
URL.revokeObjectURL('blob:something');
ok(true, "This should not throw.");
</script>
var url = new URL("..\\", base);
is(url.href, "http://test.com/path/");
<script>
var base = new URL("http:\\\\test.com\\path/to\\file?query\\backslash#hash\\");
is(base.href, "http://test.com/path/to/file?query\\backslash#hash\\");
url = new URL("\\test", base);
is(url.href, "http://test.com/test");
var url = new URL("..\\", base);
is(url.href, "http://test.com/path/");
url = new URL("\\test\\", base);
is(url.href, "http://test.com/test/");
url = new URL("\\test", base);
is(url.href, "http://test.com/test");
url = new URL("http://example.org/test", base);
is(url.href, "http://example.org/test");
url = new URL("\\test\\", base);
is(url.href, "http://test.com/test/");
url = new URL("ftp://tmp/test", base);
is(url.href, "ftp://tmp/test");
url = new URL("http://example.org/test", base);
is(url.href, "http://example.org/test");
url = new URL("ftp:\\\\tmp\\test", base);
is(url.href, "ftp://tmp/test");
url = new URL("ftp://tmp/test", base);
is(url.href, "ftp://tmp/test");
url = new URL("scheme://tmp\\test", base);
is(url.href, "scheme://tmp\\test");
</script>
url = new URL("ftp:\\\\tmp\\test", base);
is(url.href, "ftp://tmp/test");
<script>
/** Test for Bug 1275746 **/
SimpleTest.doesThrow(() => {
var url = new URL("http:");
}, "http: is not a valid URL");
SimpleTest.doesThrow(() => {
var url = new URL("http:///");
}, "http: is not a valid URL");
url = new URL("scheme://tmp\\test", base);
is(url.href, "scheme://tmp\\test");
</script>
var url = new URL("file:");
is(url.href, "file:///", "Parsing file: should work.");
<script>
/** Test for Bug 1275746 **/
SimpleTest.doesThrow(() => { var url = new URL("http:"); }, "http: is not a valid URL");
SimpleTest.doesThrow(() => { var url = new URL("http:///"); }, "http: is not a valid URL");
url = new URL("file:///");
is(url.href, "file:///", "Parsing file:/// should work.");
</script>
var url = new URL("file:");
is(url.href, "file:///", "Parsing file: should work.");
<script>
var url = new URL("scheme:path/to/file?query#hash");
is(url.href, "scheme:path/to/file?query#hash");
is(url.pathname, "path/to/file");
is(url.search, "?query");
is(url.hash, "#hash");
url = new URL("file:///");
is(url.href, "file:///", "Parsing file:/// should work.");
</script>
// pathname cannot be overwritten.
url.pathname = "new/path?newquery#newhash";
is(url.href, "scheme:path/to/file?query#hash");
<script>
var url = new URL("scheme:path/to/file?query#hash");
is(url.href, "scheme:path/to/file?query#hash");
is(url.pathname, "path/to/file");
is(url.search, "?query");
is(url.hash, "#hash");
// don't escape '#' until we implement a spec-compliant parser.
url.search = "?newquery#newhash";
is(url.href, "scheme:path/to/file?newquery#newhash#hash");
// pathname cannot be overwritten.
url.pathname = "new/path?newquery#newhash";
is(url.href, "scheme:path/to/file?query#hash");
// nulls get encoded, whitespace gets stripped
url = new URL("scheme:pa\0\nth/to/fi\0\nle?qu\0\nery#ha\0\nsh");
is(url.href, "scheme:pa%00th/to/fi%00le?qu%00ery#ha%00sh");
// don't escape '#' until we implement a spec-compliant parser.
url.search = "?newquery#newhash";
is(url.href, "scheme:path/to/file?newquery#newhash#hash");
url.search = "new\0\nquery";
is(url.href, "scheme:pa%00th/to/fi%00le?new%00%0Aquery#ha%00sh");
url.hash = "new\0\nhash";
is(url.href, "scheme:pa%00th/to/fi%00le?new%00%0Aquery#new%00%0Ahash");
// nulls get encoded, whitespace gets stripped
url = new URL("scheme:pa\0\nth/to/fi\0\nle?qu\0\nery#ha\0\nsh");
is(url.href, "scheme:pa%00th/to/fi%00le?qu%00ery#ha%00sh");
url = new URL("scheme:path#hash");
is(url.href, "scheme:path#hash");
url.search = "query";
is(url.href, "scheme:path?query#hash");
url.hash = "";
is(url.href, "scheme:path?query");
url.hash = "newhash";
is(url.href, "scheme:path?query#newhash");
url.search = "";
is(url.href, "scheme:path#newhash");
url.search = "new\0\nquery";
is(url.href, "scheme:pa%00th/to/fi%00le?new%00%0Aquery#ha%00sh");
url.hash = "new\0\nhash";
is(url.href, "scheme:pa%00th/to/fi%00le?new%00%0Aquery#new%00%0Ahash");
// we don't implement a spec-compliant parser yet.
// make sure we are bug compatible with existing implementations.
url = new URL('data:text/html,<a href="http://example.org/?q">Link</a>');
is(url.href, 'data:text/html,<a%20href="http://example.org/?q">Link</a>');
</script>
url = new URL("scheme:path#hash");
is(url.href, "scheme:path#hash");
url.search = "query";
is(url.href, "scheme:path?query#hash");
url.hash = "";
is(url.href, "scheme:path?query");
url.hash = "newhash";
is(url.href, "scheme:path?query#newhash");
url.search = "";
is(url.href, "scheme:path#newhash");
<script>
is(
new URL("/static/client/runtime.js", "x:/").href,
"x:/static/client/runtime.js",
"URL constructor accepts root-relative input with x:/ base",
);
ok("canParse" in URL, "URL.canParse exists");
ok(
URL.canParse("/static/client/runtime.js", "x:/"),
"URL.canParse accepts root-relative input with x:/ base",
);
ok(
URL.canParse("/static/client/runtime.js", document.baseURI),
"URL.canParse accepts root-relative input with document base",
);
ok(!URL.canParse("/static/client/runtime.js"), "URL.canParse rejects root-relative input without base");
ok(
!URL.canParse("/static/client/runtime.js", "mailto:test@example.com"),
"URL.canParse rejects root-relative input against opaque mailto: base",
);
SimpleTest.doesThrow(() => {
new URL("/static/client/runtime.js", "mailto:test@example.com");
}, "constructor rejects root-relative input against opaque mailto: base");
</script>
// we don't implement a spec-compliant parser yet.
// make sure we are bug compatible with existing implementations.
url = new URL("data:text/html,<a href=\"http://example.org/?q\">Link</a>");
is(url.href, "data:text/html,<a%20href=\"http://example.org/?q\">Link</a>");
</script>
<script>
var u = new URL('http://www.example.org');
ok(u.toJSON(), 'http://www.example.org', "URL.toJSON()");
is(JSON.stringify(u), "\"http://www.example.org/\"", "JSON.stringify(u) works");
</script>
</body>
<script>
var u = new URL("http://www.example.org");
ok(u.toJSON(), "http://www.example.org", "URL.toJSON()");
is(JSON.stringify(u), '"http://www.example.org/"', "JSON.stringify(u) works");
</script>
</body>
</html>
+220 -176
View File
@@ -1,212 +1,224 @@
function ok(a, msg) {
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
postMessage({ type: "status", status: !!a, msg: a + ": " + msg });
}
function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
dump("IS: " + (a === b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({
type: "status",
status: a === b,
msg: a + " === " + b + ": " + msg,
});
}
onmessage = function() {
onmessage = function () {
status = false;
try {
if ((URL instanceof Object)) {
if (URL instanceof Object) {
status = true;
}
} catch(e) {
}
} catch (e) {}
var tests = [
{ url: 'http://www.abc.com',
{
url: "http://www.abc.com",
base: undefined,
error: false,
href: 'http://www.abc.com/',
origin: 'http://www.abc.com',
protocol: 'http:',
username: '',
password: '',
host: 'www.abc.com',
hostname: 'www.abc.com',
port: '',
pathname: '/',
search: '',
hash: ''
href: "http://www.abc.com/",
origin: "http://www.abc.com",
protocol: "http:",
username: "",
password: "",
host: "www.abc.com",
hostname: "www.abc.com",
port: "",
pathname: "/",
search: "",
hash: "",
},
{ url: 'ftp://auser:apw@www.abc.com',
{
url: "ftp://auser:apw@www.abc.com",
base: undefined,
error: false,
href: 'ftp://auser:apw@www.abc.com/',
origin: 'ftp://www.abc.com',
protocol: 'ftp:',
username: 'auser',
password: 'apw',
host: 'www.abc.com',
hostname: 'www.abc.com',
port: '',
pathname: '/',
search: '',
hash: ''
href: "ftp://auser:apw@www.abc.com/",
origin: "ftp://www.abc.com",
protocol: "ftp:",
username: "auser",
password: "apw",
host: "www.abc.com",
hostname: "www.abc.com",
port: "",
pathname: "/",
search: "",
hash: "",
},
{ url: 'http://www.abc.com:90/apath/',
{
url: "http://www.abc.com:90/apath/",
base: undefined,
error: false,
href: 'http://www.abc.com:90/apath/',
origin: 'http://www.abc.com:90',
protocol: 'http:',
username: '',
password: '',
host: 'www.abc.com:90',
hostname: 'www.abc.com',
port: '90',
pathname: '/apath/',
search: '',
hash: ''
href: "http://www.abc.com:90/apath/",
origin: "http://www.abc.com:90",
protocol: "http:",
username: "",
password: "",
host: "www.abc.com:90",
hostname: "www.abc.com",
port: "90",
pathname: "/apath/",
search: "",
hash: "",
},
{ url: 'http://www.abc.com/apath/afile.txt#ahash',
{
url: "http://www.abc.com/apath/afile.txt#ahash",
base: undefined,
error: false,
href: 'http://www.abc.com/apath/afile.txt#ahash',
origin: 'http://www.abc.com',
protocol: 'http:',
username: '',
password: '',
host: 'www.abc.com',
hostname: 'www.abc.com',
port: '',
pathname: '/apath/afile.txt',
search: '',
hash: '#ahash'
href: "http://www.abc.com/apath/afile.txt#ahash",
origin: "http://www.abc.com",
protocol: "http:",
username: "",
password: "",
host: "www.abc.com",
hostname: "www.abc.com",
port: "",
pathname: "/apath/afile.txt",
search: "",
hash: "#ahash",
},
{ url: 'http://example.com/?test#hash',
{
url: "http://example.com/?test#hash",
base: undefined,
error: false,
href: 'http://example.com/?test#hash',
origin: 'http://example.com',
protocol: 'http:',
username: '',
password: '',
host: 'example.com',
hostname: 'example.com',
port: '',
pathname: '/',
search: '?test',
hash: '#hash'
href: "http://example.com/?test#hash",
origin: "http://example.com",
protocol: "http:",
username: "",
password: "",
host: "example.com",
hostname: "example.com",
port: "",
pathname: "/",
search: "?test",
hash: "#hash",
},
{ url: 'http://example.com/?test',
{
url: "http://example.com/?test",
base: undefined,
error: false,
href: 'http://example.com/?test',
origin: 'http://example.com',
protocol: 'http:',
username: '',
password: '',
host: 'example.com',
hostname: 'example.com',
port: '',
pathname: '/',
search: '?test',
hash: ''
href: "http://example.com/?test",
origin: "http://example.com",
protocol: "http:",
username: "",
password: "",
host: "example.com",
hostname: "example.com",
port: "",
pathname: "/",
search: "?test",
hash: "",
},
{ url: 'http://example.com/carrot#question%3f',
{
url: "http://example.com/carrot#question%3f",
base: undefined,
error: false,
hash: '#question%3f'
hash: "#question%3f",
},
{ url: 'https://example.com:4443?',
{
url: "https://example.com:4443?",
base: undefined,
error: false,
protocol: 'https:',
port: '4443',
pathname: '/',
hash: '',
search: ''
protocol: "https:",
port: "4443",
pathname: "/",
hash: "",
search: "",
},
{ url: 'http://www.abc.com/apath/afile.txt#ahash?asearch',
{
url: "http://www.abc.com/apath/afile.txt#ahash?asearch",
base: undefined,
error: false,
href: 'http://www.abc.com/apath/afile.txt#ahash?asearch',
protocol: 'http:',
pathname: '/apath/afile.txt',
hash: '#ahash?asearch',
search: ''
href: "http://www.abc.com/apath/afile.txt#ahash?asearch",
protocol: "http:",
pathname: "/apath/afile.txt",
hash: "#ahash?asearch",
search: "",
},
{ url: 'http://www.abc.com/apath/afile.txt?asearch#ahash',
{
url: "http://www.abc.com/apath/afile.txt?asearch#ahash",
base: undefined,
error: false,
href: 'http://www.abc.com/apath/afile.txt?asearch#ahash',
protocol: 'http:',
pathname: '/apath/afile.txt',
hash: '#ahash',
search: '?asearch'
href: "http://www.abc.com/apath/afile.txt?asearch#ahash",
protocol: "http:",
pathname: "/apath/afile.txt",
hash: "#ahash",
search: "?asearch",
},
{ url: 'http://abc.com/apath/afile.txt?#ahash',
{
url: "http://abc.com/apath/afile.txt?#ahash",
base: undefined,
error: false,
pathname: '/apath/afile.txt',
hash: '#ahash',
search: ''
pathname: "/apath/afile.txt",
hash: "#ahash",
search: "",
},
{ url: 'http://auser:apassword@www.abc.com:90/apath/afile.txt?asearch#ahash',
{
url: "http://auser:apassword@www.abc.com:90/apath/afile.txt?asearch#ahash",
base: undefined,
error: false,
protocol: 'http:',
username: 'auser',
password: 'apassword',
host: 'www.abc.com:90',
hostname: 'www.abc.com',
port: '90',
pathname: '/apath/afile.txt',
hash: '#ahash',
search: '?asearch',
origin: 'http://www.abc.com:90'
protocol: "http:",
username: "auser",
password: "apassword",
host: "www.abc.com:90",
hostname: "www.abc.com",
port: "90",
pathname: "/apath/afile.txt",
hash: "#ahash",
search: "?asearch",
origin: "http://www.abc.com:90",
},
{ url: '/foo#bar',
base: 'www.test.org',
error: true,
},
{ url: '/foo#bar',
base: null,
error: true,
},
{ url: '/foo#bar',
base: 42,
error: true,
},
{ url: 'ftp://ftp.something.net',
{ url: "/foo#bar", base: "www.test.org", error: true },
{ url: "/foo#bar", base: null, error: true },
{ url: "/foo#bar", base: 42, error: true },
{
url: "ftp://ftp.something.net",
base: undefined,
error: false,
protocol: 'ftp:',
protocol: "ftp:",
},
{ url: 'file:///tmp/file',
{
url: "file:///tmp/file",
base: undefined,
error: false,
protocol: 'file:',
protocol: "file:",
},
{ url: 'gopher://gopher.something.net',
{
url: "gopher://gopher.something.net",
base: undefined,
error: false,
protocol: 'gopher:',
protocol: "gopher:",
},
{ url: 'ws://ws.something.net',
{
url: "ws://ws.something.net",
base: undefined,
error: false,
protocol: 'ws:',
protocol: "ws:",
},
{ url: 'wss://ws.something.net',
{
url: "wss://ws.something.net",
base: undefined,
error: false,
protocol: 'wss:',
protocol: "wss:",
},
{ url: 'foo://foo.something.net',
{
url: "foo://foo.something.net",
base: undefined,
error: false,
protocol: 'foo:',
protocol: "foo:",
},
];
while(tests.length) {
while (tests.length) {
var test = tests.shift();
var error = false;
@@ -217,7 +229,7 @@ onmessage = function() {
} else {
url = new URL(test.url);
}
} catch(e) {
} catch (e) {
error = true;
}
@@ -226,47 +238,79 @@ onmessage = function() {
continue;
}
if ('href' in test) is(url.href, test.href, "href");
if ('origin' in test) is(url.origin, test.origin, "origin");
if ('protocol' in test) is(url.protocol, test.protocol, "protocol");
if ('username' in test) is(url.username, test.username, "username");
if ('password' in test) is(url.password, test.password, "password");
if ('host' in test) is(url.host, test.host, "host");
if ('hostname' in test) is(url.hostname, test.hostname, "hostname");
if ('port' in test) is(url.port, test.port, "port");
if ('pathname' in test) is(url.pathname, test.pathname, "pathname");
if ('search' in test) is(url.search, test.search, "search");
if ('hash' in test) is(url.hash, test.hash, "hash");
if ("href" in test) is(url.href, test.href, "href");
if ("origin" in test) is(url.origin, test.origin, "origin");
if ("protocol" in test) is(url.protocol, test.protocol, "protocol");
if ("username" in test) is(url.username, test.username, "username");
if ("password" in test) is(url.password, test.password, "password");
if ("host" in test) is(url.host, test.host, "host");
if ("hostname" in test) is(url.hostname, test.hostname, "hostname");
if ("port" in test) is(url.port, test.port, "port");
if ("pathname" in test) is(url.pathname, test.pathname, "pathname");
if ("search" in test) is(url.search, test.search, "search");
if ("hash" in test) is(url.hash, test.hash, "hash");
url = new URL('https://www.example.net/what#foo?bar');
url = new URL("https://www.example.net/what#foo?bar");
ok(url, "Url exists!");
if ('href' in test) url.href = test.href;
if ('protocol' in test) url.protocol = test.protocol;
if ('username' in test && test.username) url.username = test.username;
if ('password' in test && test.password) url.password = test.password;
if ('host' in test) url.host = test.host;
if ('hostname' in test) url.hostname = test.hostname;
if ('port' in test) url.port = test.port;
if ('pathname' in test) url.pathname = test.pathname;
if ('search' in test) url.search = test.search;
if ('hash' in test) url.hash = test.hash;
if ("href" in test) url.href = test.href;
if ("protocol" in test) url.protocol = test.protocol;
if ("username" in test && test.username) url.username = test.username;
if ("password" in test && test.password) url.password = test.password;
if ("host" in test) url.host = test.host;
if ("hostname" in test) url.hostname = test.hostname;
if ("port" in test) url.port = test.port;
if ("pathname" in test) url.pathname = test.pathname;
if ("search" in test) url.search = test.search;
if ("hash" in test) url.hash = test.hash;
if ('href' in test) is(url.href, test.href, "href");
if ('origin' in test) is(url.origin, test.origin, "origin");
if ('protocol' in test) is(url.protocol, test.protocol, "protocol");
if ('username' in test) is(url.username, test.username, "username");
if ('password' in test) is(url.password, test.password, "password");
if ('host' in test) is(url.host, test.host, "host");
if ('hostname' in test) is(test.hostname, url.hostname, "hostname");
if ('port' in test) is(test.port, url.port, "port");
if ('pathname' in test) is(test.pathname, url.pathname, "pathname");
if ('search' in test) is(test.search, url.search, "search");
if ('hash' in test) is(test.hash, url.hash, "hash");
if ("href" in test) is(url.href, test.href, "href");
if ("origin" in test) is(url.origin, test.origin, "origin");
if ("protocol" in test) is(url.protocol, test.protocol, "protocol");
if ("username" in test) is(url.username, test.username, "username");
if ("password" in test) is(url.password, test.password, "password");
if ("host" in test) is(url.host, test.host, "host");
if ("hostname" in test) is(test.hostname, url.hostname, "hostname");
if ("port" in test) is(test.port, url.port, "port");
if ("pathname" in test) is(test.pathname, url.pathname, "pathname");
if ("search" in test) is(test.search, url.search, "search");
if ("hash" in test) is(test.hash, url.hash, "hash");
if ('href' in test) is (test.href, url + '', 'stringify works');
if ("href" in test) is(test.href, url + "", "stringify works");
}
postMessage({type: 'finish' });
}
is(
new URL("/static/client/runtime.js", "x:/").href,
"x:/static/client/runtime.js",
"URL constructor accepts root-relative input with x:/ base",
);
ok("canParse" in URL, "URL.canParse exists");
ok(
URL.canParse("/static/client/runtime.js", "x:/"),
"URL.canParse accepts root-relative input with x:/ base",
);
ok(
URL.canParse("/static/client/runtime.js", self.location.href),
"URL.canParse accepts root-relative input with worker location base",
);
ok(
!URL.canParse("/static/client/runtime.js"),
"URL.canParse rejects root-relative input without base",
);
ok(
!URL.canParse("/static/client/runtime.js", "mailto:test@example.com"),
"URL.canParse rejects root-relative input against opaque mailto: base",
);
let mailtoRelativeFailed = false;
try {
new URL("/static/client/runtime.js", "mailto:test@example.com");
} catch (e) {
mailtoRelativeFailed = true;
}
ok(
mailtoRelativeFailed,
"constructor rejects root-relative input against opaque mailto: base",
);
postMessage({ type: "finish" });
};
+1
View File
@@ -142,6 +142,7 @@ EditorBase::EditorBase()
, mDispatchInputEvent(true)
, mIsInEditAction(false)
, mHidingCaret(false)
, mIsHTMLEditorClass(false)
{
}
+20
View File
@@ -114,6 +114,7 @@ class DeleteNodeTransaction;
class DeleteTextTransaction;
class EditAggregateTransaction;
class ErrorResult;
class HTMLEditor;
class InsertNodeTransaction;
class InsertTextTransaction;
class JoinNodeTransaction;
@@ -121,6 +122,7 @@ class PlaceholderTransaction;
class RemoveStyleSheetTransaction;
class SplitNodeTransaction;
class TextComposition;
class TextEditor;
struct EditorDOMPoint;
namespace dom {
@@ -1113,14 +1115,32 @@ protected:
bool mIsInEditAction;
// Whether caret is hidden forcibly.
bool mHidingCaret;
// Whether we are an HTML editor class.
bool mIsHTMLEditorClass;
friend bool NSCanUnload(nsISupports* serviceMgr);
friend class AutoRules;
friend class AutoSelectionRestorer;
friend class AutoTransactionsConserveSelection;
friend class RangeUpdater;
friend class nsIEditor;
};
} // namespace mozilla
// nsIEditor helper functions.
// Here because of code context.
mozilla::EditorBase*
nsIEditor::AsEditorBase()
{
return static_cast<mozilla::EditorBase*>(this);
}
const mozilla::EditorBase*
nsIEditor::AsEditorBase() const
{
return static_cast<const mozilla::EditorBase*>(this);
}
#endif // #ifndef mozilla_EditorBase_h
+8 -1
View File
@@ -233,9 +233,16 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
NS_IMETHODIMP
HTMLEditRules::Init(TextEditor* aTextEditor)
{
if (NS_WARN_IF(!aTextEditor)) {
return NS_ERROR_INVALID_ARG;
}
InitFields();
mHTMLEditor = static_cast<HTMLEditor*>(aTextEditor);
mHTMLEditor = aTextEditor->AsHTMLEditor();
if (NS_WARN_IF(!mHTMLEditor)) {
return NS_ERROR_INVALID_ARG;
}
// call through to base class Init
nsresult rv = TextEditRules::Init(aTextEditor);
+2 -1
View File
@@ -132,6 +132,7 @@ HTMLEditor::HTMLEditor()
, mPositionedObjectBorderTop(0)
, mGridSize(0)
{
mIsHTMLEditorClass = true;
}
HTMLEditor::~HTMLEditor()
@@ -501,7 +502,7 @@ HTMLEditor::InitRules()
// instantiate the rules for the html editor
mRules = new HTMLEditRules();
}
return mRules->Init(static_cast<TextEditor*>(this));
return mRules->Init(this);
}
NS_IMETHODIMP
+16
View File
@@ -1108,4 +1108,20 @@ private:
} // namespace mozilla
// nsIEditor helper functions.
// Here because of code context.
mozilla::HTMLEditor*
nsIEditor::AsHTMLEditor()
{
return static_cast<mozilla::EditorBase*>(this)->mIsHTMLEditorClass ?
static_cast<mozilla::HTMLEditor*>(this) : nullptr;
}
const mozilla::HTMLEditor*
nsIEditor::AsHTMLEditor() const
{
return static_cast<const mozilla::EditorBase*>(this)->mIsHTMLEditorClass ?
static_cast<const mozilla::HTMLEditor*>(this) : nullptr;
}
#endif // #ifndef mozilla_HTMLEditor_h
+21 -18
View File
@@ -30,24 +30,18 @@ namespace mozilla {
using namespace dom;
#ifdef DEBUG
nsresult
HTMLEditorEventListener::Connect(EditorBase* aEditorBase)
{
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryObject(aEditorBase);
nsCOMPtr<nsIHTMLInlineTableEditor> htmlInlineTableEditor =
do_QueryObject(aEditorBase);
NS_PRECONDITION(htmlEditor && htmlInlineTableEditor,
"Set HTMLEditor or its sub class");
return EditorEventListener::Connect(aEditorBase);
}
#endif
HTMLEditor*
HTMLEditorEventListener::GetHTMLEditor()
{
// mEditor must be HTMLEditor or its subclass.
return static_cast<HTMLEditor*>(mEditorBase);
if (NS_WARN_IF(!aEditorBase)) {
return NS_ERROR_INVALID_ARG;
}
// Guarantee that mEditorBase is always HTMLEditor.
HTMLEditor* htmlEditor = aEditorBase->AsHTMLEditor();
if (NS_WARN_IF(!htmlEditor)) {
return NS_ERROR_INVALID_ARG;
}
return EditorEventListener::Connect(htmlEditor);
}
nsresult
@@ -59,7 +53,8 @@ HTMLEditorEventListener::MouseUp(nsIDOMMouseEvent* aMouseEvent)
// FYI: We need to notify HTML editor of mouseup even if it's consumed
// because HTML editor always needs to release grabbing resizer.
HTMLEditor* htmlEditor = GetHTMLEditor();
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
MOZ_ASSERT(htmlEditor);
nsCOMPtr<nsIDOMEventTarget> target;
nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
@@ -85,7 +80,9 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
WidgetMouseEvent* mousedownEvent =
aMouseEvent->AsEvent()->WidgetEventPtr()->AsMouseEvent();
HTMLEditor* htmlEditor = GetHTMLEditor();
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
MOZ_ASSERT(htmlEditor);
// Contenteditable should disregard mousedowns outside it.
// IsAcceptableInputEvent() checks it for a mouse event.
if (!htmlEditor->IsAcceptableInputEvent(mousedownEvent)) {
@@ -221,13 +218,19 @@ HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
nsresult
HTMLEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
{
if (NS_WARN_IF(DetachedFromEditor())) {
return NS_OK;
}
nsCOMPtr<nsIDOMEventTarget> target;
nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
GetHTMLEditor()->DoInlineTableEditingAction(element);
HTMLEditor* htmlEditor = mEditorBase->AsHTMLEditor();
MOZ_ASSERT(htmlEditor);
htmlEditor->DoInlineTableEditingAction(element);
return EditorEventListener::MouseClick(aMouseEvent);
}
+1 -5
View File
@@ -25,17 +25,13 @@ public:
{
}
#ifdef DEBUG
// WARNING: You must be use HTMLEditor or its sub class for this class.
// Connect() fails if aEditorBase isn't an HTMLEditor instance.
virtual nsresult Connect(EditorBase* aEditorBase) override;
#endif
protected:
virtual nsresult MouseDown(nsIDOMMouseEvent* aMouseEvent) override;
virtual nsresult MouseUp(nsIDOMMouseEvent* aMouseEvent) override;
virtual nsresult MouseClick(nsIDOMMouseEvent* aMouseEvent) override;
inline HTMLEditor* GetHTMLEditor();
};
} // namespace mozilla
+1
View File
@@ -10,6 +10,7 @@
#include "gfxFontUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/EditorUtils.h" // AutoEditBatch, AutoRules
#include "mozilla/HTMLEditor.h"
#include "mozilla/mozalloc.h"
#include "mozilla/Preferences.h"
#include "mozilla/TextEditRules.h"
+14
View File
@@ -247,4 +247,18 @@ protected:
} // namespace mozilla
// nsIEditor helper functions.
// Here because of code context.
mozilla::TextEditor*
nsIEditor::AsTextEditor()
{
return static_cast<mozilla::TextEditor*>(this);
}
const mozilla::TextEditor*
nsIEditor::AsTextEditor() const
{
return static_cast<const mozilla::TextEditor*>(this);
}
#endif // #ifndef mozilla_TextEditor_h
+38
View File
@@ -21,6 +21,14 @@ interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
%{C++
namespace mozilla {
class EditorBase;
class HTMLEditor;
class TextEditor;
} // namespace mozilla
%}
[scriptable, uuid(094be624-f0bf-400f-89e2-6a84baab9474)]
interface nsIEditor : nsISupports
{
@@ -564,4 +572,34 @@ interface nsIEditor : nsISupports
* or nsIEditorObserver::CancelEditAction(). Otherwise, false.
*/
[noscript] readonly attribute boolean isInEditAction;
%{C++
/**
* AsEditorBase() returns a pointer to EditorBase class.
*
* In order to avoid circular dependency issues, this method is defined
* in mozilla/EditorBase.h. Consumers need to #include that header.
*/
inline mozilla::EditorBase* AsEditorBase();
inline const mozilla::EditorBase* AsEditorBase() const;
/**
* AsTextEditor() returns a pointer to TextEditor class.
*
* In order to avoid circular dependency issues, this method is defined
* in mozilla/TextEditor.h. Consumers need to #include that header.
*/
inline mozilla::TextEditor* AsTextEditor();
inline const mozilla::TextEditor* AsTextEditor() const;
/**
* AsHTMLEditor() returns a pointer to HTMLEditor class.
*
* In order to avoid circular dependency issues, this method is defined
* in mozilla/HTMLEditor.h. Consumers need to #include that header.
*/
inline mozilla::HTMLEditor* AsHTMLEditor();
inline const mozilla::HTMLEditor* AsHTMLEditor() const;
%}
};
@@ -97,9 +97,6 @@ using namespace mozilla::dom;
#define INLINESPELL_STARTED_TOPIC "inlineSpellChecker-spellCheck-started"
#define INLINESPELL_ENDED_TOPIC "inlineSpellChecker-spellCheck-ended"
static bool ContentIsDescendantOf(nsINode* aPossibleDescendant,
nsINode* aPossibleAncestor);
static const char kMaxSpellCheckSelectionSize[] = "extensions.spellcheck.inline.max-misspellings";
mozInlineSpellStatus::mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker)
@@ -238,7 +235,7 @@ mozInlineSpellStatus::InitForNavigation(
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> currentAnchor = do_QueryInterface(aOldAnchorNode, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (root && currentAnchor && ! ContentIsDescendantOf(currentAnchor, root)) {
if (root && currentAnchor && !nsContentUtils::ContentIsShadowIncludingDescendantOf(currentAnchor, root)) {
*aContinue = false;
return NS_OK;
}
@@ -1490,8 +1487,11 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
return NS_OK;
}
aWordUtil.SetEnd(endNode, endOffset);
aWordUtil.SetPosition(beginNode, beginOffset);
nsresult rv = aWordUtil.SetPositionAndEnd(beginNode, beginOffset, endNode, endOffset);
if (NS_FAILED(rv)) {
// Just bail out and don't try to spell-check this
return NS_OK;
}
}
// aWordUtil.SetPosition flushes pending notifications, check editor again.
@@ -1840,24 +1840,6 @@ nsresult mozInlineSpellChecker::SaveCurrentSelectionPosition()
return NS_OK;
}
// This is a copy of nsContentUtils::ContentIsDescendantOf. Another crime
// for XPCOM's rap sheet
bool // static
ContentIsDescendantOf(nsINode* aPossibleDescendant,
nsINode* aPossibleAncestor)
{
NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
do {
if (aPossibleDescendant == aPossibleAncestor)
return true;
aPossibleDescendant = aPossibleDescendant->GetParentNode();
} while (aPossibleDescendant);
return false;
}
// mozInlineSpellChecker::HandleNavigationEvent
//
// Acts upon mouse clicks and keyboard navigation changes, spell checking
@@ -23,6 +23,8 @@
#include "nsIFrame.h"
#include <algorithm>
#include "mozilla/BinarySearch.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/dom/ShadowRoot.h"
using namespace mozilla;
@@ -69,8 +71,10 @@ mozInlineSpellWordUtil::Init(nsWeakPtr aWeakEditor)
mDOMDocument = domDoc;
mDocument = do_QueryInterface(domDoc);
// Find the root node for the editor. For contenteditable we'll need something
// cleverer here.
mIsContentEditableOrDesignMode = !!editor->AsHTMLEditor();
// Find the root node for the editor. For contenteditable the mRootNode could
// change to shadow root if the begin and end are inside the shadowDOM.
nsCOMPtr<nsIDOMElement> rootElt;
rv = editor->GetRootElement(getter_AddRefs(rootElt));
NS_ENSURE_SUCCESS(rv, rv);
@@ -154,7 +158,7 @@ FindNextTextNode(nsINode* aNode, int32_t aOffset, nsINode* aRoot)
return checkNode;
}
// mozInlineSpellWordUtil::SetEnd
// mozInlineSpellWordUtil::SetPositionAndEnd
//
// We have two ranges "hard" and "soft". The hard boundary is simply
// the scope of the root node. The soft boundary is that which is set
@@ -172,34 +176,44 @@ FindNextTextNode(nsINode* aNode, int32_t aOffset, nsINode* aRoot)
// position.
nsresult
mozInlineSpellWordUtil::SetEnd(nsINode* aEndNode, int32_t aEndOffset)
mozInlineSpellWordUtil::SetPositionAndEnd(nsINode* aPositionNode,
int32_t aPositionOffset,
nsINode* aEndNode,
int32_t aEndOffset)
{
MOZ_ASSERT(aPositionNode, "Null begin node?");
NS_PRECONDITION(aEndNode, "Null end node?");
NS_ASSERTION(mRootNode, "Not initialized");
// Find a appropriate root if we are dealing with contenteditable nodes which
// are in the shadow DOM. See UXP Issue #3011
if (mIsContentEditableOrDesignMode) {
nsINode* rootNode = aPositionNode->SubtreeRoot();
if (rootNode != aEndNode->SubtreeRoot()) {
return NS_ERROR_FAILURE;
}
if (mozilla::dom::ShadowRoot::FromNode(rootNode)) {
mRootNode = rootNode;
}
}
InvalidateWords();
if (!IsTextNode(aPositionNode)) {
// Start at the start of the first text node after aNode/aOffset.
aPositionNode = FindNextTextNode(aPositionNode, aPositionOffset, mRootNode);
aPositionOffset = 0;
}
mSoftBegin = NodeOffset(aPositionNode, aPositionOffset);
if (!IsTextNode(aEndNode)) {
// End at the start of the first text node after aEndNode/aEndOffset.
aEndNode = FindNextTextNode(aEndNode, aEndOffset, mRootNode);
aEndOffset = 0;
}
mSoftEnd = NodeOffset(aEndNode, aEndOffset);
return NS_OK;
}
nsresult
mozInlineSpellWordUtil::SetPosition(nsINode* aNode, int32_t aOffset)
{
InvalidateWords();
if (!IsTextNode(aNode)) {
// Start at the start of the first text node after aNode/aOffset.
aNode = FindNextTextNode(aNode, aOffset, mRootNode);
aOffset = 0;
}
mSoftBegin = NodeOffset(aNode, aOffset);
nsresult rv = EnsureWords();
if (NS_FAILED(rv)) {
@@ -207,8 +221,10 @@ mozInlineSpellWordUtil::SetPosition(nsINode* aNode, int32_t aOffset)
}
int32_t textOffset = MapDOMPositionToSoftTextOffset(mSoftBegin);
if (textOffset < 0)
if (textOffset < 0) {
return NS_OK;
}
mNextWordIndex = FindRealWordContaining(textOffset, HINT_END, true);
return NS_OK;
}
@@ -29,12 +29,11 @@ class nsINode;
* The basic operation is:
*
* 1. Call Init with the weak pointer to the editor that you're using.
* 2. Call SetEnd to set where you want to stop spellchecking. We'll stop
* at the word boundary after that. If SetEnd is not called, we'll stop
* at the end of the document's root element.
* 3. Call SetPosition to initialize the current position inside the
* previously given range.
* 4. Call GetNextWord over and over until it returns false.
* 2. Call SetPositionAndEnd to to initialize the current position inside the
* previously given range and set where you want to stop spellchecking.
* We'll stop at the word boundary after that. If SetEnd is not called,
* we'll stop at the end of the root element.
* 3. Call GetNextWord over and over until it returns false.
*/
class mozInlineSpellWordUtil
@@ -57,17 +56,22 @@ public:
};
mozInlineSpellWordUtil()
: mRootNode(nullptr),
mSoftBegin(nullptr, 0), mSoftEnd(nullptr, 0),
mNextWordIndex(-1), mSoftTextValid(false) {}
: mIsContentEditableOrDesignMode(false)
, mRootNode(nullptr)
, mSoftBegin(nullptr, 0)
, mSoftEnd(nullptr, 0)
, mNextWordIndex(-1)
, mSoftTextValid(false)
{}
nsresult Init(nsWeakPtr aWeakEditor);
nsresult SetEnd(nsINode* aEndNode, int32_t aEndOffset);
// sets the current position, this should be inside the range. If we are in
// the middle of a word, we'll move to its start.
nsresult SetPosition(nsINode* aNode, int32_t aOffset);
// Sets the current position and end. This should be inside the range.
// If we are in the middle of a word, we'll move to its start.
nsresult SetPositionAndEnd(nsINode* aPositionNode,
int32_t aPositionOffset,
nsINode* aEndNode,
int32_t aEndOffset);
// Given a point inside or immediately following a word, this returns the
// DOM range that exactly encloses that word's characters. The current
@@ -100,7 +104,8 @@ private:
// cached stuff for the editor, set by Init
nsCOMPtr<nsIDOMDocument> mDOMDocument;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsIDocument> mDocument;
bool mIsContentEditableOrDesignMode;
// range to check, see SetPosition and SetEnd
nsINode* mRootNode;
+59
View File
@@ -37,6 +37,10 @@ function TypedArrayLengthMethod() {
return TypedArrayLength(this);
}
function TypedArrayContentTypeIsBigIntMethod() {
return IsBigInt64TypedArray(this) || IsBigUint64TypedArray(this);
}
function GetAttachedArrayBuffer(tarray) {
var buffer = ViewedArrayBufferIfReified(tarray);
if (IsDetachedBuffer(buffer))
@@ -895,6 +899,61 @@ function TypedArrayToReversed() {
return A;
}
// ES2023 23.2.3.36 %TypedArray%.prototype.with ( index, value )
function TypedArrayWith(index, value) {
// Step 1.
var O = this;
// Step 2.
// This function is not generic.
// We want to make sure that we have an attached buffer, per spec prose.
var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
// If we got here, `this` is either a typed array or a wrapper for one.
// Step 3.
var len;
if (isTypedArray)
len = TypedArrayLength(O);
else
len = callFunction(CallTypedArrayMethodIfWrapped, O, "TypedArrayLengthMethod");
// Steps 4-6.
var relativeIndex = ToInteger(index);
var actualIndex = relativeIndex >= 0 ? relativeIndex : len + relativeIndex;
// Steps 7-8.
var isBigIntContentType;
if (isTypedArray) {
isBigIntContentType = callFunction(TypedArrayContentTypeIsBigIntMethod, O);
} else {
isBigIntContentType = callFunction(CallTypedArrayMethodIfWrapped, O,
"TypedArrayContentTypeIsBigIntMethod");
}
var numericValue = isBigIntContentType ? ToBigInt(value) : ToNumber(value);
// Step 9.
if (actualIndex < 0 || actualIndex >= len)
ThrowRangeError(JSMSG_BAD_INDEX);
// Step 10.
var A = TypedArrayCreateSameType(O, len);
// Steps 11-12.
for (var k = 0; k < len; k++) {
var fromValue;
if (k === actualIndex) {
fromValue = numericValue;
} else {
fromValue = O[k];
}
A[k] = fromValue;
}
// Step 13.
return A;
}
// ES6 draft 20150220 22.2.3.22.1 %TypedArray%.prototype.set(array [, offset])
function SetFromNonTypedArray(target, array, targetOffset, targetLength, targetBuffer) {
assert(!IsPossiblyWrappedTypedArray(array),
@@ -97,6 +97,10 @@ assertThrowsInstanceOf(() => {
array.values();
}, TypeError);
assertThrowsInstanceOf(() => {
array.with(POISON, POISON);
}, TypeError);
assertThrowsInstanceOf(() => {
array.every(POISON);
}, TypeError);
+84
View File
@@ -0,0 +1,84 @@
for (var constructor of anyTypedArrayConstructors) {
assertEq(constructor.prototype.with.length, 2);
var original = new constructor([1, 2, 3, 4]);
var updated = original.with(1, 9);
assertDeepEq(updated, new constructor([1, 9, 3, 4]));
assertDeepEq(original, new constructor([1, 2, 3, 4]));
assertEq(updated === original, false);
assertEq(updated.constructor, constructor);
assertDeepEq(new constructor([1, 2, 3]).with(-1, 7),
new constructor([1, 2, 7]));
assertDeepEq(new constructor([1, 2, 3]).with(-0, 7),
new constructor([7, 2, 3]));
assertThrowsInstanceOf(() => {
new constructor([1, 2, 3]).with(3, 9);
}, RangeError);
assertThrowsInstanceOf(() => {
new constructor([1, 2, 3]).with(-4, 9);
}, RangeError);
var valueOrder = [];
var value = {
valueOf() {
valueOrder.push("valueOf");
return 9;
}
};
assertThrowsInstanceOf(() => {
new constructor([1, 2, 3]).with(9, value);
}, RangeError);
assertEq(valueOrder.join(","), "valueOf");
var ctorIgnored = new constructor([5, 6, 7]);
Object.defineProperty(ctorIgnored, "constructor", {
get() {
throw new Error("constructor accessor called");
}
});
assertDeepEq(ctorIgnored.with(0, 4), new constructor([4, 6, 7]));
if (constructor === Uint8ClampedArray ||
(typeof isSharedConstructor === "function" && isSharedConstructor(constructor) &&
constructor.name === Uint8ClampedArray.name))
{
assertDeepEq(new constructor([0, 1, 2]).with(1, 2.6),
new constructor([0, 3, 2]));
}
var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./,
new Proxy(new constructor(), {})];
invalidReceivers.forEach(invalidReceiver => {
assertThrowsInstanceOf(() => {
constructor.prototype.with.call(invalidReceiver, 0, 1);
}, TypeError,
"Assert that with fails if this value is not a TypedArray");
});
}
for (var constructor of typedArrayConstructors) {
if (typeof newGlobal === "function") {
var withFn = newGlobal()[constructor.name].prototype.with;
var original = new constructor([3, 2, 1]);
var updated = withFn.call(original, 1, 8);
assertDeepEq(updated, new constructor([3, 8, 1]));
assertDeepEq(original, new constructor([3, 2, 1]));
}
}
if (typeof BigInt64Array === "function" && typeof BigUint64Array === "function") {
var bigIntArray = new BigInt64Array([1n, 2n, 3n]);
assertEq(BigInt64Array.prototype.with.length, 2);
assertDeepEq(bigIntArray.with(1, 9n), new BigInt64Array([1n, 9n, 3n]));
assertThrowsInstanceOf(() => bigIntArray.with(1, 9), TypeError);
var bigUintArray = new BigUint64Array([1n, 2n, 3n]);
assertDeepEq(bigUintArray.with(2, 4n), new BigUint64Array([1n, 2n, 4n]));
assertThrowsInstanceOf(() => bigUintArray.with(9, 1), TypeError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);
+1
View File
@@ -1629,6 +1629,7 @@ TypedArrayObject::protoFunctions[] = {
JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
JS_SELF_HOSTED_FN("toReversed", "TypedArrayToReversed", 0, 0),
JS_SELF_HOSTED_FN("with", "TypedArrayWith", 2, 0),
JS_SELF_HOSTED_FN("slice", "TypedArraySlice", 2, 0),
JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
JS_SELF_HOSTED_FN("sort", "TypedArraySort", 1, 0),
+6
View File
@@ -1977,6 +1977,12 @@ pref("network.predictor.max-resources-per-entry", 100);
pref("network.predictor.max-uri-length", 500);
pref("network.predictor.cleaned-up", false);
// Cloudflare Image Resizing compatibility.
// When enabled, URLs containing the "/cdn-cgi/image/" marker will have
// everything after that marker treated as opaque path data. This matches
// Cloudflare's expectations for Image Resizing URLs.
pref("network.url.cloudflare_image_resizing.enabled", true);
// The following prefs pertain to the negotiate-auth extension (see bug 17578),
// which provides transparent Kerberos or NTLM authentication using the SPNEGO
// protocol. Each pref is a comma-separated list of keys, where each key has
+53
View File
@@ -26,6 +26,7 @@
#include "prprf.h"
#include "nsReadableUtils.h"
#include "nsPrintfCString.h"
#include "mozilla/Preferences.h" //fixes up dependency issues in non-unified building
using mozilla::dom::EncodingUtils;
using namespace mozilla::ipc;
@@ -1105,6 +1106,58 @@ nsStandardURL::ParseURL(const char *spec, int32_t specLen)
nsresult
nsStandardURL::ParsePath(const char *spec, uint32_t pathPos, int32_t pathLen)
{
// Cloudflare Image Resizing compatibility (pref-controlled)
//
// This feature detects the "/cdn-cgi/image/" marker in the URL path and
// treats everything after it as opaque path data. Cloudflare's Image
// Resizing service expects clients to preserve the entire suffix exactly.
//
// Because this code runs in a hot path (URL parsing), we avoid calling
// Preferences::GetBool() repeatedly. Instead, we use AddBoolVarCache()
// to cache the pref value once and read it cheaply thereafter.
// Cached preference: true = enable Cloudflare Image Resizing fixup
static bool sCloudflareImageResizingEnabled = true;
static bool sCloudflareImageResizingPrefCached = false;
if (!sCloudflareImageResizingPrefCached) {
Preferences::AddBoolVarCache(
&sCloudflareImageResizingEnabled,
"network.url.cloudflare_image_resizing.enabled",
true // default if pref does not exist
);
sCloudflareImageResizingPrefCached = true;
}
if (sCloudflareImageResizingEnabled) {
// Extract the full path substring from the full URL spec.
nsDependentCSubstring fullPath(spec + pathPos, pathLen);
// Prepare iterators for scanning the path.
nsACString::const_iterator begin, end;
fullPath.BeginReading(begin);
fullPath.EndReading(end);
// Search for the Cloudflare Image Resizing marker.
nsACString::const_iterator cfPos = begin;
if (FindInReadable(NS_LITERAL_CSTRING("/cdn-cgi/image/"), cfPos, end)) {
// Compute how far into the path the marker was found.
uint32_t offset = cfPos.get() - begin.get();
// Rewrite the internal path representation so that the path
// begins at the Cloudflare marker. Everything before it is ignored.
mPath.mPos = pathPos + offset;
mPath.mLen = pathLen - offset;
// We handled the path; no further parsing needed.
return NS_OK;
}
}
LOG(("ParsePath: %s pathpos %d len %d\n",spec,pathPos,pathLen));
if (pathLen > net_GetURLMaxLength()) {