зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
2a6cd87e82
2
.gdbinit
2
.gdbinit
|
@ -179,3 +179,5 @@ end
|
|||
def ft
|
||||
call $arg0->DumpFrameTree()
|
||||
end
|
||||
|
||||
source .gdbinit_python
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
python
|
||||
import sys
|
||||
sys.path.append('python/gdbpp/')
|
||||
import gdbpp
|
||||
end
|
|
@ -116,3 +116,6 @@ testing/talos/lib/
|
|||
testing/talos/talos/tests/tp5n.zip
|
||||
testing/talos/talos/tests/tp5n
|
||||
testing/talos/talos/tests/devtools/damp.manifest.develop
|
||||
|
||||
# Ignore files created when running a reftest.
|
||||
lextab.py
|
||||
|
|
|
@ -133,3 +133,6 @@ GPATH
|
|||
^testing/talos/talos/tests/tp5n.zip
|
||||
^testing/talos/talos/tests/tp5n
|
||||
^testing/talos/talos/tests/devtools/damp.manifest.develop
|
||||
|
||||
# Ignore files created when running a reftest.
|
||||
^lextab.py$
|
||||
|
|
|
@ -11,7 +11,9 @@ support-files =
|
|||
[browser_usercontextid_tabdrop.js]
|
||||
skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
|
||||
[browser_windowName.js]
|
||||
tags = openwindow
|
||||
[browser_windowOpen.js]
|
||||
tags = openwindow
|
||||
[browser_serviceworkers.js]
|
||||
[browser_broadcastchannel.js]
|
||||
[browser_blobUrl.js]
|
||||
|
|
|
@ -14,6 +14,25 @@ const {
|
|||
normalizeTime,
|
||||
} = ExtensionUtils;
|
||||
|
||||
let historySvc = Ci.nsINavHistoryService;
|
||||
const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
|
||||
["link", historySvc.TRANSITION_LINK],
|
||||
["typed", historySvc.TRANSITION_TYPED],
|
||||
["auto_bookmark", historySvc.TRANSITION_BOOKMARK],
|
||||
["auto_subframe", historySvc.TRANSITION_EMBED],
|
||||
["manual_subframe", historySvc.TRANSITION_FRAMED_LINK],
|
||||
]);
|
||||
|
||||
function getTransitionType(transition) {
|
||||
// cannot set a default value for the transition argument as the framework sets it to null
|
||||
transition = transition || "link";
|
||||
let transitionType = TRANSITION_TO_TRANSITION_TYPES_MAP.get(transition);
|
||||
if (!transitionType) {
|
||||
throw new Error(`|${transition}| is not a supported transition for history`);
|
||||
}
|
||||
return transitionType;
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts a nsINavHistoryResultNode into a plain object
|
||||
*
|
||||
|
@ -24,7 +43,7 @@ function convertNavHistoryResultNode(node) {
|
|||
id: node.pageGuid,
|
||||
url: node.uri,
|
||||
title: node.title,
|
||||
lastVisitTime: PlacesUtils.toTime(node.time),
|
||||
lastVisitTime: PlacesUtils.toDate(node.time).getTime(),
|
||||
visitCount: node.accessCount,
|
||||
};
|
||||
}
|
||||
|
@ -48,13 +67,39 @@ function convertNavHistoryContainerResultNode(container) {
|
|||
extensions.registerSchemaAPI("history", "history", (extension, context) => {
|
||||
return {
|
||||
history: {
|
||||
addUrl: function(details) {
|
||||
let transition, date;
|
||||
try {
|
||||
transition = getTransitionType(details.transition);
|
||||
} catch (error) {
|
||||
return Promise.reject({message: error.message});
|
||||
}
|
||||
if (details.visitTime) {
|
||||
date = normalizeTime(details.visitTime);
|
||||
}
|
||||
let pageInfo = {
|
||||
title: details.title,
|
||||
url: details.url,
|
||||
visits: [
|
||||
{
|
||||
transition,
|
||||
date,
|
||||
},
|
||||
],
|
||||
};
|
||||
try {
|
||||
return History.insert(pageInfo).then(() => undefined);
|
||||
} catch (error) {
|
||||
return Promise.reject({message: error.message});
|
||||
}
|
||||
},
|
||||
deleteAll: function() {
|
||||
return History.clear();
|
||||
},
|
||||
deleteRange: function(filter) {
|
||||
let newFilter = {
|
||||
beginDate: new Date(filter.startTime),
|
||||
endDate: new Date(filter.endTime),
|
||||
beginDate: normalizeTime(filter.startTime),
|
||||
endDate: normalizeTime(filter.endTime),
|
||||
};
|
||||
// History.removeVisitsByFilter returns a boolean, but our API should return nothing
|
||||
return History.removeVisitsByFilter(newFilter).then(() => undefined);
|
||||
|
|
|
@ -187,9 +187,8 @@
|
|||
},
|
||||
{
|
||||
"name": "addUrl",
|
||||
"unsupported": true,
|
||||
"type": "function",
|
||||
"description": "Adds a URL to the history at the current time with a $(topic:transition-types)[transition type] of \"link\".",
|
||||
"description": "Adds a URL to the history with a default visitTime of the current time and a default $(topic:transition-types)[transition type] of \"link\".",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
|
@ -198,7 +197,22 @@
|
|||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "The URL to add."
|
||||
"description": "The URL to add. Must be a valid URL that can be added to history."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The title of the page."
|
||||
},
|
||||
"transition": {
|
||||
"$ref": "TransitionType",
|
||||
"optional": true,
|
||||
"description": "The $(topic:transition-types)[transition type] for this visit from its referrer."
|
||||
},
|
||||
"visitTime": {
|
||||
"$ref": "HistoryTime",
|
||||
"optional": true,
|
||||
"description": "The date when this visit occurred."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -245,12 +259,12 @@
|
|||
"type": "object",
|
||||
"properties": {
|
||||
"startTime": {
|
||||
"type": "number",
|
||||
"description": "Items added to history after this date, represented in milliseconds since the epoch."
|
||||
"$ref": "HistoryTime",
|
||||
"description": "Items added to history after this date."
|
||||
},
|
||||
"endTime": {
|
||||
"type": "number",
|
||||
"description": "Items added to history before this date, represented in milliseconds since the epoch."
|
||||
"$ref": "HistoryTime",
|
||||
"description": "Items added to history before this date."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
|
||||
"resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
add_task(function* test_delete() {
|
||||
function background() {
|
||||
|
@ -30,7 +34,6 @@ add_task(function* test_delete() {
|
|||
}
|
||||
|
||||
const REFERENCE_DATE = new Date(1999, 9, 9, 9, 9);
|
||||
let {PlacesUtils} = Cu.import("resource://gre/modules/PlacesUtils.jsm", {});
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
|
@ -71,8 +74,8 @@ add_task(function* test_delete() {
|
|||
is(yield PlacesTestUtils.isPageInDB(testUrl), false, "expected url not found in history database");
|
||||
|
||||
let filter = {
|
||||
startTime: PlacesUtils.toTime(visits[1].visitDate),
|
||||
endTime: PlacesUtils.toTime(visits[3].visitDate),
|
||||
startTime: PlacesUtils.toDate(visits[1].visitDate),
|
||||
endTime: PlacesUtils.toDate(visits[3].visitDate),
|
||||
};
|
||||
|
||||
extension.sendMessage("delete-range", filter);
|
||||
|
@ -83,8 +86,8 @@ add_task(function* test_delete() {
|
|||
ok(yield PlacesTestUtils.isPageInDB(visits[5].uri), "expected uri found in history database");
|
||||
is(yield PlacesTestUtils.visitsInDB(visits[5].uri), 1, "1 visit for uri found in history database");
|
||||
|
||||
filter.startTime = PlacesUtils.toTime(visits[0].visitDate);
|
||||
filter.endTime = PlacesUtils.toTime(visits[5].visitDate);
|
||||
filter.startTime = PlacesUtils.toDate(visits[0].visitDate);
|
||||
filter.endTime = PlacesUtils.toDate(visits[5].visitDate);
|
||||
|
||||
extension.sendMessage("delete-range", filter);
|
||||
yield extension.awaitMessage("range-deleted");
|
||||
|
@ -191,3 +194,86 @@ add_task(function* test_search() {
|
|||
yield extension.unload();
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
add_task(function* test_add_url() {
|
||||
function background() {
|
||||
const TEST_DOMAIN = "http://example.com/";
|
||||
|
||||
browser.test.onMessage.addListener((msg, testData) => {
|
||||
let [details, type] = testData;
|
||||
details.url = details.url || `${TEST_DOMAIN}${type}`;
|
||||
if (msg === "add-url") {
|
||||
details.title = `Title for ${type}`;
|
||||
browser.history.addUrl(details).then(() => {
|
||||
return browser.history.search({text: details.url});
|
||||
}).then(results => {
|
||||
browser.test.assertEq(1, results.length, "1 result found when searching for added URL");
|
||||
browser.test.sendMessage("url-added", {details, result: results[0]});
|
||||
});
|
||||
} else if (msg === "expect-failure") {
|
||||
let expectedMsg = testData[2];
|
||||
browser.history.addUrl(details).then(() => {
|
||||
browser.test.fail(`Expected error thrown for ${type}`);
|
||||
}, error => {
|
||||
browser.test.assertTrue(
|
||||
error.message.includes(expectedMsg),
|
||||
`"Expected error thrown when trying to add a URL with ${type}`
|
||||
);
|
||||
browser.test.sendMessage("add-failed");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
browser.test.sendMessage("ready");
|
||||
}
|
||||
|
||||
let addTestData = [
|
||||
[{}, "default"],
|
||||
[{visitTime: new Date()}, "with_date"],
|
||||
[{visitTime: Date.now()}, "with_ms_number"],
|
||||
[{visitTime: Date.now().toString()}, "with_ms_string"],
|
||||
[{visitTime: new Date().toISOString()}, "with_iso_string"],
|
||||
[{transition: "typed"}, "valid_transition"],
|
||||
];
|
||||
|
||||
let failTestData = [
|
||||
[{transition: "generated"}, "an invalid transition", "|generated| is not a supported transition for history"],
|
||||
[{visitTime: Date.now() + 1000000}, "a future date", "cannot be a future date"],
|
||||
[{url: "about.config"}, "an invalid url", "about.config is not a valid URL"],
|
||||
];
|
||||
|
||||
function* checkUrl(results) {
|
||||
ok(yield PlacesTestUtils.isPageInDB(results.details.url), `${results.details.url} found in history database`);
|
||||
ok(PlacesUtils.isValidGuid(results.result.id), "URL was added with a valid id");
|
||||
is(results.result.title, results.details.title, "URL was added with the correct title");
|
||||
if (results.details.visitTime) {
|
||||
is(results.result.lastVisitTime,
|
||||
Number(ExtensionUtils.normalizeTime(results.details.visitTime)),
|
||||
"URL was added with the correct date");
|
||||
}
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["history"],
|
||||
},
|
||||
background: `(${background})()`,
|
||||
});
|
||||
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
for (let data of addTestData) {
|
||||
extension.sendMessage("add-url", data);
|
||||
let results = yield extension.awaitMessage("url-added");
|
||||
yield checkUrl(results);
|
||||
}
|
||||
|
||||
for (let data of failTestData) {
|
||||
extension.sendMessage("expect-failure", data);
|
||||
yield extension.awaitMessage("add-failed");
|
||||
}
|
||||
|
||||
yield extension.unload();
|
||||
});
|
||||
|
|
|
@ -189,10 +189,10 @@ var gSecurityPane = {
|
|||
|
||||
let malware = malwareTable.value
|
||||
.split(",")
|
||||
.filter(x => x !== "goog-unwanted-simple" && x !== "test-unwanted-simple");
|
||||
.filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple");
|
||||
|
||||
if (blockUncommonUnwanted.checked) {
|
||||
malware.push("goog-unwanted-simple");
|
||||
malware.push("goog-unwanted-shavar");
|
||||
malware.push("test-unwanted-simple");
|
||||
}
|
||||
|
||||
|
|
|
@ -112,8 +112,8 @@ add_task(function*() {
|
|||
|
||||
// when the preference is on, the malware table should include these ids
|
||||
let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(",");
|
||||
is(malwareTable.includes("goog-unwanted-simple"), !checked,
|
||||
"malware table doesn't include goog-unwanted-simple");
|
||||
is(malwareTable.includes("goog-unwanted-shavar"), !checked,
|
||||
"malware table doesn't include goog-unwanted-shavar");
|
||||
is(malwareTable.includes("test-unwanted-simple"), !checked,
|
||||
"malware table doesn't include test-unwanted-simple");
|
||||
let sortedMalware = malwareTable.slice(0);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
tags = openwindow
|
||||
skip-if = buildapp == "mulet"
|
||||
support-files =
|
||||
browser_privatebrowsing_concurrent_page.html
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
}
|
||||
|
||||
@media (-moz-mac-yosemite-theme) {
|
||||
#navigator-toolbox::after {
|
||||
#navigator-toolbox:-moz-window-inactive::after {
|
||||
background-image: linear-gradient(to top, hsla(0,0%,0%,.1), hsla(0,0%,0%,.1) 1px, hsla(0,0%,100%,0) 1px, hsla(0,0%,100%,0) 2px, transparent 3px);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#filter substitution
|
||||
python
|
||||
import sys
|
||||
sys.path.append('@topsrcdir@/python/gdbpp')
|
||||
import gdbpp
|
||||
end
|
|
@ -60,6 +60,8 @@ if CONFIG['MOZ_DMD']:
|
|||
# Put a useful .gdbinit in the bin directory, to be picked up automatically
|
||||
# by GDB when we debug executables there.
|
||||
FINAL_TARGET_FILES += ['/.gdbinit']
|
||||
FINAL_TARGET_PP_FILES += ['.gdbinit_python.in']
|
||||
OBJDIR_FILES += ['!/dist/bin/.gdbinit_python']
|
||||
|
||||
# Install the clang-cl runtime library for ASAN next to the binaries we produce.
|
||||
if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']:
|
||||
|
|
|
@ -45,6 +45,8 @@ PrincipalOriginAttributes::InheritFromDocShellToDoc(const DocShellOriginAttribut
|
|||
// Bug 1225349 - PrincipalOriginAttributes should inherit mSignedPkg
|
||||
// accordingly by URI
|
||||
mSignedPkg = aAttrs.mSignedPkg;
|
||||
|
||||
mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -56,6 +58,8 @@ PrincipalOriginAttributes::InheritFromNecko(const NeckoOriginAttributes& aAttrs)
|
|||
// addonId is computed from the principal URI and never propagated
|
||||
mUserContextId = aAttrs.mUserContextId;
|
||||
mSignedPkg = aAttrs.mSignedPkg;
|
||||
|
||||
mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -71,6 +75,8 @@ DocShellOriginAttributes::InheritFromDocToChildDocShell(const PrincipalOriginAtt
|
|||
// Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
|
||||
// mSignedPkg accordingly by mSignedPkgInBrowser
|
||||
mSignedPkg = aAttrs.mSignedPkg;
|
||||
|
||||
mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -85,6 +91,8 @@ NeckoOriginAttributes::InheritFromDocToNecko(const PrincipalOriginAttributes& aA
|
|||
// TODO:
|
||||
// Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
|
||||
// mSignedPkg accordingly by mSignedPkgInBrowser
|
||||
|
||||
mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -99,6 +107,8 @@ NeckoOriginAttributes::InheritFromDocShellToNecko(const DocShellOriginAttributes
|
|||
// TODO:
|
||||
// Bug 1225353 - DocShell/NeckoOriginAttributes should inherit
|
||||
// mSignedPkg accordingly by mSignedPkgInBrowser
|
||||
|
||||
mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -145,6 +155,12 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const
|
|||
params->Set(NS_LITERAL_STRING("signedPkg"), mSignedPkg);
|
||||
}
|
||||
|
||||
if (mPrivateBrowsingId) {
|
||||
value.Truncate();
|
||||
value.AppendInt(mPrivateBrowsingId);
|
||||
params->Set(NS_LITERAL_STRING("privateBrowsingId"), value);
|
||||
}
|
||||
|
||||
aStr.Truncate();
|
||||
|
||||
params->Serialize(value);
|
||||
|
@ -171,6 +187,10 @@ public:
|
|||
: mOriginAttributes(aOriginAttributes)
|
||||
{
|
||||
MOZ_ASSERT(aOriginAttributes);
|
||||
// If mPrivateBrowsingId is passed in as >0 and is not present in the suffix,
|
||||
// then it will remain >0 when it should be 0 according to the suffix. Set to 0 before
|
||||
// iterating to fix this.
|
||||
mOriginAttributes->mPrivateBrowsingId = 0;
|
||||
}
|
||||
|
||||
bool URLParamsIterator(const nsString& aName,
|
||||
|
@ -217,6 +237,16 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
if (aName.EqualsLiteral("privateBrowsingId")) {
|
||||
nsresult rv;
|
||||
int64_t val = aValue.ToInteger64(&rv);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
NS_ENSURE_TRUE(val >= 0 && val <= UINT32_MAX, false);
|
||||
mOriginAttributes->mPrivateBrowsingId = static_cast<uint32_t>(val);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// No other attributes are supported.
|
||||
return false;
|
||||
}
|
||||
|
@ -262,6 +292,12 @@ OriginAttributes::PopulateFromOrigin(const nsACString& aOrigin,
|
|||
return PopulateFromSuffix(Substring(origin, pos));
|
||||
}
|
||||
|
||||
void
|
||||
OriginAttributes::SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing)
|
||||
{
|
||||
mPrivateBrowsingId = aInPrivateBrowsing ? 1 : 0;
|
||||
}
|
||||
|
||||
BasePrincipal::BasePrincipal()
|
||||
{}
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ public:
|
|||
mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser &&
|
||||
mAddonId == aOther.mAddonId &&
|
||||
mUserContextId == aOther.mUserContextId &&
|
||||
mSignedPkg == aOther.mSignedPkg;
|
||||
mSignedPkg == aOther.mSignedPkg &&
|
||||
mPrivateBrowsingId == aOther.mPrivateBrowsingId;
|
||||
}
|
||||
bool operator!=(const OriginAttributes& aOther) const
|
||||
{
|
||||
|
@ -51,6 +52,10 @@ public:
|
|||
bool PopulateFromOrigin(const nsACString& aOrigin,
|
||||
nsACString& aOriginNoSuffix);
|
||||
|
||||
// Helper function to match mIsPrivateBrowsing to existing private browsing
|
||||
// flags. Once all other flags are removed, this can be removed too.
|
||||
void SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing);
|
||||
|
||||
protected:
|
||||
OriginAttributes() {}
|
||||
explicit OriginAttributes(const OriginAttributesDictionary& aOther)
|
||||
|
@ -175,6 +180,10 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mPrivateBrowsingId.WasPassed() && mPrivateBrowsingId.Value() != aAttrs.mPrivateBrowsingId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -674,37 +674,32 @@ EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase)
|
|||
static bool
|
||||
AllSchemesMatch(nsIURI* aURI, nsIURI* aOtherURI)
|
||||
{
|
||||
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
|
||||
nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(aOtherURI);
|
||||
auto stringComparator = nsCaseInsensitiveCStringComparator();
|
||||
if (!nestedURI && !nestedOtherURI) {
|
||||
// Neither of the URIs is nested, compare their schemes directly:
|
||||
nsAutoCString scheme, otherScheme;
|
||||
aURI->GetScheme(scheme);
|
||||
aOtherURI->GetScheme(otherScheme);
|
||||
return scheme.Equals(otherScheme, stringComparator);
|
||||
}
|
||||
while (nestedURI && nestedOtherURI) {
|
||||
nsCOMPtr<nsIURI> currentURI = do_QueryInterface(nestedURI);
|
||||
nsCOMPtr<nsIURI> currentOtherURI = do_QueryInterface(nestedOtherURI);
|
||||
nsCOMPtr<nsIURI> currentURI = aURI;
|
||||
nsCOMPtr<nsIURI> currentOtherURI = aOtherURI;
|
||||
while (currentURI && currentOtherURI) {
|
||||
nsAutoCString scheme, otherScheme;
|
||||
currentURI->GetScheme(scheme);
|
||||
currentOtherURI->GetScheme(otherScheme);
|
||||
if (!scheme.Equals(otherScheme, stringComparator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
|
||||
nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
|
||||
// If neither are nested and all schemes have matched so far
|
||||
// (or we would have bailed already), we're the same:
|
||||
if (!nestedURI && !nestedOtherURI) {
|
||||
return true;
|
||||
}
|
||||
// If one is nested and the other not, they're not equal:
|
||||
if (!nestedURI != !nestedOtherURI) {
|
||||
return false;
|
||||
}
|
||||
// At this stage, both are still nested URIs, so let's play again:
|
||||
nestedURI->GetInnerURI(getter_AddRefs(currentURI));
|
||||
nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI));
|
||||
nestedURI = do_QueryInterface(currentURI);
|
||||
nestedOtherURI = do_QueryInterface(currentOtherURI);
|
||||
}
|
||||
if (!!nestedURI != !!nestedOtherURI) {
|
||||
// If only one of the scheme chains runs out at one point, clearly the chains
|
||||
// aren't of the same length, so we bail:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -27,7 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=995943
|
|||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestCompleteLog();
|
||||
if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
|
||||
SimpleTest.expectAssertions(5); // See bug 1067022
|
||||
SimpleTest.expectAssertions(6, 9); // See bug 1067022
|
||||
else if (Services.appinfo.OS == "WINNT")
|
||||
SimpleTest.expectAssertions(0, 1); // See bug 1067022
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=758258
|
|||
|
||||
var Ci = Components.interfaces;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1) {
|
||||
SimpleTest.expectAssertions(0, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* gData is an array of objects. Each object represents a test case.
|
||||
|
|
|
@ -53,7 +53,7 @@ LoadContext::LoadContext(nsIPrincipal* aPrincipal,
|
|||
{
|
||||
PrincipalOriginAttributes poa = BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
|
||||
mOriginAttributes.InheritFromDocToChildDocShell(poa);
|
||||
|
||||
mOriginAttributes.SyncAttributesWithPrivateBrowsing(mUsePrivateBrowsing);
|
||||
if (!aOptionalBase) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
, mIsNotNull(aToCopy.mIsNotNull)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(aToCopy.mUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId != 0));
|
||||
}
|
||||
|
||||
// appId/inIsolatedMozBrowser arguments override those in SerializedLoadContext
|
||||
|
@ -71,6 +72,7 @@ public:
|
|||
, mIsNotNull(aToCopy.mIsNotNull)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(aToCopy.mUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId != 0));
|
||||
}
|
||||
|
||||
LoadContext(dom::Element* aTopFrameElement,
|
||||
|
@ -88,6 +90,7 @@ public:
|
|||
, mIsNotNull(true)
|
||||
#endif
|
||||
{
|
||||
MOZ_ASSERT(aUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId != 0));
|
||||
}
|
||||
|
||||
// Constructor taking reserved appId for the safebrowsing cookie.
|
||||
|
|
|
@ -41,6 +41,7 @@ SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
|
|||
mUsePrivateBrowsing = isPrivate;
|
||||
mIsPrivateBitValid = true;
|
||||
}
|
||||
mOriginAttributes.SyncAttributesWithPrivateBrowsing(mUsePrivateBrowsing);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +62,7 @@ SerializedLoadContext::Init(nsILoadContext* aLoadContext)
|
|||
mIsPrivateBitValid = true;
|
||||
aLoadContext->GetIsContent(&mIsContent);
|
||||
aLoadContext->GetUsePrivateBrowsing(&mUsePrivateBrowsing);
|
||||
mOriginAttributes.SyncAttributesWithPrivateBrowsing(mUsePrivateBrowsing);
|
||||
aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
|
||||
if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) {
|
||||
NS_WARNING("GetOriginAttributes failed");
|
||||
|
|
|
@ -814,6 +814,7 @@ nsDocShell::nsDocShell()
|
|||
, mParentCharsetSource(0)
|
||||
, mJSRunToCompletionDepth(0)
|
||||
{
|
||||
AssertOriginAttributesMatchPrivateBrowsing();
|
||||
mHistoryID = ++gDocshellIDCounter;
|
||||
if (gDocShellCount++ == 0) {
|
||||
NS_ASSERTION(sURIFixup == nullptr,
|
||||
|
@ -2182,7 +2183,7 @@ NS_IMETHODIMP
|
|||
nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
|
||||
|
||||
AssertOriginAttributesMatchPrivateBrowsing();
|
||||
*aUsePrivateBrowsing = mInPrivateBrowsing;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2205,6 +2206,9 @@ nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing)
|
|||
bool changed = aUsePrivateBrowsing != mInPrivateBrowsing;
|
||||
if (changed) {
|
||||
mInPrivateBrowsing = aUsePrivateBrowsing;
|
||||
|
||||
mOriginAttributes.SyncAttributesWithPrivateBrowsing(mInPrivateBrowsing);
|
||||
|
||||
if (mAffectPrivateSessionLifetime) {
|
||||
if (aUsePrivateBrowsing) {
|
||||
IncreasePrivateDocShellCount();
|
||||
|
@ -2274,6 +2278,7 @@ nsDocShell::SetAffectPrivateSessionLifetime(bool aAffectLifetime)
|
|||
{
|
||||
bool change = aAffectLifetime != mAffectPrivateSessionLifetime;
|
||||
if (change && mInPrivateBrowsing) {
|
||||
AssertOriginAttributesMatchPrivateBrowsing();
|
||||
if (aAffectLifetime) {
|
||||
IncreasePrivateDocShellCount();
|
||||
} else {
|
||||
|
@ -2966,6 +2971,7 @@ nsDocShell::GetSessionStorageForPrincipal(nsIPrincipal* aPrincipal,
|
|||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow();
|
||||
|
||||
AssertOriginAttributesMatchPrivateBrowsing();
|
||||
if (aCreate) {
|
||||
return manager->CreateStorage(domWin->GetCurrentInnerWindow(), aPrincipal,
|
||||
aDocumentURI, mInPrivateBrowsing, aStorage);
|
||||
|
@ -3660,6 +3666,11 @@ nsDocShell::FindItemWithName(const char16_t* aName,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocShell::AssertOriginAttributesMatchPrivateBrowsing(){
|
||||
MOZ_ASSERT((mOriginAttributes.mPrivateBrowsingId != 0) == mInPrivateBrowsing);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::DoFindItemWithName(const char16_t* aName,
|
||||
nsISupports* aRequestor,
|
||||
|
@ -5763,6 +5774,7 @@ nsDocShell::Destroy()
|
|||
|
||||
if (mInPrivateBrowsing) {
|
||||
mInPrivateBrowsing = false;
|
||||
mOriginAttributes.SyncAttributesWithPrivateBrowsing(mInPrivateBrowsing);
|
||||
if (mAffectPrivateSessionLifetime) {
|
||||
DecreasePrivateDocShellCount();
|
||||
}
|
||||
|
@ -6420,6 +6432,7 @@ nsDocShell::SetTitle(const char16_t* aTitle)
|
|||
}
|
||||
}
|
||||
|
||||
AssertOriginAttributesMatchPrivateBrowsing();
|
||||
if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
|
||||
!mInPrivateBrowsing) {
|
||||
nsCOMPtr<IHistory> history = services::GetHistoryService();
|
||||
|
|
|
@ -1035,6 +1035,10 @@ private:
|
|||
nsIDocShellTreeItem* aOriginalRequestor,
|
||||
nsIDocShellTreeItem** aResult);
|
||||
|
||||
// Helper assertion to enforce that mInPrivateBrowsing is in sync with
|
||||
// OriginAttributes.mPrivateBrowsingId
|
||||
void AssertOriginAttributesMatchPrivateBrowsing();
|
||||
|
||||
// Notify consumers of a search being loaded through the observer service:
|
||||
void MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
|
||||
const nsString& aKeyword);
|
||||
|
|
|
@ -9,11 +9,15 @@ support-files =
|
|||
|
||||
[test_child_navigation_by_location.html]
|
||||
[test_other_auxiliary_navigation_by_location.html]
|
||||
tags = openwindow
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
|
||||
[test_our_auxiliary_navigation_by_location.html]
|
||||
tags = openwindow
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
|
||||
[test_parent_navigation_by_location.html]
|
||||
tags = openwindow
|
||||
[test_sibling_navigation_by_location.html]
|
||||
tags = openwindow
|
||||
[test_top_navigation_by_location_exotic.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || android_version == '18' #bug 948948, NS_ERROR_FAILURE from nsWindowWatcher::GetPrompt
|
||||
[test_top_navigation_by_location.html]
|
||||
|
|
|
@ -9,3 +9,4 @@ support-files =
|
|||
|
||||
[browser_bug343515.js]
|
||||
[browser_test-content-chromeflags.js]
|
||||
tags = openwindow
|
|
@ -179,7 +179,8 @@ ChromeUtils::IsOriginAttributesEqual(dom::GlobalObject& aGlobal,
|
|||
aA.mAppId == aB.mAppId &&
|
||||
aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
|
||||
aA.mSignedPkg == aB.mSignedPkg &&
|
||||
aA.mUserContextId == aB.mUserContextId;
|
||||
aA.mUserContextId == aB.mUserContextId &&
|
||||
aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "mozilla/dom/WakeLock.h"
|
||||
#include "mozilla/dom/power/PowerManagerService.h"
|
||||
#include "mozilla/dom/CellBroadcast.h"
|
||||
#include "mozilla/dom/FlyWebService.h"
|
||||
#include "mozilla/dom/IccManager.h"
|
||||
#include "mozilla/dom/InputPortManager.h"
|
||||
#include "mozilla/dom/MobileMessageManager.h"
|
||||
|
@ -1620,6 +1621,20 @@ Navigator::GetDeprecatedBattery(ErrorResult& aRv)
|
|||
return mBatteryManager;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Navigator::PublishServer(const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<FlyWebService> service = FlyWebService::GetOrCreate();
|
||||
if (!service) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return service->PublishServer(aName, aOptions, mWindow, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Navigator::GetFeature(const nsAString& aName, ErrorResult& aRv)
|
||||
{
|
||||
|
|
|
@ -43,6 +43,8 @@ class ArrayBufferViewOrBlobOrStringOrFormData;
|
|||
struct MobileIdOptions;
|
||||
class ServiceWorkerContainer;
|
||||
class DOMRequest;
|
||||
struct FlyWebPublishOptions;
|
||||
struct FlyWebFilter;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -167,6 +169,9 @@ public:
|
|||
Promise* GetBattery(ErrorResult& aRv);
|
||||
battery::BatteryManager* GetDeprecatedBattery(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> PublishServer(const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
static void AppName(nsAString& aAppName, bool aUsePrefOverriddenValue);
|
||||
|
||||
static nsresult GetPlatform(nsAString& aPlatform,
|
||||
|
|
|
@ -294,21 +294,6 @@ GetWebIDLCallerPrincipal()
|
|||
return aes->mWebIDLCallerPrincipal;
|
||||
}
|
||||
|
||||
static JSContext*
|
||||
FindJSContext(nsIGlobalObject* aGlobalObject)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
JSContext *cx = nullptr;
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject);
|
||||
if (sgo && sgo->GetScriptContext()) {
|
||||
cx = sgo->GetScriptContext()->GetNativeContext();
|
||||
}
|
||||
if (!cx) {
|
||||
cx = nsContentUtils::GetSafeJSContext();
|
||||
}
|
||||
return cx;
|
||||
}
|
||||
|
||||
AutoJSAPI::AutoJSAPI()
|
||||
: mCx(nullptr)
|
||||
, mOldAutoJSAPIOwnsErrorReporting(false)
|
||||
|
@ -344,16 +329,20 @@ WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage,
|
|||
JSErrorReport* aRep);
|
||||
|
||||
void
|
||||
AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
|
||||
AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
|
||||
JSContext* aCx, bool aIsMainThread)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
|
||||
MOZ_ASSERT(bool(aGlobalObject) == bool(aGlobal));
|
||||
MOZ_ASSERT_IF(aGlobalObject, aGlobalObject->GetGlobalJSObject() == aGlobal);
|
||||
#ifdef DEBUG
|
||||
bool haveException = JS_IsExceptionPending(aCx);
|
||||
#endif // DEBUG
|
||||
|
||||
mCx = aCx;
|
||||
mIsMainThread = aIsMainThread;
|
||||
mGlobalObject = aGlobalObject;
|
||||
if (aIsMainThread) {
|
||||
// This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher
|
||||
// can GC, which is only possible because XPCJSContextStack::Push calls
|
||||
|
@ -451,7 +440,8 @@ AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
|
|||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
|
||||
|
||||
InitInternal(aGlobalObject->GetGlobalJSObject(), aCx, aIsMainThread);
|
||||
InitInternal(aGlobalObject, aGlobalObject->GetGlobalJSObject(), aCx,
|
||||
aIsMainThread);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -459,7 +449,7 @@ AutoJSAPI::Init()
|
|||
{
|
||||
MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once");
|
||||
|
||||
InitInternal(/* aGlobal */ nullptr,
|
||||
InitInternal(/* aGlobalObject */ nullptr, /* aGlobal */ nullptr,
|
||||
nsContentUtils::GetDefaultJSContextForThread(),
|
||||
NS_IsMainThread());
|
||||
}
|
||||
|
@ -479,7 +469,7 @@ AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx)
|
|||
return false;
|
||||
}
|
||||
|
||||
InitInternal(global, aCx, NS_IsMainThread());
|
||||
InitInternal(aGlobalObject, global, aCx, NS_IsMainThread());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -651,13 +641,13 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
|
|||
bool aIsMainThread,
|
||||
JSContext* aCx)
|
||||
: AutoJSAPI(aGlobalObject, aIsMainThread,
|
||||
aCx ? aCx : FindJSContext(aGlobalObject))
|
||||
aCx ? aCx : nsContentUtils::GetSafeJSContext())
|
||||
, ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
|
||||
, mWebIDLCallerPrincipal(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aGlobalObject);
|
||||
MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
|
||||
MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
|
||||
MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == nsContentUtils::GetSafeJSContext());
|
||||
|
||||
if (aIsMainThread && gRunToCompletionListeners > 0) {
|
||||
mDocShellEntryMonitor.emplace(cx(), aReason);
|
||||
|
@ -774,12 +764,6 @@ danger::AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull)
|
|||
{
|
||||
MOZ_ASSERT_IF(!allowNull, cx);
|
||||
|
||||
// Hold a strong ref to the nsIScriptContext, if any. This ensures that we
|
||||
// only destroy the mContext of an nsJSContext when it is not on the cx stack
|
||||
// (and therefore not in use). See nsJSContext::DestroyJSContext().
|
||||
if (cx)
|
||||
mScx = GetScriptContextFromJSContext(cx);
|
||||
|
||||
XPCJSContextStack *stack = XPCJSRuntime::Get()->GetJSContextStack();
|
||||
stack->Push(cx);
|
||||
mStackDepthAfterPush = stack->Count();
|
||||
|
@ -811,7 +795,6 @@ danger::AutoCxPusher::~AutoCxPusher()
|
|||
DebugOnly<JSContext*> stackTop;
|
||||
MOZ_ASSERT(mPushedContext == nsXPConnect::XPConnect()->GetCurrentJSContext());
|
||||
XPCJSRuntime::Get()->GetJSContextStack()->Pop();
|
||||
mScx = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -40,15 +40,12 @@ public:
|
|||
explicit AutoCxPusher(JSContext *aCx, bool aAllowNull = false);
|
||||
~AutoCxPusher();
|
||||
|
||||
nsIScriptContext* GetScriptContext() { return mScx; }
|
||||
|
||||
// Returns true if this AutoCxPusher performed the push that is currently at
|
||||
// the top of the cx stack.
|
||||
bool IsStackTop() const;
|
||||
|
||||
private:
|
||||
mozilla::Maybe<JSAutoRequest> mAutoRequest;
|
||||
nsCOMPtr<nsIScriptContext> mScx;
|
||||
uint32_t mStackDepthAfterPush;
|
||||
#ifdef DEBUG
|
||||
JSContext* mPushedContext;
|
||||
|
@ -305,6 +302,11 @@ protected:
|
|||
AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx);
|
||||
|
||||
private:
|
||||
// We need to hold a strong ref to our global object, so it won't go away
|
||||
// while we're being used. This _could_ become a JS::Rooted<JSObject*> if we
|
||||
// grabbed our JSContext in our constructor instead of waiting for Init(), so
|
||||
// we could construct this at that point. It might be worth it do to that.
|
||||
RefPtr<nsIGlobalObject> mGlobalObject;
|
||||
mozilla::Maybe<danger::AutoCxPusher> mCxPusher;
|
||||
mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
|
||||
JSContext *mCx;
|
||||
|
@ -315,7 +317,8 @@ private:
|
|||
bool mIsMainThread;
|
||||
Maybe<JSErrorReporter> mOldErrorReporter;
|
||||
|
||||
void InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread);
|
||||
void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
|
||||
JSContext* aCx, bool aIsMainThread);
|
||||
|
||||
AutoJSAPI(const AutoJSAPI&) = delete;
|
||||
AutoJSAPI& operator= (const AutoJSAPI&) = delete;
|
||||
|
|
|
@ -12,24 +12,6 @@
|
|||
|
||||
class nsIJSArgArray;
|
||||
|
||||
// seems like overkill for just this 1 function - but let's see what else
|
||||
// falls out first.
|
||||
inline nsIScriptContext *
|
||||
GetScriptContextFromJSContext(JSContext *cx)
|
||||
{
|
||||
if (!(JS::ContextOptionsRef(cx).privateIsNSISupports())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIScriptContext> scx =
|
||||
do_QueryInterface(static_cast<nsISupports *>
|
||||
(::JS_GetContextPrivate(cx)));
|
||||
|
||||
// This will return a pointer to something that's about to be
|
||||
// released, but that's ok here.
|
||||
return scx;
|
||||
}
|
||||
|
||||
// A factory function for turning a JS::Value argv into an nsIArray
|
||||
// but also supports an effecient way of extracting the original argv.
|
||||
// The resulting object will take a copy of the array, and ensure each
|
||||
|
|
|
@ -629,15 +629,11 @@ nsDOMMutationObserver::Observe(nsINode& aTarget,
|
|||
bool attributeOldValue =
|
||||
aOptions.mAttributeOldValue.WasPassed() &&
|
||||
aOptions.mAttributeOldValue.Value();
|
||||
bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList &&
|
||||
nsContentUtils::ThreadsafeIsCallerChrome();
|
||||
bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList;
|
||||
bool characterDataOldValue =
|
||||
aOptions.mCharacterDataOldValue.WasPassed() &&
|
||||
aOptions.mCharacterDataOldValue.Value();
|
||||
bool animations =
|
||||
aOptions.mAnimations.WasPassed() &&
|
||||
aOptions.mAnimations.Value() &&
|
||||
nsContentUtils::ThreadsafeIsCallerChrome();
|
||||
bool animations = aOptions.mAnimations;
|
||||
|
||||
if (!aOptions.mAttributes.WasPassed() &&
|
||||
(aOptions.mAttributeOldValue.WasPassed() ||
|
||||
|
@ -763,7 +759,7 @@ nsDOMMutationObserver::GetObservingInfo(
|
|||
info.mAttributeOldValue.Construct(mr->AttributeOldValue());
|
||||
info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
|
||||
info.mNativeAnonymousChildList = mr->NativeAnonymousChildList();
|
||||
info.mAnimations.Construct(mr->Animations());
|
||||
info.mAnimations = mr->Animations();
|
||||
nsCOMArray<nsIAtom>& filters = mr->AttributeFilter();
|
||||
if (filters.Count()) {
|
||||
info.mAttributeFilter.Construct();
|
||||
|
|
|
@ -164,6 +164,7 @@
|
|||
#include "mozilla/dom/HTMLIFrameElement.h"
|
||||
#include "mozilla/dom/HTMLImageElement.h"
|
||||
#include "mozilla/dom/MediaSource.h"
|
||||
#include "mozilla/dom/FlyWebService.h"
|
||||
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
@ -8988,6 +8989,13 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Don't save presentation if there are active FlyWeb connections or FlyWeb
|
||||
// servers.
|
||||
FlyWebService* flyWebService = FlyWebService::GetExisting();
|
||||
if (flyWebService && flyWebService->HasConnectionOrServer(win->WindowID())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mSubDocuments) {
|
||||
for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
|
||||
auto entry = static_cast<SubDocMapEntry*>(iter.Get());
|
||||
|
|
|
@ -2146,6 +2146,16 @@ nsFrameLoader::MaybeCreateDocShell()
|
|||
return rv;
|
||||
}
|
||||
|
||||
bool isPrivate = false;
|
||||
nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell);
|
||||
NS_ENSURE_STATE(parentContext);
|
||||
|
||||
rv = parentContext->GetUsePrivateBrowsing(&isPrivate);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
|
||||
|
||||
nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
|
||||
|
||||
if (OwnerIsMozBrowserOrAppFrame()) {
|
||||
|
@ -3391,6 +3401,9 @@ nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
|
|||
nsGkAtoms::mozpresentation,
|
||||
presentationURLStr);
|
||||
|
||||
bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
|
||||
attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
|
||||
|
||||
bool tabContextUpdated =
|
||||
aTabContext->SetTabContext(OwnerIsMozBrowserFrame(),
|
||||
mIsPrerendered,
|
||||
|
|
|
@ -226,6 +226,7 @@
|
|||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/NavigatorBinding.h"
|
||||
#include "mozilla/dom/ImageBitmap.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistration.h"
|
||||
#include "mozilla/dom/U2F.h"
|
||||
#include "mozilla/dom/WebIDLGlobalNameHash.h"
|
||||
|
@ -1093,7 +1094,9 @@ nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
|
|||
return false;
|
||||
}
|
||||
for (int32_t i = 0; i < int32_t(length); ++i) {
|
||||
props.append(INT_TO_JSID(i));
|
||||
if (!props.append(INT_TO_JSID(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2240,7 +2243,7 @@ public:
|
|||
NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID)
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
WindowStateHolder(nsIScriptContext* aContext, nsGlobalWindow *aWindow);
|
||||
explicit WindowStateHolder(nsGlobalWindow *aWindow);
|
||||
|
||||
nsGlobalWindow* GetInnerWindow() { return mInnerWindow; }
|
||||
|
||||
|
@ -2261,10 +2264,9 @@ protected:
|
|||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID)
|
||||
|
||||
WindowStateHolder::WindowStateHolder(nsIScriptContext* aContext,
|
||||
nsGlobalWindow* aWindow)
|
||||
WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow)
|
||||
: mInnerWindow(aWindow),
|
||||
mInnerWindowReflector(aContext->GetNativeContext(), aWindow->GetWrapper())
|
||||
mInnerWindowReflector(nsContentUtils::RootingCx(), aWindow->GetWrapper())
|
||||
{
|
||||
NS_PRECONDITION(aWindow, "null window");
|
||||
NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
|
||||
|
@ -2331,7 +2333,7 @@ InitializeLegacyNetscapeObject(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
|
|||
// uses enablePrivilege. If you're not doing test automation, you _must_ not
|
||||
// flip this pref, or you will be exposing all your users to security
|
||||
// vulnerabilities.
|
||||
if (!Preferences::GetBool("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer")) {
|
||||
if (!xpc::IsInAutomation()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6309,7 +6311,7 @@ FullscreenTransitionTask::Run()
|
|||
// more than exposing an intermediate state.
|
||||
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
uint32_t timeout =
|
||||
Preferences::GetUint("full-screen-api.transition.timeout", 500);
|
||||
Preferences::GetUint("full-screen-api.transition.timeout", 1000);
|
||||
mTimer->Init(observer, timeout, nsITimer::TYPE_ONE_SHOT);
|
||||
} else if (stage == eAfterToggle) {
|
||||
Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
|
||||
|
@ -12269,7 +12271,7 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
|
|||
// New script entry point required, due to the "Create a script" sub-step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
|
||||
nsAutoMicroTask mt;
|
||||
AutoEntryScript aes(this, reason, true, aScx->GetNativeContext());
|
||||
AutoEntryScript aes(this, reason, true);
|
||||
JS::CompileOptions options(aes.cx());
|
||||
options.setFileAndLine(filename, lineNo)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
|
@ -12946,7 +12948,7 @@ nsGlobalWindow::SaveWindowState()
|
|||
// to the page.
|
||||
inner->Freeze();
|
||||
|
||||
nsCOMPtr<nsISupports> state = new WindowStateHolder(mContext, inner);
|
||||
nsCOMPtr<nsISupports> state = new WindowStateHolder(inner);
|
||||
|
||||
#ifdef DEBUG_PAGE_CACHE
|
||||
printf("saving window state, state = %p\n", (void*)state);
|
||||
|
@ -14360,6 +14362,11 @@ already_AddRefed<Promise>
|
|||
nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ImageBitmap::Create(this, aImage, Nothing(), aRv);
|
||||
}
|
||||
|
||||
|
@ -14368,9 +14375,30 @@ nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
|
|||
int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ImageBitmap::Create(this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise>
|
||||
nsGlobalWindow::CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
int32_t aOffset, int32_t aLength,
|
||||
ImageBitmapFormat aFormat,
|
||||
const Sequence<ChannelPixelLayout>& aLayout,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
|
||||
return ImageBitmap::Create(this, aImage, aOffset, aLength, aFormat, aLayout,
|
||||
aRv);
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_TYPE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper called by methods that move/resize the window,
|
||||
// to ensure the presContext (if any) is aware of resolution
|
||||
// change that may happen in multi-monitor configuration.
|
||||
|
|
|
@ -103,11 +103,13 @@ namespace mozilla {
|
|||
class DOMEventTargetHelper;
|
||||
namespace dom {
|
||||
class BarProp;
|
||||
struct ChannelPixelLayout;
|
||||
class Console;
|
||||
class Crypto;
|
||||
class External;
|
||||
class Function;
|
||||
class Gamepad;
|
||||
enum class ImageBitmapFormat : uint32_t;
|
||||
class MediaQueryList;
|
||||
class MozSelfSupport;
|
||||
class Navigator;
|
||||
|
@ -1203,6 +1205,14 @@ public:
|
|||
int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise>
|
||||
CreateImageBitmap(const mozilla::dom::ImageBitmapSource& aImage,
|
||||
int32_t aOffset, int32_t aLength,
|
||||
mozilla::dom::ImageBitmapFormat aFormat,
|
||||
const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
|
||||
// ChromeWindow bits. Do NOT call these unless your window is in
|
||||
// fact an nsGlobalChromeWindow.
|
||||
uint16_t WindowState();
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
class nsIScriptGlobalObject;
|
||||
|
||||
#define NS_ISCRIPTCONTEXT_IID \
|
||||
{ 0x901f0d5e, 0x217a, 0x45fa, \
|
||||
{ 0x9a, 0xca, 0x45, 0x0f, 0xe7, 0x2f, 0x10, 0x9a } }
|
||||
{ 0x54cbe9cf, 0x7282, 0x421a, \
|
||||
{ 0x91, 0x6f, 0xd0, 0x70, 0x73, 0xde, 0xb8, 0xc0 } }
|
||||
|
||||
class nsIOffThreadScriptReceiver;
|
||||
|
||||
|
@ -37,12 +37,6 @@ public:
|
|||
**/
|
||||
virtual nsIScriptGlobalObject *GetGlobalObject() = 0;
|
||||
|
||||
/**
|
||||
* Return the native script context
|
||||
*
|
||||
**/
|
||||
virtual JSContext* GetNativeContext() = 0;
|
||||
|
||||
/**
|
||||
* Initialize the context generally. Does not create a global object.
|
||||
**/
|
||||
|
|
|
@ -574,27 +574,6 @@ DumpString(const nsAString &str)
|
|||
#define JS_OPTIONS_DOT_STR "javascript.options."
|
||||
|
||||
static const char js_options_dot_str[] = JS_OPTIONS_DOT_STR;
|
||||
#ifdef JS_GC_ZEAL
|
||||
static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
|
||||
static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
|
||||
#endif
|
||||
static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
|
||||
static const char js_memnotify_option_str[] = JS_OPTIONS_DOT_STR "mem.notify";
|
||||
|
||||
void
|
||||
nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
|
||||
{
|
||||
sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
|
||||
sPostGCEventsToObserver = Preferences::GetBool(js_memnotify_option_str);
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
|
||||
int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
|
||||
int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);
|
||||
if (zeal >= 0)
|
||||
::JS_SetGCZeal(context->mContext, (uint8_t)zeal, frequency);
|
||||
#endif
|
||||
}
|
||||
|
||||
nsJSContext::nsJSContext(bool aGCOnDestruction,
|
||||
nsIScriptGlobalObject* aGlobalObject)
|
||||
|
@ -606,17 +585,6 @@ nsJSContext::nsJSContext(bool aGCOnDestruction,
|
|||
|
||||
++sContextCount;
|
||||
|
||||
mContext = ::JS_NewContext(sRuntime, gStackSize);
|
||||
if (mContext) {
|
||||
::JS_SetContextPrivate(mContext, static_cast<nsIScriptContext *>(this));
|
||||
|
||||
// Make sure the new context gets the default context options
|
||||
JS::ContextOptionsRef(mContext).setPrivateIsNSISupports(true);
|
||||
|
||||
// Watch for the JS boolean options
|
||||
Preferences::RegisterCallback(JSOptionChangedCallback,
|
||||
js_options_dot_str, this);
|
||||
}
|
||||
mIsInitialized = false;
|
||||
mProcessingScriptTag = false;
|
||||
HoldJSObjects(this);
|
||||
|
@ -626,7 +594,7 @@ nsJSContext::~nsJSContext()
|
|||
{
|
||||
mGlobalObjectRef = nullptr;
|
||||
|
||||
DestroyJSContext();
|
||||
Destroy();
|
||||
|
||||
--sContextCount;
|
||||
|
||||
|
@ -638,31 +606,13 @@ nsJSContext::~nsJSContext()
|
|||
}
|
||||
}
|
||||
|
||||
// This function is called either by the destructor or unlink, which means that
|
||||
// it can never be called when there is an outstanding ref to the
|
||||
// nsIScriptContext on the stack. Our stack-scoped cx pushers hold such a ref,
|
||||
// so we can assume here that mContext is not on the stack (and therefore not
|
||||
// in use).
|
||||
void
|
||||
nsJSContext::DestroyJSContext()
|
||||
nsJSContext::Destroy()
|
||||
{
|
||||
if (!mContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear our entry in the JSContext, bugzilla bug 66413
|
||||
::JS_SetContextPrivate(mContext, nullptr);
|
||||
|
||||
// Unregister our "javascript.options.*" pref-changed callback.
|
||||
Preferences::UnregisterCallback(JSOptionChangedCallback,
|
||||
js_options_dot_str, this);
|
||||
|
||||
if (mGCOnDestruction) {
|
||||
PokeGC(JS::gcreason::NSJSCONTEXT_DESTROY);
|
||||
}
|
||||
|
||||
JS_DestroyContextNoGC(mContext);
|
||||
mContext = nullptr;
|
||||
DropJSObjects(this);
|
||||
}
|
||||
|
||||
|
@ -674,16 +624,13 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext)
|
||||
NS_ASSERTION(!tmp->mContext || !js::ContextHasOutstandingRequests(tmp->mContext),
|
||||
"Trying to unlink a context with outstanding requests.");
|
||||
tmp->mIsInitialized = false;
|
||||
tmp->mGCOnDestruction = false;
|
||||
tmp->mWindowProxy = nullptr;
|
||||
tmp->DestroyJSContext();
|
||||
tmp->Destroy();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt())
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
@ -697,21 +644,6 @@ NS_INTERFACE_MAP_END
|
|||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSContext)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSContext)
|
||||
|
||||
nsrefcnt
|
||||
nsJSContext::GetCCRefcnt()
|
||||
{
|
||||
nsrefcnt refcnt = mRefCnt.get();
|
||||
|
||||
// In the (abnormal) case of synchronous cycle-collection, the context may be
|
||||
// actively running JS code in which case we must keep it alive by adding an
|
||||
// extra refcount.
|
||||
if (mContext && js::ContextHasOutstandingRequests(mContext)) {
|
||||
refcnt++;
|
||||
}
|
||||
|
||||
return refcnt;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
AtomIsEventHandlerName(nsIAtom *aName)
|
||||
|
@ -744,12 +676,6 @@ nsJSContext::GetGlobalObject()
|
|||
return mGlobalObjectRef;
|
||||
}
|
||||
|
||||
JSContext*
|
||||
nsJSContext::GetNativeContext()
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSContext::InitContext()
|
||||
{
|
||||
|
@ -757,11 +683,7 @@ nsJSContext::InitContext()
|
|||
// WillInitializeContext/DidInitializeContext around this call.
|
||||
NS_ENSURE_TRUE(!mIsInitialized, NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
if (!mContext)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
JSOptionChangedCallback(js_options_dot_str, this);
|
||||
|
||||
// XXXbz Is there still a point to this function?
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1197,7 +1119,6 @@ static const JSFunctionSpec JProfFunctions[] = {
|
|||
nsresult
|
||||
nsJSContext::InitClasses(JS::Handle<JSObject*> aGlobalObj)
|
||||
{
|
||||
JSOptionChangedCallback(js_options_dot_str, this);
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
@ -2623,6 +2544,11 @@ nsJSContext::EnsureStatics()
|
|||
"javascript.options.compact_on_user_inactive_delay",
|
||||
NS_DEAULT_INACTIVE_GC_DELAY);
|
||||
|
||||
Preferences::AddBoolVarCache(&sPostGCEventsToConsole,
|
||||
JS_OPTIONS_DOT_STR "mem.log");
|
||||
Preferences::AddBoolVarCache(&sPostGCEventsToObserver,
|
||||
JS_OPTIONS_DOT_STR "mem.notify");
|
||||
|
||||
nsIObserver* observer = new nsJSEnvironmentObserver();
|
||||
obs->AddObserver(observer, "memory-pressure", false);
|
||||
obs->AddObserver(observer, "user-interaction-inactive", false);
|
||||
|
|
|
@ -50,7 +50,6 @@ public:
|
|||
virtual nsIScriptGlobalObject *GetGlobalObject() override;
|
||||
inline nsIScriptGlobalObject *GetGlobalObjectRef() { return mGlobalObjectRef; }
|
||||
|
||||
virtual JSContext* GetNativeContext() override;
|
||||
virtual nsresult InitContext() override;
|
||||
virtual bool IsContextInitialized() override;
|
||||
|
||||
|
@ -150,11 +149,8 @@ protected:
|
|||
nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv);
|
||||
|
||||
private:
|
||||
void DestroyJSContext();
|
||||
void Destroy();
|
||||
|
||||
nsrefcnt GetCCRefcnt();
|
||||
|
||||
JSContext *mContext;
|
||||
JS::Heap<JSObject*> mWindowProxy;
|
||||
|
||||
bool mIsInitialized;
|
||||
|
@ -168,8 +164,6 @@ private:
|
|||
// context does. It is eventually collected by the cycle collector.
|
||||
nsCOMPtr<nsIScriptGlobalObject> mGlobalObjectRef;
|
||||
|
||||
static void JSOptionChangedCallback(const char *pref, void *data);
|
||||
|
||||
static bool DOMOperationCallback(JSContext *cx);
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
@ -102,8 +101,6 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
|||
MOZ_ASSERT_IF(aScopeChain.length() != 0,
|
||||
js::IsObjectInContextCompartment(aScopeChain[0], cx));
|
||||
MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
|
||||
mozilla::DebugOnly<nsIScriptContext*> ctx = GetScriptContextFromJSContext(cx);
|
||||
MOZ_ASSERT_IF(ctx, ctx->IsContextInitialized());
|
||||
|
||||
// Do the junk Gecko is supposed to do before calling into JSAPI.
|
||||
for (size_t i = 0; i < aScopeChain.length(); ++i) {
|
||||
|
|
|
@ -689,8 +689,7 @@ nsScriptLoader::CreateModuleScript(nsModuleLoadRequest* aRequest)
|
|||
}
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
AutoEntryScript aes(globalObject, "CompileModule", true,
|
||||
context->GetNativeContext());
|
||||
AutoEntryScript aes(globalObject, "CompileModule", true);
|
||||
|
||||
bool oldProcessingScriptTag = context->GetProcessingScriptTag();
|
||||
context->SetProcessingScriptTag(true);
|
||||
|
@ -1978,8 +1977,7 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest)
|
|||
// New script entry point required, due to the "Create a script" sub-step of
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
|
||||
nsAutoMicroTask mt;
|
||||
AutoEntryScript aes(globalObject, "<script> element", true,
|
||||
context->GetNativeContext());
|
||||
AutoEntryScript aes(globalObject, "<script> element", true);
|
||||
JS::Rooted<JSObject*> global(aes.cx(),
|
||||
globalObject->GetGlobalJSObject());
|
||||
|
||||
|
|
|
@ -692,6 +692,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 904183 # b2g(bug 904183
|
|||
[test_createHTMLDocument.html]
|
||||
[test_declare_stylesheet_obsolete.html]
|
||||
[test_dialogArguments.html]
|
||||
tags = openwindow
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # showmodaldialog
|
||||
[test_document.all_iteration.html]
|
||||
[test_document.all_unqualified.html]
|
||||
|
@ -792,6 +793,7 @@ tags = audiochannel
|
|||
[test_open_null_features.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
|
||||
[test_openDialogChromeOnly.html]
|
||||
tags = openwindow
|
||||
[test_orientation_alternate.html]
|
||||
skip-if = toolkit != 'gonk'
|
||||
[test_orientation_frame.html]
|
||||
|
|
|
@ -233,12 +233,32 @@ function testSubtree() {
|
|||
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
||||
|
||||
observer.disconnect();
|
||||
SimpleTest.finish();
|
||||
testDictionaryWithoutChromePriv();
|
||||
});
|
||||
SpecialPowers.observeMutationEvents(m, document, true, true);
|
||||
parent.style.display = "block";
|
||||
}
|
||||
|
||||
function testDictionaryWithoutChromePriv()
|
||||
{
|
||||
var m = new MutationObserver(function() {});
|
||||
try {
|
||||
m.observe(document, { childList: true, get nativeAnonymousChildList() { throw "Foo1"; } } );
|
||||
ok(true, "Shouldn't throw!");
|
||||
} catch(ex) {
|
||||
ok(false, "Did throw " + ex);
|
||||
}
|
||||
|
||||
try {
|
||||
m.observe(document, { childList: true, get animations() { throw "Foo2"; } } );
|
||||
ok(true, "Shouldn't throw!");
|
||||
} catch(ex) {
|
||||
ok(false, "Did throw " + ex);
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -3159,9 +3159,15 @@ ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
|||
JS::AutoValueVector newArgs(aCx);
|
||||
// Arguments are passed in as value, key, object. Keep value and key, replace
|
||||
// object with the maplike/setlike object.
|
||||
newArgs.append(args.get(0));
|
||||
newArgs.append(args.get(1));
|
||||
newArgs.append(maplikeOrSetlikeObj);
|
||||
if (!newArgs.append(args.get(0))) {
|
||||
return false;
|
||||
}
|
||||
if (!newArgs.append(args.get(1))) {
|
||||
return false;
|
||||
}
|
||||
if (!newArgs.append(maplikeOrSetlikeObj)) {
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
|
||||
// Now actually call the user specified callback
|
||||
return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
|
||||
|
|
|
@ -3276,18 +3276,27 @@ struct StrongPtrForMember
|
|||
|
||||
inline
|
||||
JSObject*
|
||||
GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
|
||||
GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*>)
|
||||
{
|
||||
return JS_GetErrorPrototype(aCx);
|
||||
}
|
||||
|
||||
inline
|
||||
JSObject*
|
||||
GetIteratorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
|
||||
GetIteratorPrototype(JSContext* aCx, JS::Handle<JSObject*>)
|
||||
{
|
||||
return JS_GetIteratorPrototype(aCx);
|
||||
}
|
||||
|
||||
namespace binding_detail {
|
||||
inline
|
||||
JSObject*
|
||||
GetHackedNamespaceProtoObject(JSContext* aCx, JS::Handle<JSObject*>)
|
||||
{
|
||||
return JS_NewPlainObject(aCx);
|
||||
}
|
||||
} // namespace binding_detail
|
||||
|
||||
// Resolve an id on the given global object that wants to be included in
|
||||
// Exposed=System webidl annotations. False return value means exception
|
||||
// thrown.
|
||||
|
|
|
@ -498,6 +498,16 @@ DOMInterfaces = {
|
|||
'wrapperCache': False,
|
||||
},
|
||||
|
||||
'FlyWebFetchEvent': {
|
||||
'headerFile': 'FlyWebServerEvents.h',
|
||||
'nativeType': 'mozilla::dom::FlyWebFetchEvent',
|
||||
},
|
||||
|
||||
'FlyWebWebSocketEvent': {
|
||||
'headerFile': 'FlyWebServerEvents.h',
|
||||
'nativeType': 'mozilla::dom::FlyWebWebSocketEvent',
|
||||
},
|
||||
|
||||
'FontFaceSet': {
|
||||
'implicitJSContext': [ 'load' ],
|
||||
},
|
||||
|
@ -645,6 +655,10 @@ DOMInterfaces = {
|
|||
'headerFile': 'xpcjsid.h',
|
||||
},
|
||||
|
||||
'ImageBitmap': {
|
||||
'implicitJSContext': [ 'mapDataInto' ],
|
||||
},
|
||||
|
||||
'ImageCapture': {
|
||||
'binaryNames': { 'videoStreamTrack': 'GetVideoStreamTrack' }
|
||||
},
|
||||
|
@ -1887,6 +1901,22 @@ DOMInterfaces = {
|
|||
'headerFile': 'TestBindingHeader.h',
|
||||
'register': False
|
||||
},
|
||||
|
||||
'TestNamespace' : {
|
||||
'headerFile': 'TestBindingHeader.h',
|
||||
'register': False,
|
||||
},
|
||||
|
||||
'TestRenamedNamespace' : {
|
||||
'headerFile': 'TestBindingHeader.h',
|
||||
'register': False,
|
||||
},
|
||||
|
||||
'TestProtoObjectHackedNamespace' : {
|
||||
'headerFile': 'TestBindingHeader.h',
|
||||
'register': False,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
# These are temporary, until they've been converted to use new DOM bindings
|
||||
|
|
|
@ -625,7 +625,7 @@ def NeedsGeneratedHasInstance(descriptor):
|
|||
return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
|
||||
|
||||
|
||||
def InterfaceObjectProtoGetter(descriptor):
|
||||
def InterfaceObjectProtoGetter(descriptor, forXrays=False):
|
||||
"""
|
||||
Returns a tuple with two elements:
|
||||
|
||||
|
@ -638,11 +638,19 @@ def InterfaceObjectProtoGetter(descriptor):
|
|||
"""
|
||||
parentInterface = descriptor.interface.parent
|
||||
if parentInterface:
|
||||
assert not descriptor.interface.isNamespace()
|
||||
parentIfaceName = parentInterface.identifier.name
|
||||
parentDesc = descriptor.getDescriptor(parentIfaceName)
|
||||
prefix = toBindingNamespace(parentDesc.name)
|
||||
protoGetter = prefix + "::GetConstructorObject"
|
||||
protoHandleGetter = prefix + "::GetConstructorObjectHandle"
|
||||
elif descriptor.interface.isNamespace():
|
||||
if (forXrays or
|
||||
not descriptor.interface.getExtendedAttribute("ProtoObjectHack")):
|
||||
protoGetter = "JS_GetObjectPrototype"
|
||||
else:
|
||||
protoGetter = "binding_detail::GetHackedNamespaceProtoObject"
|
||||
protoHandleGetter = None
|
||||
else:
|
||||
protoGetter = "JS_GetFunctionPrototype"
|
||||
protoHandleGetter = None
|
||||
|
@ -661,7 +669,10 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
|
||||
def define(self):
|
||||
if self.descriptor.interface.ctor():
|
||||
assert not self.descriptor.interface.isNamespace()
|
||||
ctorname = CONSTRUCT_HOOK_NAME
|
||||
elif self.descriptor.interface.isNamespace():
|
||||
ctorname = "nullptr"
|
||||
else:
|
||||
ctorname = "ThrowingConstructor"
|
||||
if NeedsGeneratedHasInstance(self.descriptor):
|
||||
|
@ -675,11 +686,15 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
if len(self.descriptor.interface.namedConstructors) > 0:
|
||||
slotCount += (" + %i /* slots for the named constructors */" %
|
||||
len(self.descriptor.interface.namedConstructors))
|
||||
(protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor)
|
||||
(protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor,
|
||||
forXrays=True)
|
||||
|
||||
if ctorname == "ThrowingConstructor" and hasinstance == "InterfaceHasInstance":
|
||||
ret = ""
|
||||
classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
|
||||
elif ctorname == "nullptr" and hasinstance == "nullptr":
|
||||
ret = ""
|
||||
classOpsPtr = "JS_NULL_CLASS_OPS"
|
||||
else:
|
||||
ret = fill(
|
||||
"""
|
||||
|
@ -703,33 +718,51 @@ class CGInterfaceObjectJSClass(CGThing):
|
|||
hasInstance=hasinstance)
|
||||
classOpsPtr = "&sInterfaceObjectClassOps"
|
||||
|
||||
if self.descriptor.interface.isNamespace():
|
||||
classString = self.descriptor.interface.getExtendedAttribute("ClassString")
|
||||
if classString is None:
|
||||
classString = "Object"
|
||||
else:
|
||||
classString = classString[0]
|
||||
toStringResult = "[object %s]" % classString
|
||||
objectOps = "JS_NULL_OBJECT_OPS"
|
||||
else:
|
||||
classString = "Function"
|
||||
toStringResult = ("function %s() {\\n [native code]\\n}" %
|
||||
self.descriptor.interface.identifier.name)
|
||||
# We need non-default ObjectOps so we can actually make
|
||||
# use of our toStringResult.
|
||||
objectOps = "&sInterfaceObjectClassObjectOps"
|
||||
|
||||
ret = ret + fill(
|
||||
"""
|
||||
static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
|
||||
{
|
||||
"Function",
|
||||
"${classString}",
|
||||
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
|
||||
${classOpsPtr},
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
&sInterfaceObjectClassObjectOps
|
||||
${objectOps}
|
||||
},
|
||||
eInterface,
|
||||
${prototypeID},
|
||||
${depth},
|
||||
${hooks},
|
||||
"function ${name}() {\\n [native code]\\n}",
|
||||
"${toStringResult}",
|
||||
${protoGetter}
|
||||
};
|
||||
""",
|
||||
classString=classString,
|
||||
slotCount=slotCount,
|
||||
ctorname=ctorname,
|
||||
hasInstance=hasinstance,
|
||||
classOpsPtr=classOpsPtr,
|
||||
hooks=NativePropertyHooks(self.descriptor),
|
||||
name=self.descriptor.interface.identifier.name,
|
||||
objectOps=objectOps,
|
||||
prototypeID=prototypeID,
|
||||
depth=depth,
|
||||
toStringResult=toStringResult,
|
||||
protoGetter=protoGetter)
|
||||
return ret
|
||||
|
||||
|
@ -12725,9 +12758,18 @@ class CGDictionary(CGThing):
|
|||
# by the author needs to get converted, so we can remember if we have any
|
||||
# members present here.
|
||||
conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
|
||||
conversion = ("if (!isNull && !${propGet}) {\n"
|
||||
" return false;\n"
|
||||
"}\n")
|
||||
if isChromeOnly(member):
|
||||
conversion = ("if (!isNull) {\n"
|
||||
" if (!nsContentUtils::ThreadsafeIsCallerChrome()) {\n"
|
||||
" temp->setUndefined();\n"
|
||||
" } else if (!${propGet}) {\n"
|
||||
" return false;\n"
|
||||
" }\n"
|
||||
"}\n")
|
||||
else:
|
||||
conversion = ("if (!isNull && !${propGet}) {\n"
|
||||
" return false;\n"
|
||||
"}\n")
|
||||
if member.defaultValue:
|
||||
if (member.type.isUnion() and
|
||||
(not member.type.nullable() or
|
||||
|
@ -12837,6 +12879,8 @@ class CGDictionary(CGThing):
|
|||
if member.canHaveMissingValue():
|
||||
# Only do the conversion if we have a value
|
||||
conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
|
||||
if isChromeOnly(member):
|
||||
conversion = CGIfWrapper(conversion, "nsContentUtils::ThreadsafeIsCallerChrome()")
|
||||
return conversion
|
||||
|
||||
def getMemberTrace(self, member):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from WebIDL import IDLInterface, IDLExternalInterface, IDLImplementsStatement
|
||||
from WebIDL import IDLImplementsStatement
|
||||
import os
|
||||
from collections import defaultdict
|
||||
|
||||
|
@ -53,7 +53,7 @@ class Configuration:
|
|||
|
||||
assert not thing.isType()
|
||||
|
||||
if not thing.isInterface():
|
||||
if not thing.isInterface() and not thing.isNamespace():
|
||||
continue
|
||||
iface = thing
|
||||
self.interfaces[iface.identifier.name] = iface
|
||||
|
@ -433,6 +433,7 @@ class Descriptor(DescriptorProvider):
|
|||
# them as having a concrete descendant.
|
||||
self.concrete = (not self.interface.isExternal() and
|
||||
not self.interface.isCallback() and
|
||||
not self.interface.isNamespace() and
|
||||
desc.get('concrete', True))
|
||||
self.hasUnforgeableMembers = (self.concrete and
|
||||
any(MemberIsUnforgeable(m, self) for m in
|
||||
|
|
|
@ -158,6 +158,9 @@ class IDLObject(object):
|
|||
def isInterface(self):
|
||||
return False
|
||||
|
||||
def isNamespace(self):
|
||||
return False
|
||||
|
||||
def isEnum(self):
|
||||
return False
|
||||
|
||||
|
@ -585,8 +588,8 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||
return set()
|
||||
|
||||
|
||||
class IDLPartialInterface(IDLObject):
|
||||
def __init__(self, location, name, members, nonPartialInterface):
|
||||
class IDLPartialInterfaceOrNamespace(IDLObject):
|
||||
def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
|
||||
assert isinstance(name, IDLUnresolvedIdentifier)
|
||||
|
||||
IDLObject.__init__(self, location)
|
||||
|
@ -596,9 +599,9 @@ class IDLPartialInterface(IDLObject):
|
|||
# propagated to our non-partial interface.
|
||||
self.propagatedExtendedAttrs = []
|
||||
self._haveSecureContextExtendedAttribute = False
|
||||
self._nonPartialInterface = nonPartialInterface
|
||||
self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
|
||||
self._finished = False
|
||||
nonPartialInterface.addPartialInterface(self)
|
||||
nonPartialInterfaceOrNamespace.addPartialInterface(self)
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
for attr in attrs:
|
||||
|
@ -635,18 +638,22 @@ class IDLPartialInterface(IDLObject):
|
|||
return
|
||||
self._finished = True
|
||||
if (not self._haveSecureContextExtendedAttribute and
|
||||
self._nonPartialInterface.getExtendedAttribute("SecureContext")):
|
||||
self._nonPartialInterfaceOrNamespace.getExtendedAttribute("SecureContext")):
|
||||
# This gets propagated to all our members.
|
||||
for member in self.members:
|
||||
if member.getExtendedAttribute("SecureContext"):
|
||||
raise WebIDLError("[SecureContext] specified on both a "
|
||||
"partial interface member and on the "
|
||||
"non-partial interface",
|
||||
[member.location, self._nonPartialInterface.location])
|
||||
member.addExtendedAttributes([IDLExtendedAttribute(self._nonPartialInterface.location, ("SecureContext",))])
|
||||
# Need to make sure our non-partial interface gets finished so it can
|
||||
# report cases when we only have partial interfaces.
|
||||
self._nonPartialInterface.finish(scope)
|
||||
[member.location,
|
||||
self._nonPartialInterfaceOrNamespace.location])
|
||||
member.addExtendedAttributes(
|
||||
[IDLExtendedAttribute(self._nonPartialInterfaceOrNamespace.location,
|
||||
("SecureContext",))])
|
||||
# Need to make sure our non-partial interface or namespace gets
|
||||
# finished so it can report cases when we only have partial
|
||||
# interfaces/namespaces.
|
||||
self._nonPartialInterfaceOrNamespace.finish(scope)
|
||||
|
||||
def validate(self):
|
||||
pass
|
||||
|
@ -666,7 +673,7 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
|
|||
exposureSet.update(globalScope.globalNameMapping[name])
|
||||
|
||||
|
||||
class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
||||
class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
|
||||
def __init__(self, location, parentScope, name, parent, members,
|
||||
isKnownNonPartial):
|
||||
assert isinstance(parentScope, IDLScope)
|
||||
|
@ -712,9 +719,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||
if isKnownNonPartial:
|
||||
self.setNonPartial(location, parent, members)
|
||||
|
||||
def __str__(self):
|
||||
return "Interface '%s'" % self.identifier.name
|
||||
|
||||
def ctor(self):
|
||||
identifier = IDLUnresolvedIdentifier(self.location, "constructor",
|
||||
allowForbidden=True)
|
||||
|
@ -810,6 +814,20 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||
|
||||
assert iter(self.members)
|
||||
|
||||
if self.isNamespace():
|
||||
assert not self.parent
|
||||
for m in self.members:
|
||||
if m.isAttr() or m.isMethod():
|
||||
if m.isStatic():
|
||||
raise WebIDLError("Don't mark things explicitly static "
|
||||
"in namespaces",
|
||||
[self.location, m.location])
|
||||
# Just mark all our methods/attributes as static. The other
|
||||
# option is to duplicate the relevant InterfaceMembers
|
||||
# production bits but modified to produce static stuff to
|
||||
# start with, but that sounds annoying.
|
||||
m.forceStatic()
|
||||
|
||||
if self.parent:
|
||||
self.parent.finish(scope)
|
||||
self.parent._hasChildInterfaces = True
|
||||
|
@ -1324,9 +1342,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||
[self.location, iterableDecl.location,
|
||||
indexedGetter.location])
|
||||
|
||||
def isInterface(self):
|
||||
return True
|
||||
|
||||
def isExternal(self):
|
||||
return False
|
||||
|
||||
|
@ -1377,7 +1392,172 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||
return not hasattr(self, "_noInterfaceObject")
|
||||
|
||||
def hasInterfacePrototypeObject(self):
|
||||
return not self.isCallback() and self.getUserData('hasConcreteDescendant', False)
|
||||
return (not self.isCallback() and not self.isNamespace()
|
||||
and self.getUserData('hasConcreteDescendant', False))
|
||||
|
||||
def addImplementedInterface(self, implementedInterface):
|
||||
assert(isinstance(implementedInterface, IDLInterface))
|
||||
self.implementedInterfaces.add(implementedInterface)
|
||||
|
||||
def getInheritedInterfaces(self):
|
||||
"""
|
||||
Returns a list of the interfaces this interface inherits from
|
||||
(not including this interface itself). The list is in order
|
||||
from most derived to least derived.
|
||||
"""
|
||||
assert(self._finished)
|
||||
if not self.parent:
|
||||
return []
|
||||
parentInterfaces = self.parent.getInheritedInterfaces()
|
||||
parentInterfaces.insert(0, self.parent)
|
||||
return parentInterfaces
|
||||
|
||||
def getConsequentialInterfaces(self):
|
||||
assert(self._finished)
|
||||
# The interfaces we implement directly
|
||||
consequentialInterfaces = set(self.implementedInterfaces)
|
||||
|
||||
# And their inherited interfaces
|
||||
for iface in self.implementedInterfaces:
|
||||
consequentialInterfaces |= set(iface.getInheritedInterfaces())
|
||||
|
||||
# And now collect up the consequential interfaces of all of those
|
||||
temp = set()
|
||||
for iface in consequentialInterfaces:
|
||||
temp |= iface.getConsequentialInterfaces()
|
||||
|
||||
return consequentialInterfaces | temp
|
||||
|
||||
def findInterfaceLoopPoint(self, otherInterface):
|
||||
"""
|
||||
Finds an interface, amongst our ancestors and consequential interfaces,
|
||||
that inherits from otherInterface or implements otherInterface
|
||||
directly. If there is no such interface, returns None.
|
||||
"""
|
||||
if self.parent:
|
||||
if self.parent == otherInterface:
|
||||
return self
|
||||
loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
|
||||
if loopPoint:
|
||||
return loopPoint
|
||||
if otherInterface in self.implementedInterfaces:
|
||||
return self
|
||||
for iface in self.implementedInterfaces:
|
||||
loopPoint = iface.findInterfaceLoopPoint(otherInterface)
|
||||
if loopPoint:
|
||||
return loopPoint
|
||||
return None
|
||||
|
||||
def getExtendedAttribute(self, name):
|
||||
return self._extendedAttrDict.get(name, None)
|
||||
|
||||
def setNonPartial(self, location, parent, members):
|
||||
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
|
||||
if self._isKnownNonPartial:
|
||||
raise WebIDLError("Two non-partial definitions for the "
|
||||
"same %s" %
|
||||
("interface" if self.isInterface()
|
||||
else "namespace"),
|
||||
[location, self.location])
|
||||
self._isKnownNonPartial = True
|
||||
# Now make it look like we were parsed at this new location, since
|
||||
# that's the place where the interface is "really" defined
|
||||
self.location = location
|
||||
assert not self.parent
|
||||
self.parent = parent
|
||||
# Put the new members at the beginning
|
||||
self.members = members + self.members
|
||||
|
||||
def addPartialInterface(self, partial):
|
||||
assert self.identifier.name == partial.identifier.name
|
||||
self._partialInterfaces.append(partial)
|
||||
|
||||
def getJSImplementation(self):
|
||||
classId = self.getExtendedAttribute("JSImplementation")
|
||||
if not classId:
|
||||
return classId
|
||||
assert isinstance(classId, list)
|
||||
assert len(classId) == 1
|
||||
return classId[0]
|
||||
|
||||
def isJSImplemented(self):
|
||||
return bool(self.getJSImplementation())
|
||||
|
||||
def isProbablyShortLivingObject(self):
|
||||
current = self
|
||||
while current:
|
||||
if current.getExtendedAttribute("ProbablyShortLivingObject"):
|
||||
return True
|
||||
current = current.parent
|
||||
return False
|
||||
|
||||
def isNavigatorProperty(self):
|
||||
naviProp = self.getExtendedAttribute("NavigatorProperty")
|
||||
if not naviProp:
|
||||
return False
|
||||
assert len(naviProp) == 1
|
||||
assert isinstance(naviProp, list)
|
||||
assert len(naviProp[0]) != 0
|
||||
return True
|
||||
|
||||
def getNavigatorProperty(self):
|
||||
naviProp = self.getExtendedAttribute("NavigatorProperty")
|
||||
if not naviProp:
|
||||
return None
|
||||
assert len(naviProp) == 1
|
||||
assert isinstance(naviProp, list)
|
||||
assert len(naviProp[0]) != 0
|
||||
conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes
|
||||
attr = IDLAttribute(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
|
||||
IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
|
||||
True,
|
||||
extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
|
||||
navigatorObjectGetter=True)
|
||||
attr._exposureGlobalNames = self._exposureGlobalNames
|
||||
# We're abusing Constant a little bit here, because we need Cached. The
|
||||
# getter will create a new object every time, but we're never going to
|
||||
# clear the cached value.
|
||||
extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
|
||||
IDLExtendedAttribute(self.location, ("Cached", )),
|
||||
IDLExtendedAttribute(self.location, ("Constant", )) ]
|
||||
attr.addExtendedAttributes(extendedAttrs)
|
||||
return attr
|
||||
|
||||
def hasChildInterfaces(self):
|
||||
return self._hasChildInterfaces
|
||||
|
||||
def isOnGlobalProtoChain(self):
|
||||
return self._isOnGlobalProtoChain
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set(self.members)
|
||||
deps.update(self.implementedInterfaces)
|
||||
if self.parent:
|
||||
deps.add(self.parent)
|
||||
return deps
|
||||
|
||||
def hasMembersInSlots(self):
|
||||
return self._ownMembersInSlots != 0
|
||||
|
||||
conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
|
||||
"SecureContext",
|
||||
"CheckAnyPermissions",
|
||||
"CheckAllPermissions" ]
|
||||
def isExposedConditionally(self):
|
||||
return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
|
||||
|
||||
class IDLInterface(IDLInterfaceOrNamespace):
|
||||
def __init__(self, location, parentScope, name, parent, members,
|
||||
isKnownNonPartial):
|
||||
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
|
||||
parent, members, isKnownNonPartial)
|
||||
|
||||
def __str__(self):
|
||||
return "Interface '%s'" % self.identifier.name
|
||||
|
||||
def isInterface(self):
|
||||
return True
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
for attr in attrs:
|
||||
|
@ -1556,155 +1736,46 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||
attrlist = attr.listValue()
|
||||
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
|
||||
|
||||
def addImplementedInterface(self, implementedInterface):
|
||||
assert(isinstance(implementedInterface, IDLInterface))
|
||||
self.implementedInterfaces.add(implementedInterface)
|
||||
|
||||
def getInheritedInterfaces(self):
|
||||
"""
|
||||
Returns a list of the interfaces this interface inherits from
|
||||
(not including this interface itself). The list is in order
|
||||
from most derived to least derived.
|
||||
"""
|
||||
assert(self._finished)
|
||||
if not self.parent:
|
||||
return []
|
||||
parentInterfaces = self.parent.getInheritedInterfaces()
|
||||
parentInterfaces.insert(0, self.parent)
|
||||
return parentInterfaces
|
||||
class IDLNamespace(IDLInterfaceOrNamespace):
|
||||
def __init__(self, location, parentScope, name, members, isKnownNonPartial):
|
||||
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
|
||||
None, members, isKnownNonPartial)
|
||||
|
||||
def getConsequentialInterfaces(self):
|
||||
assert(self._finished)
|
||||
# The interfaces we implement directly
|
||||
consequentialInterfaces = set(self.implementedInterfaces)
|
||||
def __str__(self):
|
||||
return "Namespace '%s'" % self.identifier.name
|
||||
|
||||
# And their inherited interfaces
|
||||
for iface in self.implementedInterfaces:
|
||||
consequentialInterfaces |= set(iface.getInheritedInterfaces())
|
||||
|
||||
# And now collect up the consequential interfaces of all of those
|
||||
temp = set()
|
||||
for iface in consequentialInterfaces:
|
||||
temp |= iface.getConsequentialInterfaces()
|
||||
|
||||
return consequentialInterfaces | temp
|
||||
|
||||
def findInterfaceLoopPoint(self, otherInterface):
|
||||
"""
|
||||
Finds an interface, amongst our ancestors and consequential interfaces,
|
||||
that inherits from otherInterface or implements otherInterface
|
||||
directly. If there is no such interface, returns None.
|
||||
"""
|
||||
if self.parent:
|
||||
if self.parent == otherInterface:
|
||||
return self
|
||||
loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
|
||||
if loopPoint:
|
||||
return loopPoint
|
||||
if otherInterface in self.implementedInterfaces:
|
||||
return self
|
||||
for iface in self.implementedInterfaces:
|
||||
loopPoint = iface.findInterfaceLoopPoint(otherInterface)
|
||||
if loopPoint:
|
||||
return loopPoint
|
||||
return None
|
||||
|
||||
def getExtendedAttribute(self, name):
|
||||
return self._extendedAttrDict.get(name, None)
|
||||
|
||||
def setNonPartial(self, location, parent, members):
|
||||
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
|
||||
if self._isKnownNonPartial:
|
||||
raise WebIDLError("Two non-partial definitions for the "
|
||||
"same interface",
|
||||
[location, self.location])
|
||||
self._isKnownNonPartial = True
|
||||
# Now make it look like we were parsed at this new location, since
|
||||
# that's the place where the interface is "really" defined
|
||||
self.location = location
|
||||
assert not self.parent
|
||||
self.parent = parent
|
||||
# Put the new members at the beginning
|
||||
self.members = members + self.members
|
||||
|
||||
def addPartialInterface(self, partial):
|
||||
assert self.identifier.name == partial.identifier.name
|
||||
self._partialInterfaces.append(partial)
|
||||
|
||||
def getJSImplementation(self):
|
||||
classId = self.getExtendedAttribute("JSImplementation")
|
||||
if not classId:
|
||||
return classId
|
||||
assert isinstance(classId, list)
|
||||
assert len(classId) == 1
|
||||
return classId[0]
|
||||
|
||||
def isJSImplemented(self):
|
||||
return bool(self.getJSImplementation())
|
||||
|
||||
def isProbablyShortLivingObject(self):
|
||||
current = self
|
||||
while current:
|
||||
if current.getExtendedAttribute("ProbablyShortLivingObject"):
|
||||
return True
|
||||
current = current.parent
|
||||
return False
|
||||
|
||||
def isNavigatorProperty(self):
|
||||
naviProp = self.getExtendedAttribute("NavigatorProperty")
|
||||
if not naviProp:
|
||||
return False
|
||||
assert len(naviProp) == 1
|
||||
assert isinstance(naviProp, list)
|
||||
assert len(naviProp[0]) != 0
|
||||
def isNamespace(self):
|
||||
return True
|
||||
|
||||
def getNavigatorProperty(self):
|
||||
naviProp = self.getExtendedAttribute("NavigatorProperty")
|
||||
if not naviProp:
|
||||
return None
|
||||
assert len(naviProp) == 1
|
||||
assert isinstance(naviProp, list)
|
||||
assert len(naviProp[0]) != 0
|
||||
conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterface.conditionExtendedAttributes
|
||||
attr = IDLAttribute(self.location,
|
||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
|
||||
IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
|
||||
True,
|
||||
extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
|
||||
navigatorObjectGetter=True)
|
||||
attr._exposureGlobalNames = self._exposureGlobalNames
|
||||
# We're abusing Constant a little bit here, because we need Cached. The
|
||||
# getter will create a new object every time, but we're never going to
|
||||
# clear the cached value.
|
||||
extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
|
||||
IDLExtendedAttribute(self.location, ("Cached", )),
|
||||
IDLExtendedAttribute(self.location, ("Constant", )) ]
|
||||
attr.addExtendedAttributes(extendedAttrs)
|
||||
return attr
|
||||
def addExtendedAttributes(self, attrs):
|
||||
# The set of things namespaces support is small enough it's simpler
|
||||
# to factor out into a separate method than it is to sprinkle
|
||||
# isNamespace() checks all through
|
||||
# IDLInterfaceOrNamespace.addExtendedAttributes.
|
||||
for attr in attrs:
|
||||
identifier = attr.identifier()
|
||||
|
||||
def hasChildInterfaces(self):
|
||||
return self._hasChildInterfaces
|
||||
if identifier == "Exposed":
|
||||
convertExposedAttrToGlobalNameSet(attr,
|
||||
self._exposureGlobalNames)
|
||||
elif identifier == "ClassString":
|
||||
# Takes a string value to override the default "Object" if
|
||||
# desired.
|
||||
if not attr.hasValue():
|
||||
raise WebIDLError("[%s] must have a value" % identifier,
|
||||
[attr.location])
|
||||
elif identifier == "ProtoObjectHack":
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[%s] must not have arguments" % identifier,
|
||||
[attr.location])
|
||||
else:
|
||||
raise WebIDLError("Unknown extended attribute %s on namespace" %
|
||||
identifier,
|
||||
[attr.location])
|
||||
|
||||
def isOnGlobalProtoChain(self):
|
||||
return self._isOnGlobalProtoChain
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set(self.members)
|
||||
deps.update(self.implementedInterfaces)
|
||||
if self.parent:
|
||||
deps.add(self.parent)
|
||||
return deps
|
||||
|
||||
def hasMembersInSlots(self):
|
||||
return self._ownMembersInSlots != 0
|
||||
|
||||
conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
|
||||
"SecureContext",
|
||||
"CheckAnyPermissions",
|
||||
"CheckAllPermissions" ]
|
||||
def isExposedConditionally(self):
|
||||
return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
|
||||
attrlist = attr.listValue()
|
||||
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
|
||||
|
||||
|
||||
class IDLDictionary(IDLObjectWithScope):
|
||||
|
@ -3930,7 +4001,7 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
self.type = type
|
||||
self.readonly = readonly
|
||||
self.inherit = inherit
|
||||
self.static = static
|
||||
self._static = static
|
||||
self.lenientThis = False
|
||||
self._unforgeable = False
|
||||
self.stringifier = stringifier
|
||||
|
@ -3952,7 +4023,10 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
[self.location])
|
||||
|
||||
def isStatic(self):
|
||||
return self.static
|
||||
return self._static
|
||||
|
||||
def forceStatic(self):
|
||||
self._static = True
|
||||
|
||||
def __str__(self):
|
||||
return "'%s' attribute '%s'" % (self.type, self.identifier)
|
||||
|
@ -4006,16 +4080,53 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
"interface type as its type", [self.location])
|
||||
|
||||
def validate(self):
|
||||
def typeContainsChromeOnlyDictionaryMember(type):
|
||||
if (type.nullable() or
|
||||
type.isSequence() or
|
||||
type.isMozMap()):
|
||||
return typeContainsChromeOnlyDictionaryMember(type.inner)
|
||||
|
||||
if type.isUnion():
|
||||
for memberType in type.flatMemberTypes:
|
||||
(contains, location) = typeContainsChromeOnlyDictionaryMember(memberType)
|
||||
if contains:
|
||||
return (True, location)
|
||||
|
||||
if type.isDictionary():
|
||||
dictionary = type.inner
|
||||
while dictionary:
|
||||
(contains, location) = dictionaryContainsChromeOnlyMember(dictionary)
|
||||
if contains:
|
||||
return (True, location)
|
||||
dictionary = dictionary.parent
|
||||
|
||||
return (False, None)
|
||||
|
||||
def dictionaryContainsChromeOnlyMember(dictionary):
|
||||
for member in dictionary.members:
|
||||
if member.getExtendedAttribute("ChromeOnly"):
|
||||
return (True, member.location)
|
||||
(contains, location) = typeContainsChromeOnlyDictionaryMember(member.type)
|
||||
if contains:
|
||||
return (True, location)
|
||||
return (False, None)
|
||||
|
||||
IDLInterfaceMember.validate(self)
|
||||
|
||||
if ((self.getExtendedAttribute("Cached") or
|
||||
self.getExtendedAttribute("StoreInSlot")) and
|
||||
not self.affects == "Nothing"):
|
||||
raise WebIDLError("Cached attributes and attributes stored in "
|
||||
"slots must be Constant or Pure or "
|
||||
"Affects=Nothing, since the getter won't always "
|
||||
"be called.",
|
||||
[self.location])
|
||||
if (self.getExtendedAttribute("Cached") or
|
||||
self.getExtendedAttribute("StoreInSlot")):
|
||||
if not self.affects == "Nothing":
|
||||
raise WebIDLError("Cached attributes and attributes stored in "
|
||||
"slots must be Constant or Pure or "
|
||||
"Affects=Nothing, since the getter won't always "
|
||||
"be called.",
|
||||
[self.location])
|
||||
(contains, location) = typeContainsChromeOnlyDictionaryMember(self.type)
|
||||
if contains:
|
||||
raise WebIDLError("[Cached] and [StoreInSlot] must not be used "
|
||||
"on an attribute whose type contains a "
|
||||
"[ChromeOnly] dictionary member",
|
||||
[self.location, location])
|
||||
if self.getExtendedAttribute("Frozen"):
|
||||
if (not self.type.isSequence() and not self.type.isDictionary() and
|
||||
not self.type.isMozMap()):
|
||||
|
@ -4261,6 +4372,7 @@ class IDLArgument(IDLObjectWithIdentifier):
|
|||
self.enforceRange = False
|
||||
self.clamp = False
|
||||
self._allowTreatNonCallableAsNull = False
|
||||
self._extendedAttrDict = {}
|
||||
|
||||
assert not variadic or optional
|
||||
assert not variadic or not defaultValue
|
||||
|
@ -4290,11 +4402,21 @@ class IDLArgument(IDLObjectWithIdentifier):
|
|||
self.enforceRange = True
|
||||
elif identifier == "TreatNonCallableAsNull":
|
||||
self._allowTreatNonCallableAsNull = True
|
||||
elif self.dictionaryMember and identifier == "ChromeOnly":
|
||||
if not self.optional:
|
||||
raise WebIDLError("[ChromeOnly] must not be used on a required "
|
||||
"dictionary member",
|
||||
[attribute.location])
|
||||
else:
|
||||
raise WebIDLError("Unhandled extended attribute on %s" %
|
||||
("a dictionary member" if self.dictionaryMember else
|
||||
"an argument"),
|
||||
[attribute.location])
|
||||
attrlist = attribute.listValue()
|
||||
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
|
||||
|
||||
def getExtendedAttribute(self, name):
|
||||
return self._extendedAttrDict.get(name, None)
|
||||
|
||||
def isComplete(self):
|
||||
return self._isComplete
|
||||
|
@ -4565,6 +4687,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
def isStatic(self):
|
||||
return self._static
|
||||
|
||||
def forceStatic(self):
|
||||
self._static = True
|
||||
|
||||
def isGetter(self):
|
||||
return self._getter
|
||||
|
||||
|
@ -5144,7 +5269,8 @@ class Tokenizer(object):
|
|||
"or": "OR",
|
||||
"maplike": "MAPLIKE",
|
||||
"setlike": "SETLIKE",
|
||||
"iterable": "ITERABLE"
|
||||
"iterable": "ITERABLE",
|
||||
"namespace": "NAMESPACE"
|
||||
}
|
||||
|
||||
tokens.extend(keywords.values())
|
||||
|
@ -5241,7 +5367,8 @@ class Parser(Tokenizer):
|
|||
def p_Definition(self, p):
|
||||
"""
|
||||
Definition : CallbackOrInterface
|
||||
| PartialInterface
|
||||
| Namespace
|
||||
| Partial
|
||||
| Dictionary
|
||||
| Exception
|
||||
| Enum
|
||||
|
@ -5275,6 +5402,41 @@ class Parser(Tokenizer):
|
|||
assert p[1]
|
||||
p[0] = p[1]
|
||||
|
||||
def handleNonPartialObject(self, location, identifier, constructor,
|
||||
constructorArgs, nonPartialArgs):
|
||||
"""
|
||||
This handles non-partial objects (interfaces and namespaces) by
|
||||
checking for an existing partial object, and promoting it to
|
||||
non-partial as needed. The return value is the non-partial object.
|
||||
|
||||
constructorArgs are all the args for the constructor except the last
|
||||
one: isKnownNonPartial.
|
||||
|
||||
nonPartialArgs are the args for the setNonPartial call.
|
||||
"""
|
||||
# The name of the class starts with "IDL", so strip that off.
|
||||
# Also, starts with a capital letter after that, so nix that
|
||||
# as well.
|
||||
prettyname = constructor.__name__[3:].lower()
|
||||
|
||||
try:
|
||||
existingObj = self.globalScope()._lookupIdentifier(identifier)
|
||||
if existingObj:
|
||||
if not isinstance(existingObj, constructor):
|
||||
raise WebIDLError("%s has the same name as "
|
||||
"non-%s object" %
|
||||
(prettyname.capitalize(), prettyname),
|
||||
[location, existingObj.location])
|
||||
existingObj.setNonPartial(*nonPartialArgs)
|
||||
return existingObj
|
||||
except Exception, ex:
|
||||
if isinstance(ex, WebIDLError):
|
||||
raise ex
|
||||
pass
|
||||
|
||||
# True for isKnownNonPartial
|
||||
return constructor(*(constructorArgs + [True]))
|
||||
|
||||
def p_Interface(self, p):
|
||||
"""
|
||||
Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
|
||||
|
@ -5284,24 +5446,10 @@ class Parser(Tokenizer):
|
|||
members = p[5]
|
||||
parent = p[3]
|
||||
|
||||
try:
|
||||
existingObj = self.globalScope()._lookupIdentifier(identifier)
|
||||
if existingObj:
|
||||
p[0] = existingObj
|
||||
if not isinstance(p[0], IDLInterface):
|
||||
raise WebIDLError("Interface has the same name as "
|
||||
"non-interface object",
|
||||
[location, p[0].location])
|
||||
p[0].setNonPartial(location, parent, members)
|
||||
return
|
||||
except Exception, ex:
|
||||
if isinstance(ex, WebIDLError):
|
||||
raise ex
|
||||
pass
|
||||
|
||||
iface = IDLInterface(location, self.globalScope(), identifier, parent,
|
||||
members, isKnownNonPartial=True)
|
||||
p[0] = iface
|
||||
p[0] = self.handleNonPartialObject(
|
||||
location, identifier, IDLInterface,
|
||||
[location, self.globalScope(), identifier, parent, members],
|
||||
[location, parent, members])
|
||||
|
||||
def p_InterfaceForwardDecl(self, p):
|
||||
"""
|
||||
|
@ -5326,34 +5474,100 @@ class Parser(Tokenizer):
|
|||
|
||||
p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
|
||||
|
||||
def p_PartialInterface(self, p):
|
||||
def p_Namespace(self, p):
|
||||
"""
|
||||
PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
|
||||
Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
|
||||
"""
|
||||
location = self.getLocation(p, 2)
|
||||
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
|
||||
members = p[5]
|
||||
location = self.getLocation(p, 1)
|
||||
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
||||
members = p[4]
|
||||
|
||||
nonPartialInterface = None
|
||||
p[0] = self.handleNonPartialObject(
|
||||
location, identifier, IDLNamespace,
|
||||
[location, self.globalScope(), identifier, members],
|
||||
[location, None, members])
|
||||
|
||||
def p_Partial(self, p):
|
||||
"""
|
||||
Partial : PARTIAL PartialDefinition
|
||||
"""
|
||||
p[0] = p[2]
|
||||
|
||||
def p_PartialDefinition(self, p):
|
||||
"""
|
||||
PartialDefinition : PartialInterface
|
||||
| PartialNamespace
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
def handlePartialObject(self, location, identifier, nonPartialConstructor,
|
||||
nonPartialConstructorArgs,
|
||||
partialConstructorArgs):
|
||||
"""
|
||||
This handles partial objects (interfaces and namespaces) by checking for
|
||||
an existing non-partial object, and adding ourselves to it as needed.
|
||||
The return value is our partial object. For now we just use
|
||||
IDLPartialInterfaceOrNamespace for partial objects.
|
||||
|
||||
nonPartialConstructorArgs are all the args for the non-partial
|
||||
constructor except the last two: members and isKnownNonPartial.
|
||||
|
||||
partialConstructorArgs are the arguments for the
|
||||
IDLPartialInterfaceOrNamespace constructor, except the last one (the
|
||||
non-partial object).
|
||||
"""
|
||||
# The name of the class starts with "IDL", so strip that off.
|
||||
# Also, starts with a capital letter after that, so nix that
|
||||
# as well.
|
||||
prettyname = nonPartialConstructor.__name__[3:].lower()
|
||||
|
||||
nonPartialObject = None
|
||||
try:
|
||||
nonPartialInterface = self.globalScope()._lookupIdentifier(identifier)
|
||||
if nonPartialInterface:
|
||||
if not isinstance(nonPartialInterface, IDLInterface):
|
||||
raise WebIDLError("Partial interface has the same name as "
|
||||
"non-interface object",
|
||||
[location, nonPartialInterface.location])
|
||||
nonPartialObject = self.globalScope()._lookupIdentifier(identifier)
|
||||
if nonPartialObject:
|
||||
if not isinstance(nonPartialObject, nonPartialConstructor):
|
||||
raise WebIDLError("Partial %s has the same name as "
|
||||
"non-%s object" %
|
||||
(prettyname, prettyname),
|
||||
[location, nonPartialObject.location])
|
||||
except Exception, ex:
|
||||
if isinstance(ex, WebIDLError):
|
||||
raise ex
|
||||
pass
|
||||
|
||||
if not nonPartialInterface:
|
||||
nonPartialInterface = IDLInterface(location, self.globalScope(),
|
||||
identifier, None,
|
||||
[], isKnownNonPartial=False)
|
||||
partialInterface = IDLPartialInterface(location, identifier, members,
|
||||
nonPartialInterface)
|
||||
p[0] = partialInterface
|
||||
if not nonPartialObject:
|
||||
nonPartialObject = nonPartialConstructor(
|
||||
# No members, False for isKnownNonPartial
|
||||
*(nonPartialConstructorArgs + [[], False]))
|
||||
partialInterface = IDLPartialInterfaceOrNamespace(
|
||||
*(partialConstructorArgs + [nonPartialObject]))
|
||||
return partialInterface
|
||||
|
||||
def p_PartialInterface(self, p):
|
||||
"""
|
||||
PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
|
||||
"""
|
||||
location = self.getLocation(p, 1)
|
||||
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
||||
members = p[4]
|
||||
|
||||
p[0] = self.handlePartialObject(
|
||||
location, identifier, IDLInterface,
|
||||
[location, self.globalScope(), identifier, None],
|
||||
[location, identifier, members])
|
||||
|
||||
def p_PartialNamespace(self, p):
|
||||
"""
|
||||
PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
|
||||
"""
|
||||
location = self.getLocation(p, 1)
|
||||
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
||||
members = p[4]
|
||||
|
||||
p[0] = self.handlePartialObject(
|
||||
location, identifier, IDLNamespace,
|
||||
[location, self.globalScope(), identifier],
|
||||
[location, identifier, members])
|
||||
|
||||
def p_Inheritance(self, p):
|
||||
"""
|
||||
|
@ -6014,6 +6228,7 @@ class Parser(Tokenizer):
|
|||
| JSONIFIER
|
||||
| TYPEDEF
|
||||
| UNRESTRICTED
|
||||
| NAMESPACE
|
||||
"""
|
||||
p[0] = p[1]
|
||||
|
||||
|
@ -6614,18 +6829,20 @@ class Parser(Tokenizer):
|
|||
Tokenizer.__init__(self, outputdir, lexer)
|
||||
|
||||
logger = SqueakyCleanLogger()
|
||||
self.parser = yacc.yacc(module=self,
|
||||
outputdir=outputdir,
|
||||
tabmodule='webidlyacc',
|
||||
errorlog=logger
|
||||
# Pickling the grammar is a speedup in
|
||||
# some cases (older Python?) but a
|
||||
# significant slowdown in others.
|
||||
# We're not pickling for now, until it
|
||||
# becomes a speedup again.
|
||||
# , picklefile='WebIDLGrammar.pkl'
|
||||
)
|
||||
logger.reportGrammarErrors()
|
||||
try:
|
||||
self.parser = yacc.yacc(module=self,
|
||||
outputdir=outputdir,
|
||||
tabmodule='webidlyacc',
|
||||
errorlog=logger
|
||||
# Pickling the grammar is a speedup in
|
||||
# some cases (older Python?) but a
|
||||
# significant slowdown in others.
|
||||
# We're not pickling for now, until it
|
||||
# becomes a speedup again.
|
||||
# , picklefile='WebIDLGrammar.pkl'
|
||||
)
|
||||
finally:
|
||||
logger.reportGrammarErrors()
|
||||
|
||||
self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
|
||||
# To make our test harness work, pretend like we have a primary global already.
|
||||
|
@ -6696,10 +6913,11 @@ class Parser(Tokenizer):
|
|||
# We're generating a partial interface to add a readonly
|
||||
# property to the Navigator interface for every interface
|
||||
# annotated with NavigatorProperty.
|
||||
partialInterface = IDLPartialInterface(iface.location,
|
||||
IDLUnresolvedIdentifier(iface.location, "Navigator"),
|
||||
[ navigatorProperty ],
|
||||
navigatorInterface)
|
||||
partialInterface = IDLPartialInterfaceOrNamespace(
|
||||
iface.location,
|
||||
IDLUnresolvedIdentifier(iface.location, "Navigator"),
|
||||
[ navigatorProperty ],
|
||||
navigatorInterface)
|
||||
self._productions.append(partialInterface)
|
||||
|
||||
iterable = None
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
def WebIDLTest(parser, harness):
|
||||
parser.parse("""
|
||||
dictionary Dict {
|
||||
any foo;
|
||||
[ChromeOnly] any bar;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.check(len(results), 1, "Should have a dictionary")
|
||||
members = results[0].members;
|
||||
harness.check(len(members), 2, "Should have two members")
|
||||
# Note that members are ordered lexicographically, so "bar" comes
|
||||
# before "foo".
|
||||
harness.ok(members[0].getExtendedAttribute("ChromeOnly"),
|
||||
"First member is not ChromeOnly")
|
||||
harness.ok(not members[1].getExtendedAttribute("ChromeOnly"),
|
||||
"Second member is ChromeOnly")
|
||||
|
||||
parser = parser.reset()
|
||||
parser.parse("""
|
||||
dictionary Dict {
|
||||
any foo;
|
||||
any bar;
|
||||
};
|
||||
|
||||
interface Iface {
|
||||
[Constant, Cached] readonly attribute Dict dict;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
harness.check(len(results), 2, "Should have a dictionary and an interface")
|
||||
|
||||
parser = parser.reset()
|
||||
exception = None
|
||||
try:
|
||||
parser.parse("""
|
||||
dictionary Dict {
|
||||
any foo;
|
||||
[ChromeOnly] any bar;
|
||||
};
|
||||
|
||||
interface Iface {
|
||||
[Constant, Cached] readonly attribute Dict dict;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except Exception, exception:
|
||||
pass
|
||||
|
||||
harness.ok(exception, "Should have thrown.")
|
||||
harness.check(exception.message,
|
||||
"[Cached] and [StoreInSlot] must not be used on an attribute "
|
||||
"whose type contains a [ChromeOnly] dictionary member",
|
||||
"Should have thrown the right exception")
|
||||
|
||||
parser = parser.reset()
|
||||
exception = None
|
||||
try:
|
||||
parser.parse("""
|
||||
dictionary ParentDict {
|
||||
[ChromeOnly] any bar;
|
||||
};
|
||||
|
||||
dictionary Dict : ParentDict {
|
||||
any foo;
|
||||
};
|
||||
|
||||
interface Iface {
|
||||
[Constant, Cached] readonly attribute Dict dict;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except Exception, exception:
|
||||
pass
|
||||
|
||||
harness.ok(exception, "Should have thrown (2).")
|
||||
harness.check(exception.message,
|
||||
"[Cached] and [StoreInSlot] must not be used on an attribute "
|
||||
"whose type contains a [ChromeOnly] dictionary member",
|
||||
"Should have thrown the right exception (2)")
|
||||
|
||||
parser = parser.reset()
|
||||
exception = None
|
||||
try:
|
||||
parser.parse("""
|
||||
dictionary GrandParentDict {
|
||||
[ChromeOnly] any baz;
|
||||
};
|
||||
|
||||
dictionary ParentDict : GrandParentDict {
|
||||
any bar;
|
||||
};
|
||||
|
||||
dictionary Dict : ParentDict {
|
||||
any foo;
|
||||
};
|
||||
|
||||
interface Iface {
|
||||
[Constant, Cached] readonly attribute Dict dict;
|
||||
};
|
||||
""")
|
||||
results = parser.finish()
|
||||
except Exception, exception:
|
||||
pass
|
||||
|
||||
harness.ok(exception, "Should have thrown (3).")
|
||||
harness.check(exception.message,
|
||||
"[Cached] and [StoreInSlot] must not be used on an attribute "
|
||||
"whose type contains a [ChromeOnly] dictionary member",
|
||||
"Should have thrown the right exception (3)")
|
|
@ -0,0 +1,223 @@
|
|||
def WebIDLTest(parser, harness):
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
attribute any foo;
|
||||
any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
harness.check(len(results), 1, "Should have a thing.")
|
||||
harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
|
||||
harness.check(len(results[0].members), 2,
|
||||
"Should have two things in our namespace")
|
||||
harness.ok(results[0].members[0].isAttr(), "First member is attribute")
|
||||
harness.ok(results[0].members[0].isStatic(), "Attribute should be static")
|
||||
harness.ok(results[0].members[1].isMethod(), "Second member is method")
|
||||
harness.ok(results[0].members[1].isStatic(), "Operation should be static")
|
||||
|
||||
parser = parser.reset()
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
attribute any foo;
|
||||
};
|
||||
partial namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
harness.check(len(results), 2, "Should have things.")
|
||||
harness.ok(results[0].isNamespace(), "Our thing should be a namespace");
|
||||
harness.check(len(results[0].members), 2,
|
||||
"Should have two things in our namespace")
|
||||
harness.ok(results[0].members[0].isAttr(), "First member is attribute")
|
||||
harness.ok(results[0].members[0].isStatic(), "Attribute should be static");
|
||||
harness.ok(results[0].members[1].isMethod(), "Second member is method")
|
||||
harness.ok(results[0].members[1].isStatic(), "Operation should be static");
|
||||
|
||||
parser = parser.reset()
|
||||
parser.parse(
|
||||
"""
|
||||
partial namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
namespace MyNamespace {
|
||||
attribute any foo;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
harness.check(len(results), 2, "Should have things.")
|
||||
harness.ok(results[1].isNamespace(), "Our thing should be a namespace");
|
||||
harness.check(len(results[1].members), 2,
|
||||
"Should have two things in our namespace")
|
||||
harness.ok(results[1].members[0].isAttr(), "First member is attribute")
|
||||
harness.ok(results[1].members[0].isStatic(), "Attribute should be static");
|
||||
harness.ok(results[1].members[1].isMethod(), "Second member is method")
|
||||
harness.ok(results[1].members[1].isStatic(), "Operation should be static");
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
static attribute any foo;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
static any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
|
||||
interface MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
|
||||
namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
|
||||
namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
partial namespace MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
|
||||
interface MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
|
||||
partial interface MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
partial interface MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
|
||||
namespace MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
||||
|
||||
parser = parser.reset()
|
||||
threw = False
|
||||
try:
|
||||
parser.parse(
|
||||
"""
|
||||
interface MyNamespace {
|
||||
any bar();
|
||||
};
|
||||
|
||||
partial namespace MyNamespace {
|
||||
any baz();
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
except Exception, x:
|
||||
threw = True
|
||||
harness.ok(threw, "Should have thrown.")
|
|
@ -1424,6 +1424,19 @@ public:
|
|||
virtual nsISupports* GetParentObject();
|
||||
};
|
||||
|
||||
class TestNamespace {
|
||||
public:
|
||||
static bool Foo(const GlobalObject&);
|
||||
static int32_t Bar(const GlobalObject&);
|
||||
static void Baz(const GlobalObject&);
|
||||
};
|
||||
|
||||
class TestRenamedNamespace {
|
||||
};
|
||||
|
||||
class TestProtoObjectHackedNamespace {
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -1217,6 +1217,23 @@ interface TestDeprecatedInterface {
|
|||
interface TestInterfaceWithPromiseConstructorArg {
|
||||
};
|
||||
|
||||
namespace TestNamespace {
|
||||
readonly attribute boolean foo;
|
||||
long bar();
|
||||
};
|
||||
|
||||
partial namespace TestNamespace {
|
||||
void baz();
|
||||
};
|
||||
|
||||
[ClassString="RenamedNamespaceClassName"]
|
||||
namespace TestRenamedNamespace {
|
||||
};
|
||||
|
||||
[ProtoObjectHack]
|
||||
namespace TestProtoObjectHackedNamespace {
|
||||
};
|
||||
|
||||
[SecureContext]
|
||||
interface TestSecureContextInterface {
|
||||
static void alsoSecureContext();
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "ImageBitmapColorUtils.h"
|
||||
#include "ImageBitmapUtils.h"
|
||||
#include "ImageUtils.h"
|
||||
#include "imgTools.h"
|
||||
#include "libyuv.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
|
@ -395,8 +397,10 @@ ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
|
|||
: mParent(aGlobal)
|
||||
, mData(aData)
|
||||
, mSurface(nullptr)
|
||||
, mDataWrapper(new ImageUtils(mData))
|
||||
, mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height)
|
||||
, mIsPremultipliedAlpha(aIsPremultipliedAlpha)
|
||||
, mIsCroppingAreaOutSideOfSourceImage(false)
|
||||
{
|
||||
MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
|
||||
}
|
||||
|
@ -425,6 +429,97 @@ ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
|
|||
mPictureRect = FixUpNegativeDimension(aRect, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
ImageBitmap::SetIsCroppingAreaOutSideOfSourceImage(const IntSize& aSourceSize,
|
||||
const Maybe<IntRect>& aCroppingRect)
|
||||
{
|
||||
// No cropping at all.
|
||||
if (aCroppingRect.isNothing()) {
|
||||
mIsCroppingAreaOutSideOfSourceImage = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aCroppingRect->X() < 0 || aCroppingRect->Y() < 0 ||
|
||||
aCroppingRect->Width() > aSourceSize.width ||
|
||||
aCroppingRect->Height() > aSourceSize.height) {
|
||||
mIsCroppingAreaOutSideOfSourceImage = true;
|
||||
}
|
||||
}
|
||||
|
||||
static already_AddRefed<SourceSurface>
|
||||
ConvertColorFormatIfNeeded(RefPtr<SourceSurface> aSurface)
|
||||
{
|
||||
const SurfaceFormat srcFormat = aSurface->GetFormat();
|
||||
if (srcFormat == SurfaceFormat::R8G8B8A8 ||
|
||||
srcFormat == SurfaceFormat::B8G8R8A8 ||
|
||||
srcFormat == SurfaceFormat::R8G8B8X8 ||
|
||||
srcFormat == SurfaceFormat::B8G8R8X8 ||
|
||||
srcFormat == SurfaceFormat::A8R8G8B8 ||
|
||||
srcFormat == SurfaceFormat::X8R8G8B8) {
|
||||
return aSurface.forget();
|
||||
}
|
||||
|
||||
if (srcFormat == SurfaceFormat::A8 ||
|
||||
srcFormat == SurfaceFormat::Depth) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int bytesPerPixel = BytesPerPixel(SurfaceFormat::B8G8R8A8);
|
||||
const IntSize dstSize = aSurface->GetSize();
|
||||
const uint32_t dstStride = dstSize.width * bytesPerPixel;
|
||||
|
||||
RefPtr<DataSourceSurface> dstDataSurface =
|
||||
Factory::CreateDataSourceSurfaceWithStride(dstSize,
|
||||
SurfaceFormat::B8G8R8A8,
|
||||
dstStride);
|
||||
|
||||
RefPtr<DataSourceSurface> srcDataSurface = aSurface->GetDataSurface();
|
||||
if (NS_WARN_IF(!srcDataSurface)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataSourceSurface::ScopedMap srcMap(srcDataSurface, DataSourceSurface::READ);
|
||||
DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
|
||||
if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int rv = 0;
|
||||
if (srcFormat == SurfaceFormat::R8G8B8) {
|
||||
rv = RGB24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
|
||||
dstMap.GetData(), dstMap.GetStride(),
|
||||
dstSize.width, dstSize.height);
|
||||
} else if (srcFormat == SurfaceFormat::B8G8R8) {
|
||||
rv = BGR24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
|
||||
dstMap.GetData(), dstMap.GetStride(),
|
||||
dstSize.width, dstSize.height);
|
||||
} else if (srcFormat == SurfaceFormat::HSV) {
|
||||
rv = HSVToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
|
||||
dstMap.GetData(), dstMap.GetStride(),
|
||||
dstSize.width, dstSize.height);
|
||||
} else if (srcFormat == SurfaceFormat::Lab) {
|
||||
rv = LabToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
|
||||
dstMap.GetData(), dstMap.GetStride(),
|
||||
dstSize.width, dstSize.height);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(rv != 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dstDataSurface.forget();
|
||||
}
|
||||
|
||||
/*
|
||||
* The functionality of PrepareForDrawTarget method:
|
||||
* (1) Get a SourceSurface from the mData (which is a layers::Image).
|
||||
* (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
|
||||
* R8G8B8, B8G8R8, HSV or Lab.
|
||||
* Note: if the original format is A8 or Depth, then return null directly.
|
||||
* (3) Do cropping if the size of SourceSurface does not equal to the
|
||||
* mPictureRect.
|
||||
* (4) Pre-multiply alpha if needed.
|
||||
*/
|
||||
already_AddRefed<SourceSurface>
|
||||
ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
|
||||
{
|
||||
|
@ -442,6 +537,14 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
|
|||
}
|
||||
}
|
||||
|
||||
// Check if we need to convert the format.
|
||||
// Convert R8G8B8/B8G8R8/HSV/Lab to B8G8R8A8.
|
||||
// Return null if the original format is A8 or Depth.
|
||||
mSurface = ConvertColorFormatIfNeeded(mSurface);
|
||||
if (NS_WARN_IF(!mSurface)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> target = aTarget;
|
||||
IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
|
||||
|
||||
|
@ -481,7 +584,12 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
|
|||
|
||||
// Pre-multiply alpha here.
|
||||
// Apply pre-multiply alpha only if mIsPremultipliedAlpha is false.
|
||||
if (!mIsPremultipliedAlpha) {
|
||||
// Ignore this step if the source surface does not have alpha channel; this
|
||||
// kind of source surfaces might come form layers::PlanarYCbCrImage.
|
||||
if (!mIsPremultipliedAlpha &&
|
||||
mSurface->GetFormat() != SurfaceFormat::B8G8R8X8 &&
|
||||
mSurface->GetFormat() != SurfaceFormat::R8G8B8X8 &&
|
||||
mSurface->GetFormat() != SurfaceFormat::X8R8G8B8) {
|
||||
MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
|
||||
mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
|
||||
mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
|
||||
|
@ -581,6 +689,7 @@ ImageBitmap::ToCloneData()
|
|||
ImageBitmapCloneData* result = new ImageBitmapCloneData();
|
||||
result->mPictureRect = mPictureRect;
|
||||
result->mIsPremultipliedAlpha = mIsPremultipliedAlpha;
|
||||
result->mIsCroppingAreaOutSideOfSourceImage = mIsCroppingAreaOutSideOfSourceImage;
|
||||
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
|
||||
result->mSurface = surface->GetDataSurface();
|
||||
MOZ_ASSERT(result->mSurface);
|
||||
|
@ -592,11 +701,14 @@ ImageBitmap::ToCloneData()
|
|||
ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
|
||||
ImageBitmapCloneData* aData)
|
||||
{
|
||||
RefPtr<layers::Image> data =
|
||||
CreateImageFromSurface(aData->mSurface);
|
||||
RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
|
||||
|
||||
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data,
|
||||
aData->mIsPremultipliedAlpha);
|
||||
|
||||
ret->mIsCroppingAreaOutSideOfSourceImage =
|
||||
aData->mIsCroppingAreaOutSideOfSourceImage;
|
||||
|
||||
ErrorResult rv;
|
||||
ret->SetPictureRect(aData->mPictureRect, rv);
|
||||
return ret.forget();
|
||||
|
@ -670,6 +782,9 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl
|
|||
ret->SetPictureRect(aCropRect.ref(), aRv);
|
||||
}
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
|
@ -715,6 +830,9 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl
|
|||
ret->SetPictureRect(aCropRect.ref(), aRv);
|
||||
}
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
ret->SetIsCroppingAreaOutSideOfSourceImage(data->GetSize(), aCropRect);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
|
@ -775,6 +893,9 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
|
|||
ret->SetPictureRect(cropRect, aRv);
|
||||
}
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
|
@ -833,6 +954,9 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageData& aImageData,
|
|||
// The cropping information has been handled in the CreateImageFromRawData()
|
||||
// function.
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
ret->SetIsCroppingAreaOutSideOfSourceImage(imageSize, aCropRect);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
|
@ -873,6 +997,9 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, CanvasRenderingContext2D&
|
|||
ret->SetPictureRect(aCropRect.ref(), aRv);
|
||||
}
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
ret->SetIsCroppingAreaOutSideOfSourceImage(surface->GetSize(), aCropRect);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
|
@ -893,6 +1020,14 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
|
|||
ret->SetPictureRect(aCropRect.ref(), aRv);
|
||||
}
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
if (aImageBitmap.mIsCroppingAreaOutSideOfSourceImage == true) {
|
||||
ret->mIsCroppingAreaOutSideOfSourceImage = true;
|
||||
} else {
|
||||
ret->SetIsCroppingAreaOutSideOfSourceImage(aImageBitmap.mPictureRect.Size(),
|
||||
aCropRect);
|
||||
}
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
|
@ -1006,7 +1141,8 @@ DecodeBlob(Blob& aBlob)
|
|||
}
|
||||
|
||||
static already_AddRefed<layers::Image>
|
||||
DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect)
|
||||
DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect,
|
||||
/*Output*/ IntSize& sourceSize)
|
||||
{
|
||||
// Decode the blob into a SourceSurface.
|
||||
RefPtr<SourceSurface> surface = DecodeBlob(aBlob);
|
||||
|
@ -1015,6 +1151,9 @@ DecodeAndCropBlob(Blob& aBlob, Maybe<IntRect>& aCropRect)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Set the _sourceSize_ output parameter.
|
||||
sourceSize = surface->GetSize();
|
||||
|
||||
// Crop the source surface if needed.
|
||||
RefPtr<SourceSurface> croppedSurface = surface;
|
||||
|
||||
|
@ -1127,7 +1266,15 @@ public:
|
|||
private:
|
||||
already_AddRefed<ImageBitmap> CreateImageBitmap() override
|
||||
{
|
||||
RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect);
|
||||
// _sourceSize_ is used to get the original size of the source image,
|
||||
// before being cropped.
|
||||
IntSize sourceSize;
|
||||
|
||||
// Keep the orignal cropping rectangle because the mCropRect might be
|
||||
// modified in DecodeAndCropBlob().
|
||||
Maybe<IntRect> originalCropRect = mCropRect;
|
||||
|
||||
RefPtr<layers::Image> data = DecodeAndCropBlob(*mBlob, mCropRect, sourceSize);
|
||||
|
||||
if (NS_WARN_IF(!data)) {
|
||||
mPromise->MaybeRejectWithNull();
|
||||
|
@ -1136,6 +1283,10 @@ private:
|
|||
|
||||
// Create ImageBitmap object.
|
||||
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
|
||||
|
||||
return imageBitmap.forget();
|
||||
}
|
||||
};
|
||||
|
@ -1150,18 +1301,20 @@ class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnabl
|
|||
DecodeBlobInMainThreadSyncTask(WorkerPrivate* aWorkerPrivate,
|
||||
Blob& aBlob,
|
||||
Maybe<IntRect>& aCropRect,
|
||||
layers::Image** aImage)
|
||||
layers::Image** aImage,
|
||||
IntSize& aSourceSize)
|
||||
: WorkerMainThreadRunnable(aWorkerPrivate,
|
||||
NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Blob"))
|
||||
, mBlob(aBlob)
|
||||
, mCropRect(aCropRect)
|
||||
, mImage(aImage)
|
||||
, mSourceSize(aSourceSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool MainThreadRun() override
|
||||
{
|
||||
RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect);
|
||||
RefPtr<layers::Image> image = DecodeAndCropBlob(mBlob, mCropRect, mSourceSize);
|
||||
|
||||
if (NS_WARN_IF(!image)) {
|
||||
return true;
|
||||
|
@ -1176,6 +1329,7 @@ class CreateImageBitmapFromBlobWorkerTask final : public WorkerSameThreadRunnabl
|
|||
Blob& mBlob;
|
||||
Maybe<IntRect>& mCropRect;
|
||||
layers::Image** mImage;
|
||||
IntSize mSourceSize;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -1196,12 +1350,20 @@ public:
|
|||
private:
|
||||
already_AddRefed<ImageBitmap> CreateImageBitmap() override
|
||||
{
|
||||
// _sourceSize_ is used to get the original size of the source image,
|
||||
// before being cropped.
|
||||
IntSize sourceSize;
|
||||
|
||||
// Keep the orignal cropping rectangle because the mCropRect might be
|
||||
// modified in DecodeAndCropBlob().
|
||||
Maybe<IntRect> originalCropRect = mCropRect;
|
||||
|
||||
RefPtr<layers::Image> data;
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<DecodeBlobInMainThreadSyncTask> task =
|
||||
new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect,
|
||||
getter_AddRefs(data));
|
||||
getter_AddRefs(data), sourceSize);
|
||||
task->Dispatch(rv); // This is a synchronous call.
|
||||
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
|
@ -1217,6 +1379,10 @@ private:
|
|||
|
||||
// Create ImageBitmap object.
|
||||
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(mGlobalObject, data);
|
||||
|
||||
// Set mIsCroppingAreaOutSideOfSourceImage.
|
||||
imageBitmap->SetIsCroppingAreaOutSideOfSourceImage(sourceSize, originalCropRect);
|
||||
|
||||
return imageBitmap.forget();
|
||||
}
|
||||
|
||||
|
@ -1306,11 +1472,12 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
|
|||
uint32_t picRectWidth_;
|
||||
uint32_t picRectHeight_;
|
||||
uint32_t isPremultipliedAlpha_;
|
||||
uint32_t dummy_;
|
||||
uint32_t isCroppingAreaOutSideOfSourceImage_;
|
||||
|
||||
if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
|
||||
!JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
|
||||
!JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_, &dummy_)) {
|
||||
!JS_ReadUint32Pair(aReader, &isPremultipliedAlpha_,
|
||||
&isCroppingAreaOutSideOfSourceImage_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -1334,6 +1501,9 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
|
|||
RefPtr<ImageBitmap> imageBitmap =
|
||||
new ImageBitmap(aParent, img, isPremultipliedAlpha_);
|
||||
|
||||
imageBitmap->mIsCroppingAreaOutSideOfSourceImage =
|
||||
isCroppingAreaOutSideOfSourceImage_;
|
||||
|
||||
ErrorResult error;
|
||||
imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
|
||||
picRectWidth, picRectHeight), error);
|
||||
|
@ -1363,6 +1533,7 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
|
|||
const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
|
||||
const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
|
||||
const uint32_t isPremultipliedAlpha = aImageBitmap->mIsPremultipliedAlpha ? 1 : 0;
|
||||
const uint32_t isCroppingAreaOutSideOfSourceImage = aImageBitmap->mIsCroppingAreaOutSideOfSourceImage ? 1 : 0;
|
||||
|
||||
// Indexing the cloned surfaces and send the index to the receiver.
|
||||
uint32_t index = aClonedSurfaces.Length();
|
||||
|
@ -1370,7 +1541,8 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
|
|||
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
|
||||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
|
||||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
|
||||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha, 0))) {
|
||||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, isPremultipliedAlpha,
|
||||
isCroppingAreaOutSideOfSourceImage))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1395,5 +1567,554 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
|
|||
return true;
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
ImageBitmap::ExtensionsEnabled(JSContext* aCx, JSObject*)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("canvas.imagebitmap_extensions.enabled");
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
return workerPrivate->ImageBitmapExtensionsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
// ImageBitmap extensions.
|
||||
ImageBitmapFormat
|
||||
ImageBitmap::FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
|
||||
|
||||
ImageBitmapFormat platformFormat = mDataWrapper->GetFormat();
|
||||
|
||||
if (!aPossibleFormats.WasPassed() ||
|
||||
aPossibleFormats.Value().Contains(platformFormat)) {
|
||||
return platformFormat;
|
||||
} else {
|
||||
// If no matching is found, FindBestMatchingFromat() returns
|
||||
// ImageBitmapFormat::EndGuard_ and we throw an exception.
|
||||
ImageBitmapFormat optimalFormat =
|
||||
FindBestMatchingFromat(platformFormat, aPossibleFormats.Value());
|
||||
|
||||
if (optimalFormat == ImageBitmapFormat::EndGuard_) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
return optimalFormat;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
ImageBitmap::MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
|
||||
|
||||
if (aFormat == mDataWrapper->GetFormat()) {
|
||||
return mDataWrapper->GetBufferLength();
|
||||
} else {
|
||||
return CalculateImageBufferSize(aFormat, Width(), Height());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class MapDataIntoBufferSource
|
||||
{
|
||||
protected:
|
||||
MapDataIntoBufferSource(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const T& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
: mPromise(aPromise)
|
||||
, mImageBitmap(aImageBitmap)
|
||||
, mBuffer(aCx, aBuffer.Obj())
|
||||
, mOffset(aOffset)
|
||||
, mFormat(aFormat)
|
||||
{
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(JS_IsArrayBufferObject(mBuffer) ||
|
||||
JS_IsArrayBufferViewObject(mBuffer));
|
||||
}
|
||||
|
||||
virtual ~MapDataIntoBufferSource() = default;
|
||||
|
||||
void DoMapDataIntoBufferSource()
|
||||
{
|
||||
ErrorResult error;
|
||||
|
||||
// Prepare destination buffer.
|
||||
uint8_t* bufferData = nullptr;
|
||||
uint32_t bufferLength = 0;
|
||||
bool isSharedMemory = false;
|
||||
if (JS_IsArrayBufferObject(mBuffer)) {
|
||||
js::GetArrayBufferLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
|
||||
} else if (JS_IsArrayBufferViewObject(mBuffer)) {
|
||||
js::GetArrayBufferViewLengthAndData(mBuffer, &bufferLength, &isSharedMemory, &bufferData);
|
||||
} else {
|
||||
error.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!bufferData) || NS_WARN_IF(!bufferLength)) {
|
||||
error.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check length.
|
||||
const int32_t neededBufferLength =
|
||||
mImageBitmap->MappedDataLength(mFormat, error);
|
||||
|
||||
if (((int32_t)bufferLength - mOffset) < neededBufferLength) {
|
||||
error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call ImageBitmapFormatUtils.
|
||||
UniquePtr<ImagePixelLayout> layout =
|
||||
mImageBitmap->mDataWrapper->MapDataInto(bufferData,
|
||||
mOffset,
|
||||
bufferLength,
|
||||
mFormat,
|
||||
error);
|
||||
|
||||
if (NS_WARN_IF(!layout)) {
|
||||
mPromise->MaybeReject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
mPromise->MaybeResolve(*layout);
|
||||
}
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
RefPtr<ImageBitmap> mImageBitmap;
|
||||
JS::PersistentRooted<JSObject*> mBuffer;
|
||||
int32_t mOffset;
|
||||
ImageBitmapFormat mFormat;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MapDataIntoBufferSourceTask final : public Runnable,
|
||||
public MapDataIntoBufferSource<T>
|
||||
{
|
||||
public:
|
||||
MapDataIntoBufferSourceTask(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const T& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
: MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MapDataIntoBufferSourceTask() = default;
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class MapDataIntoBufferSourceWorkerTask final : public WorkerSameThreadRunnable,
|
||||
public MapDataIntoBufferSource<T>
|
||||
{
|
||||
public:
|
||||
MapDataIntoBufferSourceWorkerTask(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const T& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
: WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
|
||||
MapDataIntoBufferSource<T>(aCx, aPromise, aImageBitmap, aBuffer, aOffset, aFormat)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MapDataIntoBufferSourceWorkerTask() = default;
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MapDataIntoBufferSource<T>::DoMapDataIntoBufferSource();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AsyncMapDataIntoBufferSource(JSContext* aCx,
|
||||
Promise *aPromise,
|
||||
ImageBitmap *aImageBitmap,
|
||||
const ArrayBufferViewOrArrayBuffer& aBuffer,
|
||||
int32_t aOffset,
|
||||
ImageBitmapFormat aFormat)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(aImageBitmap);
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIRunnable> task;
|
||||
|
||||
if (aBuffer.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
||||
task = new MapDataIntoBufferSourceTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
|
||||
} else if (aBuffer.IsArrayBufferView()) {
|
||||
const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
|
||||
task = new MapDataIntoBufferSourceTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
|
||||
}
|
||||
|
||||
NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
|
||||
} else {
|
||||
RefPtr<WorkerSameThreadRunnable> task;
|
||||
|
||||
if (aBuffer.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
||||
task = new MapDataIntoBufferSourceWorkerTask<ArrayBuffer>(aCx, aPromise, aImageBitmap, buffer, aOffset, aFormat);
|
||||
} else if (aBuffer.IsArrayBufferView()) {
|
||||
const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
|
||||
task = new MapDataIntoBufferSourceWorkerTask<ArrayBufferView>(aCx, aPromise, aImageBitmap, bufferView, aOffset, aFormat);
|
||||
}
|
||||
|
||||
task->Dispatch(); // Actually, to the current worker-thread.
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ImageBitmap::MapDataInto(JSContext* aCx,
|
||||
ImageBitmapFormat aFormat,
|
||||
const ArrayBufferViewOrArrayBuffer& aBuffer,
|
||||
int32_t aOffset, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(mDataWrapper, "No ImageBitmapFormatUtils functionalities.");
|
||||
MOZ_ASSERT(aCx, "No JSContext while calling ImageBitmap::MapDataInto().");
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(mParent, aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check for cases that should throws.
|
||||
// Case 1:
|
||||
// If image bitmap was cropped to the source rectangle so that it contains any
|
||||
// transparent black pixels (cropping area is outside of the source image),
|
||||
// then reject promise with IndexSizeError and abort these steps.
|
||||
if (mIsCroppingAreaOutSideOfSourceImage) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Case 2:
|
||||
// If the image bitmap is going to be accessed in YUV422/YUV422 series with a
|
||||
// cropping area starts at an odd x or y coordinate.
|
||||
if (aFormat == ImageBitmapFormat::YUV422P ||
|
||||
aFormat == ImageBitmapFormat::YUV420P ||
|
||||
aFormat == ImageBitmapFormat::YUV420SP_NV12 ||
|
||||
aFormat == ImageBitmapFormat::YUV420SP_NV21) {
|
||||
if ((mPictureRect.x & 1) || (mPictureRect.y & 1)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
}
|
||||
|
||||
AsyncMapDataIntoBufferSource(aCx, promise, this, aBuffer, aOffset, aFormat);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// ImageBitmapFactories extensions.
|
||||
static SurfaceFormat
|
||||
ImageFormatToSurfaceFromat(mozilla::dom::ImageBitmapFormat aFormat)
|
||||
{
|
||||
switch(aFormat) {
|
||||
case ImageBitmapFormat::RGBA32:
|
||||
return SurfaceFormat::R8G8B8A8;
|
||||
case ImageBitmapFormat::BGRA32:
|
||||
return SurfaceFormat::B8G8R8A8;
|
||||
case ImageBitmapFormat::RGB24:
|
||||
return SurfaceFormat::R8G8B8;
|
||||
case ImageBitmapFormat::BGR24:
|
||||
return SurfaceFormat::B8G8R8;
|
||||
case ImageBitmapFormat::GRAY8:
|
||||
return SurfaceFormat::A8;
|
||||
case ImageBitmapFormat::HSV:
|
||||
return SurfaceFormat::HSV;
|
||||
case ImageBitmapFormat::Lab:
|
||||
return SurfaceFormat::Lab;
|
||||
case ImageBitmapFormat::DEPTH:
|
||||
return SurfaceFormat::Depth;
|
||||
default:
|
||||
return SurfaceFormat::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static already_AddRefed<layers::Image>
|
||||
CreateImageFromBufferSourceRawData(const uint8_t*aBufferData,
|
||||
uint32_t aBufferLength,
|
||||
mozilla::dom::ImageBitmapFormat aFormat,
|
||||
const Sequence<ChannelPixelLayout>& aLayout)
|
||||
{
|
||||
MOZ_ASSERT(aBufferData);
|
||||
MOZ_ASSERT(aBufferLength > 0);
|
||||
|
||||
switch(aFormat) {
|
||||
case ImageBitmapFormat::RGBA32:
|
||||
case ImageBitmapFormat::BGRA32:
|
||||
case ImageBitmapFormat::RGB24:
|
||||
case ImageBitmapFormat::BGR24:
|
||||
case ImageBitmapFormat::GRAY8:
|
||||
case ImageBitmapFormat::HSV:
|
||||
case ImageBitmapFormat::Lab:
|
||||
case ImageBitmapFormat::DEPTH:
|
||||
{
|
||||
const nsTArray<ChannelPixelLayout>& channels = aLayout;
|
||||
MOZ_ASSERT(channels.Length() != 0, "Empty Channels.");
|
||||
|
||||
const SurfaceFormat srcFormat = ImageFormatToSurfaceFromat(aFormat);
|
||||
const uint32_t srcStride = channels[0].mStride;
|
||||
const IntSize srcSize(channels[0].mWidth, channels[0].mHeight);
|
||||
|
||||
RefPtr<DataSourceSurface> dstDataSurface =
|
||||
Factory::CreateDataSourceSurfaceWithStride(srcSize, srcFormat, srcStride);
|
||||
|
||||
if (NS_WARN_IF(!dstDataSurface)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Copy the raw data into the newly created DataSourceSurface.
|
||||
DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
|
||||
if (NS_WARN_IF(!dstMap.IsMapped())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint8_t* srcBufferPtr = aBufferData;
|
||||
uint8_t* dstBufferPtr = dstMap.GetData();
|
||||
|
||||
for (int i = 0; i < srcSize.height; ++i) {
|
||||
memcpy(dstBufferPtr, srcBufferPtr, srcStride);
|
||||
srcBufferPtr += srcStride;
|
||||
dstBufferPtr += dstMap.GetStride();
|
||||
}
|
||||
|
||||
// Create an Image from the BGRA SourceSurface.
|
||||
RefPtr<SourceSurface> surface = dstDataSurface;
|
||||
RefPtr<layers::Image> image = CreateImageFromSurface(surface);
|
||||
|
||||
if (NS_WARN_IF(!image)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return image.forget();
|
||||
}
|
||||
case ImageBitmapFormat::YUV444P:
|
||||
case ImageBitmapFormat::YUV422P:
|
||||
case ImageBitmapFormat::YUV420P:
|
||||
case ImageBitmapFormat::YUV420SP_NV12:
|
||||
case ImageBitmapFormat::YUV420SP_NV21:
|
||||
{
|
||||
// Prepare the PlanarYCbCrData.
|
||||
const ChannelPixelLayout& yLayout = aLayout[0];
|
||||
const ChannelPixelLayout& uLayout = aFormat != ImageBitmapFormat::YUV420SP_NV21 ? aLayout[1] : aLayout[2];
|
||||
const ChannelPixelLayout& vLayout = aFormat != ImageBitmapFormat::YUV420SP_NV21 ? aLayout[2] : aLayout[1];
|
||||
|
||||
layers::PlanarYCbCrData data;
|
||||
|
||||
// Luminance buffer
|
||||
data.mYChannel = const_cast<uint8_t*>(aBufferData + yLayout.mOffset);
|
||||
data.mYStride = yLayout.mStride;
|
||||
data.mYSize = gfx::IntSize(yLayout.mWidth, yLayout.mHeight);
|
||||
data.mYSkip = yLayout.mSkip;
|
||||
|
||||
// Chroma buffers
|
||||
data.mCbChannel = const_cast<uint8_t*>(aBufferData + uLayout.mOffset);
|
||||
data.mCrChannel = const_cast<uint8_t*>(aBufferData + vLayout.mOffset);
|
||||
data.mCbCrStride = uLayout.mStride;
|
||||
data.mCbCrSize = gfx::IntSize(uLayout.mWidth, uLayout.mHeight);
|
||||
data.mCbSkip = uLayout.mSkip;
|
||||
data.mCrSkip = vLayout.mSkip;
|
||||
|
||||
// Picture rectangle.
|
||||
// We set the picture rectangle to exactly the size of the source image to
|
||||
// keep the full original data.
|
||||
data.mPicX = 0;
|
||||
data.mPicY = 0;
|
||||
data.mPicSize = data.mYSize;
|
||||
|
||||
// Create a layers::Image and set data.
|
||||
if (aFormat == ImageBitmapFormat::YUV444P ||
|
||||
aFormat == ImageBitmapFormat::YUV422P ||
|
||||
aFormat == ImageBitmapFormat::YUV420P) {
|
||||
RefPtr<layers::PlanarYCbCrImage> image =
|
||||
new layers::RecyclingPlanarYCbCrImage(new layers::BufferRecycleBin());
|
||||
|
||||
if (NS_WARN_IF(!image)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set Data.
|
||||
if (NS_WARN_IF(!image->CopyData(data))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return image.forget();
|
||||
} else {
|
||||
RefPtr<layers::NVImage>image = new layers::NVImage();
|
||||
|
||||
if (NS_WARN_IF(!image)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set Data.
|
||||
if (NS_WARN_IF(!image->SetData(data))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return image.forget();
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a synchronous task.
|
||||
* This class is used to create a layers::CairoImage from raw data in the main
|
||||
* thread. While creating an ImageBitmap from an BufferSource, we need to create
|
||||
* a SouceSurface from the BufferSource raw data and then set the SourceSurface
|
||||
* into a layers::CairoImage. However, the layers::CairoImage asserts the
|
||||
* setting operation in the main thread, so if we are going to create an
|
||||
* ImageBitmap from an BufferSource off the main thread, we post an event to the
|
||||
* main thread to create a layers::CairoImage from an BufferSource raw data.
|
||||
*
|
||||
* TODO: Once the layers::CairoImage is constructible off the main thread, which
|
||||
* means the SouceSurface could be released anywhere, we do not need this
|
||||
* task anymore.
|
||||
*/
|
||||
class CreateImageFromBufferSourceRawDataInMainThreadSyncTask final :
|
||||
public WorkerMainThreadRunnable
|
||||
{
|
||||
public:
|
||||
CreateImageFromBufferSourceRawDataInMainThreadSyncTask(const uint8_t* aBuffer,
|
||||
uint32_t aBufferLength,
|
||||
mozilla::dom::ImageBitmapFormat aFormat,
|
||||
const Sequence<ChannelPixelLayout>& aLayout,
|
||||
/*output*/ layers::Image** aImage)
|
||||
: WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
|
||||
NS_LITERAL_CSTRING("ImageBitmap-extensions :: Create Image from BufferSource Raw Data"))
|
||||
, mImage(aImage)
|
||||
, mBuffer(aBuffer)
|
||||
, mBufferLength(aBufferLength)
|
||||
, mFormat(aFormat)
|
||||
, mLayout(aLayout)
|
||||
{
|
||||
MOZ_ASSERT(!(*aImage), "Don't pass an existing Image into CreateImageFromBufferSourceRawDataInMainThreadSyncTask.");
|
||||
}
|
||||
|
||||
bool MainThreadRun() override
|
||||
{
|
||||
RefPtr<layers::Image> image =
|
||||
CreateImageFromBufferSourceRawData(mBuffer, mBufferLength, mFormat, mLayout);
|
||||
|
||||
if (NS_WARN_IF(!image)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
image.forget(mImage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
layers::Image** mImage;
|
||||
const uint8_t* mBuffer;
|
||||
uint32_t mBufferLength;
|
||||
mozilla::dom::ImageBitmapFormat mFormat;
|
||||
const Sequence<ChannelPixelLayout>& mLayout;
|
||||
};
|
||||
|
||||
/*static*/ already_AddRefed<Promise>
|
||||
ImageBitmap::Create(nsIGlobalObject* aGlobal,
|
||||
const ImageBitmapSource& aBuffer,
|
||||
int32_t aOffset, int32_t aLength,
|
||||
mozilla::dom::ImageBitmapFormat aFormat,
|
||||
const Sequence<ChannelPixelLayout>& aLayout,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aGlobal);
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t* bufferData = nullptr;
|
||||
uint32_t bufferLength = 0;
|
||||
|
||||
if (aBuffer.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
||||
buffer.ComputeLengthAndData();
|
||||
bufferData = buffer.Data();
|
||||
bufferLength = buffer.Length();
|
||||
} else if (aBuffer.IsArrayBufferView()) {
|
||||
const ArrayBufferView& bufferView = aBuffer.GetAsArrayBufferView();
|
||||
bufferView.ComputeLengthAndData();
|
||||
bufferData = bufferView.Data();
|
||||
bufferLength = bufferView.Length();
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(bufferData && bufferLength > 0, "Cannot read data from BufferSource.");
|
||||
|
||||
// Check the buffer.
|
||||
if (((uint32_t)(aOffset + aLength) > bufferLength)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Create and Crop the raw data into a layers::Image
|
||||
RefPtr<layers::Image> data;
|
||||
if (NS_IsMainThread()) {
|
||||
data = CreateImageFromBufferSourceRawData(bufferData + aOffset, bufferLength,
|
||||
aFormat, aLayout);
|
||||
} else {
|
||||
RefPtr<CreateImageFromBufferSourceRawDataInMainThreadSyncTask> task =
|
||||
new CreateImageFromBufferSourceRawDataInMainThreadSyncTask(bufferData + aOffset,
|
||||
bufferLength,
|
||||
aFormat,
|
||||
aLayout,
|
||||
getter_AddRefs(data));
|
||||
task->Dispatch(aRv);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!data)) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Create an ImageBimtap.
|
||||
// Assume the data from an external buffer is not alpha-premultiplied.
|
||||
RefPtr<ImageBitmap> imageBitmap = new ImageBitmap(aGlobal, data, false);
|
||||
|
||||
// We don't need to call SetPictureRect() here because there is no cropping
|
||||
// supported and the ImageBitmap's mPictureRect is the size of the source
|
||||
// image in default
|
||||
|
||||
// We don't need to set mIsCroppingAreaOutSideOfSourceImage here because there
|
||||
// is no cropping supported and the mIsCroppingAreaOutSideOfSourceImage is
|
||||
// false in default.
|
||||
|
||||
AsyncFulfillImageBitmapPromise(promise, imageBitmap);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -41,23 +41,29 @@ namespace workers {
|
|||
class WorkerStructuredCloneClosure;
|
||||
}
|
||||
|
||||
class ArrayBufferViewOrArrayBuffer;
|
||||
class CanvasRenderingContext2D;
|
||||
struct ChannelPixelLayout;
|
||||
class CreateImageBitmapFromBlob;
|
||||
class CreateImageBitmapFromBlobTask;
|
||||
class CreateImageBitmapFromBlobWorkerTask;
|
||||
class File;
|
||||
class HTMLCanvasElement;
|
||||
class HTMLImageElement;
|
||||
class HTMLVideoElement;
|
||||
enum class ImageBitmapFormat : uint32_t;
|
||||
class ImageData;
|
||||
class ImageUtils;
|
||||
template<typename T> class MapDataIntoBufferSource;
|
||||
class Promise;
|
||||
class PostMessageEvent; // For StructuredClone between windows.
|
||||
class CreateImageBitmapFromBlob;
|
||||
class CreateImageBitmapFromBlobTask;
|
||||
class CreateImageBitmapFromBlobWorkerTask;
|
||||
|
||||
struct ImageBitmapCloneData final
|
||||
{
|
||||
RefPtr<gfx::DataSourceSurface> mSurface;
|
||||
gfx::IntRect mPictureRect;
|
||||
bool mIsPremultipliedAlpha;
|
||||
bool mIsCroppingAreaOutSideOfSourceImage;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -124,6 +130,14 @@ public:
|
|||
Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
|
||||
const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
Create(nsIGlobalObject* aGlobal,
|
||||
const ImageBitmapSource& aBuffer,
|
||||
int32_t aOffset, int32_t aLength,
|
||||
mozilla::dom::ImageBitmapFormat aFormat,
|
||||
const Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static JSObject*
|
||||
ReadStructuredClone(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
|
@ -136,10 +150,30 @@ public:
|
|||
nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
|
||||
ImageBitmap* aImageBitmap);
|
||||
|
||||
// Mozilla Extensions
|
||||
static bool ExtensionsEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
friend CreateImageBitmapFromBlob;
|
||||
friend CreateImageBitmapFromBlobTask;
|
||||
friend CreateImageBitmapFromBlobWorkerTask;
|
||||
|
||||
template<typename T>
|
||||
friend class MapDataIntoBufferSource;
|
||||
|
||||
// Mozilla Extensions
|
||||
ImageBitmapFormat
|
||||
FindOptimalFormat(const Optional<Sequence<ImageBitmapFormat>>& aPossibleFormats,
|
||||
ErrorResult& aRv);
|
||||
|
||||
int32_t
|
||||
MappedDataLength(ImageBitmapFormat aFormat, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MapDataInto(JSContext* aCx,
|
||||
ImageBitmapFormat aFormat,
|
||||
const ArrayBufferViewOrArrayBuffer& aBuffer,
|
||||
int32_t aOffset, ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
|
||||
/*
|
||||
|
@ -168,6 +202,9 @@ protected:
|
|||
|
||||
void SetPictureRect(const gfx::IntRect& aRect, ErrorResult& aRv);
|
||||
|
||||
void SetIsCroppingAreaOutSideOfSourceImage(const gfx::IntSize& aSourceSize,
|
||||
const Maybe<gfx::IntRect>& aCroppingRect);
|
||||
|
||||
static already_AddRefed<ImageBitmap>
|
||||
CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
|
||||
const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
|
||||
|
@ -211,6 +248,13 @@ protected:
|
|||
RefPtr<layers::Image> mData;
|
||||
RefPtr<gfx::SourceSurface> mSurface;
|
||||
|
||||
/*
|
||||
* This is used in the ImageBitmap-Extensions implementation.
|
||||
* ImageUtils is a wrapper to layers::Image, which add some common methods for
|
||||
* accessing the layers::Image's data.
|
||||
*/
|
||||
UniquePtr<ImageUtils> mDataWrapper;
|
||||
|
||||
/*
|
||||
* The mPictureRect is the size of the source image in default, however, if
|
||||
* users specify the cropping area while creating an ImageBitmap, then this
|
||||
|
@ -225,6 +269,16 @@ protected:
|
|||
gfx::IntRect mPictureRect;
|
||||
|
||||
const bool mIsPremultipliedAlpha;
|
||||
|
||||
/*
|
||||
* Set mIsCroppingAreaOutSideOfSourceImage if image bitmap was cropped to the
|
||||
* source rectangle so that it contains any transparent black pixels (cropping
|
||||
* area is outside of the source image).
|
||||
* This is used in mapDataInto() to check if we should reject promise with
|
||||
* IndexSizeError.
|
||||
*/
|
||||
bool mIsCroppingAreaOutSideOfSourceImage;
|
||||
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,501 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ImageBitmapColorUtils_h
|
||||
#define mozilla_dom_ImageBitmapColorUtils_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/*
|
||||
* RGB family -> RGBA family.
|
||||
*/
|
||||
int
|
||||
RGB24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
int
|
||||
RGB24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* RGBA family -> RGB family.
|
||||
*/
|
||||
int
|
||||
RGBA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
int
|
||||
RGBA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* Among RGB family.
|
||||
*/
|
||||
int
|
||||
RGB24Copy(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
#define BGR24Copy RGB24Copy
|
||||
#define RGB24ToRGB24 RGB24Copy
|
||||
#define BGR24ToBGR24 BGR24Copy
|
||||
|
||||
int
|
||||
RGB24ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
#define BGR24ToRGB24 RGB24ToBGR24
|
||||
|
||||
/*
|
||||
* YUV family -> RGB family.
|
||||
*/
|
||||
int
|
||||
YUV444PToRGB24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV422PToRGB24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV420PToRGB24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV12ToRGB24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUVBuffer, int aUVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV21ToRGB24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aVUBuffer, int aVUStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV444PToBGR24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV422PToBGR24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV420PToBGR24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV12ToBGR24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUVBuffer, int aUVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV21ToBGR24(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aVUBuffer, int aVUStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* YUV family -> RGBA family.
|
||||
*/
|
||||
int
|
||||
YUV444PToRGBA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV422PToRGBA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV420PToRGBA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV12ToRGBA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUVBuffer, int aUVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV21ToRGBA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aVUBuffer, int aVUStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV444PToBGRA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV422PToBGRA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV420PToBGRA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV12ToBGRA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUVBuffer, int aUVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV21ToBGRA32(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aVUBuffer, int aVUStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* RGB family -> YUV family.
|
||||
*/
|
||||
int
|
||||
RGB24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGB24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGB24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
int
|
||||
RGB24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUVBuffer, int aUVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGB24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aVUBuffer, int aVUStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUVBuffer, int aUVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUVBuffer, int aUVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* RGBA family -> YUV family.
|
||||
*/
|
||||
int
|
||||
RGBA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGBA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGBA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
int
|
||||
RGBA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUVBuffer, int aUVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGBA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aVUBuffer, int aVUStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aVBuffer, int aVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
|
||||
int
|
||||
BGRA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUVBuffer, int aUVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aYBuffer, int aYStride,
|
||||
uint8_t* aUVBuffer, int aUVStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* RGBA/RGB family <-> HSV family.
|
||||
*/
|
||||
int
|
||||
RGBA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGB24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
HSVToRGBA32(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
HSVToBGRA32(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
HSVToRGB24(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
HSVToBGR24(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* RGBA/RGB family <-> Lab family.
|
||||
*/
|
||||
int
|
||||
RGBA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGB24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
float* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
LabToRGBA32(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
LabToBGRA32(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
LabToRGB24(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
LabToBGR24(const float* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* RGBA/RGB family -> Gray8.
|
||||
*/
|
||||
int
|
||||
RGB24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGR24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
RGBA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
BGRA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
/*
|
||||
* YUV family -> Gray8.
|
||||
*/
|
||||
int
|
||||
YUV444PToGray8(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV422PToGray8(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
YUV420PToGray8(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
const uint8_t* aVBuffer, int aVStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV12ToGray8(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
int
|
||||
NV21ToGray8(const uint8_t* aYBuffer, int aYStride,
|
||||
const uint8_t* aUBuffer, int aUStride,
|
||||
uint8_t* aDstBuffer, int aDstStride,
|
||||
int aWidth, int aHeight);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
#endif // mozilla_dom_ImageBitmapColorUtils_h
|
|
@ -11,8 +11,8 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
|
||||
// So we don't have to forward declare this elsewhere.
|
||||
class HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmap;
|
||||
typedef HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmap
|
||||
class HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmapOrArrayBufferViewOrArrayBuffer;
|
||||
typedef HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrCanvasRenderingContext2DOrImageBitmapOrArrayBufferViewOrArrayBuffer
|
||||
ImageBitmapSource;
|
||||
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ImageBitmapUtils_h
|
||||
#define mozilla_dom_ImageBitmapUtils_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
struct PlanarYCbCrData;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
struct ChannelPixelLayout;
|
||||
template<typename> class Sequence;
|
||||
|
||||
typedef nsTArray<ChannelPixelLayout> ImagePixelLayout;
|
||||
|
||||
/*
|
||||
* This function creates an ImagePixelLayout object which describes the
|
||||
* default layout of the given ImageBitmapFormat with the given width, height
|
||||
* and stride.
|
||||
*/
|
||||
UniquePtr<ImagePixelLayout>
|
||||
CreateDefaultPixelLayout(ImageBitmapFormat aFormat,
|
||||
uint32_t aWidth, uint32_t aHeight, uint32_t aStride);
|
||||
|
||||
/*
|
||||
* This function extracts information from the aImage parameter to customize
|
||||
* the ImagePixelLayout object, that is, this function creates a customized
|
||||
* ImagePixelLayout object which exactly describes the pixel layout of the
|
||||
* given aImage.
|
||||
*/
|
||||
UniquePtr<ImagePixelLayout>
|
||||
CreatePixelLayoutFromPlanarYCbCrData(const layers::PlanarYCbCrData* aData);
|
||||
|
||||
/*
|
||||
* Get the number of channels of the given ImageBitmapFormat.
|
||||
*/
|
||||
uint8_t
|
||||
GetChannelCountOfImageFormat(ImageBitmapFormat aFormat);
|
||||
|
||||
/*
|
||||
* Get the needed buffer size to store the image data in the given
|
||||
* ImageBitmapFormat with the given width and height.
|
||||
*/
|
||||
uint32_t
|
||||
CalculateImageBufferSize(ImageBitmapFormat aFormat,
|
||||
uint32_t aWidth, uint32_t aHeight);
|
||||
|
||||
/*
|
||||
* This function always copies the image data in _aSrcBuffer_ into _aDstBuffer_
|
||||
* and it also performs color conversion if the _aSrcFormat_ and the
|
||||
* _aDstFormat_ are different.
|
||||
*
|
||||
* The source image is stored in the _aSrcBuffer_ and the corresponding pixel
|
||||
* layout is described by the _aSrcLayout_.
|
||||
*
|
||||
* The copied and converted image will be stored in the _aDstBuffer_, which
|
||||
* should be allocated with enough size before invoking this function and the
|
||||
* needed size could be found by the CalculateImageBufferSize() method.
|
||||
*
|
||||
* The returned ImagePixelLayout object describes the pixel layout of the result
|
||||
* image and will be null if on failure.
|
||||
*/
|
||||
UniquePtr<ImagePixelLayout>
|
||||
CopyAndConvertImageData(ImageBitmapFormat aSrcFormat,
|
||||
const uint8_t* aSrcBuffer,
|
||||
const ImagePixelLayout* aSrcLayout,
|
||||
ImageBitmapFormat aDstFormat,
|
||||
uint8_t* aDstBuffer);
|
||||
|
||||
/*
|
||||
* This function tries to find the best ImageBitmapFormat, from the aCandiates,
|
||||
* which can be converted from the aSrcFormat. The algorithm now merely returns
|
||||
* the FIRST one, from the aCandidates, which can be converted from the
|
||||
* aSrcFormat.
|
||||
*
|
||||
* TODO: The algorithm should be updated after we implement optimizations for
|
||||
* different platforms (different kinds of layers::Image), considering
|
||||
* that some conversion might be cheaper through hardware.
|
||||
*/
|
||||
ImageBitmapFormat
|
||||
FindBestMatchingFromat(ImageBitmapFormat aSrcFormat,
|
||||
const Sequence<ImageBitmapFormat>& aCandidates);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif // mozilla_dom_ImageBitmapUtils_h
|
|
@ -0,0 +1,291 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ImageUtils.h"
|
||||
#include "ImageBitmapUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static ImageBitmapFormat
|
||||
GetImageBitmapFormatFromSurfaceFromat(SurfaceFormat aSurfaceFormat)
|
||||
{
|
||||
switch (aSurfaceFormat) {
|
||||
case SurfaceFormat::B8G8R8A8:
|
||||
case SurfaceFormat::B8G8R8X8:
|
||||
return ImageBitmapFormat::BGRA32;
|
||||
case SurfaceFormat::R8G8B8A8:
|
||||
case SurfaceFormat::R8G8B8X8:
|
||||
return ImageBitmapFormat::RGBA32;
|
||||
case SurfaceFormat::R8G8B8:
|
||||
return ImageBitmapFormat::RGB24;
|
||||
case SurfaceFormat::B8G8R8:
|
||||
return ImageBitmapFormat::BGR24;
|
||||
case SurfaceFormat::HSV:
|
||||
return ImageBitmapFormat::HSV;
|
||||
case SurfaceFormat::Lab:
|
||||
return ImageBitmapFormat::Lab;
|
||||
case SurfaceFormat::Depth:
|
||||
return ImageBitmapFormat::DEPTH;
|
||||
case SurfaceFormat::A8:
|
||||
return ImageBitmapFormat::GRAY8;
|
||||
case SurfaceFormat::R5G6B5_UINT16:
|
||||
case SurfaceFormat::YUV:
|
||||
case SurfaceFormat::NV12:
|
||||
case SurfaceFormat::UNKNOWN:
|
||||
default:
|
||||
return ImageBitmapFormat::EndGuard_;
|
||||
}
|
||||
}
|
||||
|
||||
static ImageBitmapFormat
|
||||
GetImageBitmapFormatFromPlanarYCbCrData(layers::PlanarYCbCrData const *aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
|
||||
if (aData->mYSkip == 0 && aData->mCbSkip == 0 && aData->mCrSkip == 0) { // Possibly three planes.
|
||||
if (aData->mCbChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
|
||||
aData->mCrChannel >= aData->mCbChannel + aData->mCbCrSize.height * aData->mCbCrStride) { // Three planes.
|
||||
if (aData->mYSize.height == aData->mCbCrSize.height) {
|
||||
if (aData->mYSize.width == aData->mCbCrSize.width) {
|
||||
return ImageBitmapFormat::YUV444P;
|
||||
} else if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
|
||||
return ImageBitmapFormat::YUV422P;
|
||||
}
|
||||
} else if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height) {
|
||||
if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
|
||||
return ImageBitmapFormat::YUV420P;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (aData->mYSkip == 0 && aData->mCbSkip == 1 && aData->mCrSkip == 1) { // Possibly two planes.
|
||||
if (aData->mCbChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
|
||||
aData->mCbChannel == aData->mCrChannel - 1) { // Two planes.
|
||||
if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
|
||||
((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
|
||||
return ImageBitmapFormat::YUV420SP_NV12; // Y-Cb-Cr
|
||||
}
|
||||
} else if (aData->mCrChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
|
||||
aData->mCrChannel == aData->mCbChannel - 1) { // Two planes.
|
||||
if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
|
||||
((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
|
||||
return ImageBitmapFormat::YUV420SP_NV21; // Y-Cr-Cb
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ImageBitmapFormat::EndGuard_;
|
||||
}
|
||||
|
||||
// ImageUtils::Impl implements the _generic_ algorithm which always readback
|
||||
// data in RGBA format into CPU memory.
|
||||
// Since layers::CairoImage is just a warpper to a DataSourceSurface, the
|
||||
// implementation of CairoSurfaceImpl is nothing different to the generic
|
||||
// version.
|
||||
class ImageUtils::Impl
|
||||
{
|
||||
public:
|
||||
explicit Impl(layers::Image* aImage)
|
||||
: mImage(aImage)
|
||||
, mSurface(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Impl()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ImageBitmapFormat
|
||||
GetFormat() const
|
||||
{
|
||||
return GetImageBitmapFormatFromSurfaceFromat(Surface()->GetFormat());
|
||||
}
|
||||
|
||||
virtual uint32_t
|
||||
GetBufferLength() const
|
||||
{
|
||||
const uint32_t stride = Surface()->Stride();
|
||||
const IntSize size = Surface()->GetSize();
|
||||
return (uint32_t)(size.height * stride);
|
||||
}
|
||||
|
||||
virtual UniquePtr<ImagePixelLayout>
|
||||
MapDataInto(uint8_t* aBuffer,
|
||||
uint32_t aOffset,
|
||||
uint32_t aBufferLength,
|
||||
ImageBitmapFormat aFormat,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
DataSourceSurface::ScopedMap map(Surface(), DataSourceSurface::READ);
|
||||
if (!map.IsMapped()) {
|
||||
aRv.Throw(NS_ERROR_ILLEGAL_VALUE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Copy or convert data.
|
||||
UniquePtr<ImagePixelLayout> srcLayout =
|
||||
CreateDefaultPixelLayout(GetFormat(), Surface()->GetSize().width,
|
||||
Surface()->GetSize().height, map.GetStride());
|
||||
|
||||
// Prepare destination buffer.
|
||||
uint8_t* dstBuffer = aBuffer + aOffset;
|
||||
UniquePtr<ImagePixelLayout> dstLayout =
|
||||
CopyAndConvertImageData(GetFormat(), map.GetData(), srcLayout.get(),
|
||||
aFormat, dstBuffer);
|
||||
|
||||
if (!dstLayout) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dstLayout;
|
||||
}
|
||||
|
||||
protected:
|
||||
Impl() {}
|
||||
|
||||
DataSourceSurface* Surface() const
|
||||
{
|
||||
if (!mSurface) {
|
||||
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
|
||||
MOZ_ASSERT(surface);
|
||||
|
||||
mSurface = surface->GetDataSurface();
|
||||
MOZ_ASSERT(mSurface);
|
||||
}
|
||||
|
||||
return mSurface.get();
|
||||
}
|
||||
|
||||
RefPtr<layers::Image> mImage;
|
||||
mutable RefPtr<DataSourceSurface> mSurface;
|
||||
};
|
||||
|
||||
// YUVImpl is optimized for the layers::PlanarYCbCrImage and layers::NVImage.
|
||||
// This implementation does not readback data in RGBA format but keep it in YUV
|
||||
// format family.
|
||||
class YUVImpl final : public ImageUtils::Impl
|
||||
{
|
||||
public:
|
||||
explicit YUVImpl(layers::Image* aImage)
|
||||
: Impl(aImage)
|
||||
{
|
||||
MOZ_ASSERT(aImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
|
||||
aImage->GetFormat() == ImageFormat::NV_IMAGE);
|
||||
}
|
||||
|
||||
ImageBitmapFormat GetFormat() const override
|
||||
{
|
||||
return GetImageBitmapFormatFromPlanarYCbCrData(GetPlanarYCbCrData());
|
||||
}
|
||||
|
||||
uint32_t GetBufferLength() const override
|
||||
{
|
||||
if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
|
||||
return mImage->AsPlanarYCbCrImage()->GetDataSize();
|
||||
} else {
|
||||
return mImage->AsNVImage()->GetBufferSize();
|
||||
}
|
||||
}
|
||||
|
||||
UniquePtr<ImagePixelLayout>
|
||||
MapDataInto(uint8_t* aBuffer,
|
||||
uint32_t aOffset,
|
||||
uint32_t aBufferLength,
|
||||
ImageBitmapFormat aFormat,
|
||||
ErrorResult& aRv) const override
|
||||
{
|
||||
// Prepare source buffer and pixel layout.
|
||||
const PlanarYCbCrData* data = GetPlanarYCbCrData();
|
||||
|
||||
UniquePtr<ImagePixelLayout> srcLayout =
|
||||
CreatePixelLayoutFromPlanarYCbCrData(data);
|
||||
|
||||
// Do conversion.
|
||||
UniquePtr<ImagePixelLayout> dstLayout =
|
||||
CopyAndConvertImageData(GetFormat(), data->mYChannel, srcLayout.get(),
|
||||
aFormat, aBuffer+aOffset);
|
||||
|
||||
if (!dstLayout) {
|
||||
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dstLayout;
|
||||
}
|
||||
|
||||
private:
|
||||
const PlanarYCbCrData* GetPlanarYCbCrData() const
|
||||
{
|
||||
if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
|
||||
return mImage->AsPlanarYCbCrImage()->GetData();
|
||||
} else {
|
||||
return mImage->AsNVImage()->GetData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: optimize for other platforms.
|
||||
// For GONK: implement GrallocImageImpl, GrallocPlanarYCbCrImpl and GonkCameraImpl.
|
||||
// For Windows: implement D3D9RGB32TextureImpl and D3D11ShareHandleTextureImpl.
|
||||
// Others: SharedBGRImpl, MACIOSrufaceImpl, GLImageImpl, SurfaceTextureImpl
|
||||
// EGLImageImpl and OverlayImegImpl.
|
||||
|
||||
ImageUtils::ImageUtils(layers::Image* aImage)
|
||||
: mImpl(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aImage, "Create ImageUtils with nullptr.");
|
||||
switch(aImage->GetFormat()) {
|
||||
case mozilla::ImageFormat::PLANAR_YCBCR:
|
||||
case mozilla::ImageFormat::NV_IMAGE:
|
||||
mImpl = new YUVImpl(aImage);
|
||||
break;
|
||||
case mozilla::ImageFormat::CAIRO_SURFACE:
|
||||
default:
|
||||
mImpl = new Impl(aImage);
|
||||
}
|
||||
}
|
||||
|
||||
ImageUtils::~ImageUtils()
|
||||
{
|
||||
if (mImpl) { delete mImpl; mImpl = nullptr; }
|
||||
}
|
||||
|
||||
ImageBitmapFormat
|
||||
ImageUtils::GetFormat() const
|
||||
{
|
||||
MOZ_ASSERT(mImpl);
|
||||
return mImpl->GetFormat();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ImageUtils::GetBufferLength() const
|
||||
{
|
||||
MOZ_ASSERT(mImpl);
|
||||
return mImpl->GetBufferLength();
|
||||
}
|
||||
|
||||
UniquePtr<ImagePixelLayout>
|
||||
ImageUtils::MapDataInto(uint8_t* aBuffer,
|
||||
uint32_t aOffset,
|
||||
uint32_t aBufferLength,
|
||||
ImageBitmapFormat aFormat,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(mImpl);
|
||||
MOZ_ASSERT(aBuffer, "Map data into a null buffer.");
|
||||
return mImpl->MapDataInto(aBuffer, aOffset, aBufferLength, aFormat, aRv);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,73 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ImageBitmapFormatUtils_h
|
||||
#define mozilla_dom_ImageBitmapFormatUtils_h
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
}
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
struct ChannelPixelLayout;
|
||||
enum class ImageBitmapFormat : uint32_t;
|
||||
|
||||
typedef nsTArray<ChannelPixelLayout> ImagePixelLayout;
|
||||
|
||||
/*
|
||||
* ImageUtils is a wrapper around layers::Image. It provides three unified
|
||||
* methods to all sub-classes of layers::Image, which are:
|
||||
*
|
||||
* (1) GetFormat() converts the image's format into ImageBitmapFormat enum.
|
||||
* (2) GetBufferLength() returns the number of bytes that are used to store
|
||||
* the image's underlying raw data.
|
||||
* (3) MapDataInto() writes the image's underlying raw data into a given
|
||||
* ArrayBuffer in the given format. (If the given format is different from
|
||||
* the existing format, the ImageUtils uses the ImageBitmapFormatUtils to
|
||||
* performa color conversion.)
|
||||
*
|
||||
* In theory, the functionalities of this class could be merged into the
|
||||
* interface of layers::Image. However, this is designed as a isolated wrapper
|
||||
* because we don't want to pollute the layers::Image's interface with methods
|
||||
* that are only meaningful to the ImageBitmap.
|
||||
*/
|
||||
class ImageUtils
|
||||
{
|
||||
public:
|
||||
class Impl;
|
||||
ImageUtils() = delete;
|
||||
ImageUtils(const ImageUtils&) = delete;
|
||||
ImageUtils(ImageUtils&&) = delete;
|
||||
ImageUtils& operator=(const ImageUtils&) = delete;
|
||||
ImageUtils& operator=(ImageUtils&&) = delete;
|
||||
|
||||
explicit ImageUtils(layers::Image* aImage);
|
||||
~ImageUtils();
|
||||
|
||||
ImageBitmapFormat GetFormat() const;
|
||||
|
||||
uint32_t GetBufferLength() const;
|
||||
|
||||
UniquePtr<ImagePixelLayout>
|
||||
MapDataInto(uint8_t* aBuffer, uint32_t aOffset, uint32_t aBufferLength,
|
||||
ImageBitmapFormat aFormat, ErrorResult& aRv) const;
|
||||
|
||||
protected:
|
||||
Impl* mImpl;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_ImageBitmapFormatUtils_h */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,16 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestImageBitmapColorUtils.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/canvas',
|
||||
'/media/libyuv/include'
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
|
@ -4,7 +4,10 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['compiledtest']
|
||||
TEST_DIRS += [
|
||||
'compiledtest',
|
||||
'gtest'
|
||||
]
|
||||
|
||||
# Change the following line to avoid bug 1081323 (clobber after changing a manifest):
|
||||
# Add test for webglcontextcreationerror.
|
||||
|
@ -53,11 +56,17 @@ UNIFIED_SOURCES += [
|
|||
'DocumentRendererChild.cpp',
|
||||
'DocumentRendererParent.cpp',
|
||||
'ImageBitmap.cpp',
|
||||
'ImageBitmapColorUtils.cpp',
|
||||
'ImageBitmapRenderingContext.cpp',
|
||||
'ImageBitmapUtils.cpp',
|
||||
'ImageData.cpp',
|
||||
'OffscreenCanvas.cpp',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'ImageUtils.cpp',
|
||||
]
|
||||
|
||||
# WebGL Sources
|
||||
UNIFIED_SOURCES += [
|
||||
'TexUnpackBlob.cpp',
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE HTML>
|
||||
<head>
|
||||
<title>Test ImageBitmap Extensions (Bug 1141979)</title>
|
||||
<meta charset="utf-8">
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<script src="imagebitmap_extensions_prepareSources.js"></script>
|
||||
<script src="imagebitmap_extensions_data.js"></script>
|
||||
<script src="imagebitmap_extensions.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
runTests();
|
||||
|
||||
function ok(expect, msg) {
|
||||
window.parent.postMessage({"type": "status", status: !!expect, msg: msg}, "*");
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
|
||||
prepareSources().
|
||||
then( function() { return Promise.all([testAccessing_randomTest("Video", gVideo, 20), // video might use slightly different frames
|
||||
testAccessing_randomTest("Image", gImage, 0),
|
||||
testAccessing_randomTest("Canvas", gCanvas, 0),
|
||||
testAccessing_randomTest("Ctx", gCtx, 0),
|
||||
testAccessing_randomTest("ImageData", gImageData, 0),
|
||||
testAccessing_randomTest("ImageBitmap", gImageBitmap, 0),
|
||||
testAccessing_randomTest("PNG", gPNGBlob, 0),
|
||||
testAccessing_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
|
||||
]); }).
|
||||
then( function() { return Promise.all([testCreateFromArrayBffer_randomTest("Video", gVideo, 20), // video might use slightly different frames
|
||||
testCreateFromArrayBffer_randomTest("Image", gImage, 0),
|
||||
testCreateFromArrayBffer_randomTest("Canvas", gCanvas, 0),
|
||||
testCreateFromArrayBffer_randomTest("Ctx", gCtx, 0),
|
||||
testCreateFromArrayBffer_randomTest("ImageData", gImageData, 0),
|
||||
testCreateFromArrayBffer_randomTest("ImageBitmap", gImageBitmap, 0),
|
||||
testCreateFromArrayBffer_randomTest("PNG", gPNGBlob, 0),
|
||||
testCreateFromArrayBffer_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
|
||||
]); }).
|
||||
then(testDraw()).
|
||||
then(testExceptions).
|
||||
then(testColorConversions()).
|
||||
then(function() { return testInvalidAccess([gVideo, gImage, gCanvas, gCtx, gImageData, gImageBitmap, gPNGBlob, gJPEGBlob]); } ).
|
||||
then(function() {window.parent.postMessage({"type": "finish"}, "*");}, function(ev) { failed(ev); window.parent.postMessage({"type": "finish"}, "*"); });
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,526 @@
|
|||
function failed(ex) {
|
||||
ok(false, "Promise failure: " + ex);
|
||||
}
|
||||
|
||||
function isPixel(sourceType, bitmapFormat, imageData, bitmapImageData, x, y, tolerance) {
|
||||
if (imageData.width != bitmapImageData.width ||
|
||||
imageData.height != bitmapImageData.height) {
|
||||
ok(false, "Wrong dimension");
|
||||
}
|
||||
|
||||
var index = 4 * (y * imageData.width + x);
|
||||
|
||||
var pr = imageData.data[index+0],
|
||||
pg = imageData.data[index+1],
|
||||
pb = imageData.data[index+2],
|
||||
pa = imageData.data[index+3];
|
||||
|
||||
if (bitmapFormat == "RGBA32" || bitmapFormat == "RGBX32") {
|
||||
var bpr = bitmapImageData.data[index+0],
|
||||
bpg = bitmapImageData.data[index+1],
|
||||
bpb = bitmapImageData.data[index+2],
|
||||
bpa = bitmapImageData.data[index+3];
|
||||
}
|
||||
else if (bitmapFormat == "BGRA32" || bitmapFormat == "BGRX32") {
|
||||
var bpb = bitmapImageData.data[index+0],
|
||||
bpg = bitmapImageData.data[index+1],
|
||||
bpr = bitmapImageData.data[index+2],
|
||||
bpa = bitmapImageData.data[index+3];
|
||||
}
|
||||
else {
|
||||
// format might be one of the followings: "R5G6B5", "A8", "YUV", ""
|
||||
ok(false, "Not supported ImageFormat: " + bitmapFormat);
|
||||
}
|
||||
|
||||
ok(pr - tolerance <= bpr && bpr <= pr + tolerance &&
|
||||
pg - tolerance <= bpg && bpg <= pg + tolerance &&
|
||||
pb - tolerance <= bpb && bpb <= pb + tolerance &&
|
||||
pa - tolerance <= bpa && bpa <= pa + tolerance,
|
||||
"pixel[" + x + "][" + y + "]: " + sourceType + " is "+pr+","+pg+","+pb+","+pa+"; ImageBitmap is "+ bpr + "," + bpg + "," + bpb + "," + bpa);
|
||||
}
|
||||
|
||||
function promiseThrows(p, name) {
|
||||
var didThrow;
|
||||
return p.then(function() { didThrow = false; },
|
||||
function() { didThrow = true; })
|
||||
.then(function() { ok(didThrow, name); });
|
||||
}
|
||||
|
||||
function testExceptions() {
|
||||
return Promise.all([
|
||||
promiseThrows(testColorConversion("GRAY8", "RGBA32", undefined, true), "[Exception] Cannot convert from GRAY8 to RGBA32"),
|
||||
promiseThrows(testColorConversion("GRAY8", "BGRA32", undefined, true), "[Exception] Cannot convert from GRAY8 to BGRA32"),
|
||||
promiseThrows(testColorConversion("GRAY8", "RGB24", undefined, true), "[Exception] Cannot convert from GRAY8 to RGB24"),
|
||||
promiseThrows(testColorConversion("GRAY8", "BGR24", undefined, true), "[Exception] Cannot convert from GRAY8 to BGR24"),
|
||||
promiseThrows(testColorConversion("GRAY8", "YUV444P", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV444P"),
|
||||
promiseThrows(testColorConversion("GRAY8", "YUV422P", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV422P"),
|
||||
promiseThrows(testColorConversion("GRAY8", "YUV420P", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV420P"),
|
||||
promiseThrows(testColorConversion("GRAY8", "YUV420SP_NV12", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV420SP_NV12"),
|
||||
promiseThrows(testColorConversion("GRAY8", "YUV420SP_NV21", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV420SP_NV21"),
|
||||
promiseThrows(testColorConversion("GRAY8", "HSV", undefined, true), "[Exception] Cannot convert from GRAY8 to HSV"),
|
||||
promiseThrows(testColorConversion("GRAY8", "Lab", undefined, true), "[Exception] Cannot convert from GRAY8 to Lab"),
|
||||
promiseThrows(testColorConversion("GRAY8", "DEPTH", undefined, true), "[Exception] Cannot convert from GRAY8 to DEPTH"),
|
||||
|
||||
promiseThrows(testColorConversion("DEPTH", "RGBA32", undefined, true), "[Exception] Cannot convert from DEPTH to RGBA32"),
|
||||
promiseThrows(testColorConversion("DEPTH", "BGRA32", undefined, true), "[Exception] Cannot convert from DEPTH to BGRA32"),
|
||||
promiseThrows(testColorConversion("DEPTH", "RGB24", undefined, true), "[Exception] Cannot convert from DEPTH to RGB24"),
|
||||
promiseThrows(testColorConversion("DEPTH", "BGR24", undefined, true), "[Exception] Cannot convert from DEPTH to BGR24"),
|
||||
promiseThrows(testColorConversion("DEPTH", "GRAY8", undefined, true), "[Exception] Cannot convert from DEPTH to GRAY8"),
|
||||
promiseThrows(testColorConversion("DEPTH", "YUV444P", undefined, true), "[Exception] Cannot convert from DEPTH to YUV444P"),
|
||||
promiseThrows(testColorConversion("DEPTH", "YUV422P", undefined, true), "[Exception] Cannot convert from DEPTH to YUV422P"),
|
||||
promiseThrows(testColorConversion("DEPTH", "YUV420P", undefined, true), "[Exception] Cannot convert from DEPTH to YUV420P"),
|
||||
promiseThrows(testColorConversion("DEPTH", "YUV420SP_NV12", undefined, true), "[Exception] Cannot convert from DEPTH to YUV420SP_NV12"),
|
||||
promiseThrows(testColorConversion("DEPTH", "YUV420SP_NV21", undefined, true), "[Exception] Cannot convert from DEPTH to YUV420SP_NV21"),
|
||||
promiseThrows(testColorConversion("DEPTH", "HSV", undefined, true), "[Exception] Cannot convert from DEPTH to HSV"),
|
||||
promiseThrows(testColorConversion("DEPTH", "Lab", undefined, true), "[Exception] Cannot convert from DEPTH to Lab"),
|
||||
|
||||
promiseThrows(testColorConversion("RGBA32", "DEPTH", undefined, true), "[Exception] Cannot convert from RGBA32 to DEPTH"),
|
||||
promiseThrows(testColorConversion("BGRA32", "DEPTH", undefined, true), "[Exception] Cannot convert from BGRA32 to DEPTH"),
|
||||
promiseThrows(testColorConversion("RGB24", "DEPTH", undefined, true), "[Exception] Cannot convert from RGB24 to DEPTH"),
|
||||
promiseThrows(testColorConversion("BGR24", "DEPTH", undefined, true), "[Exception] Cannot convert from BGR24 to DEPTH"),
|
||||
promiseThrows(testColorConversion("YUV444P", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV444P to DEPTH"),
|
||||
promiseThrows(testColorConversion("YUV422P", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV422P to DEPTH"),
|
||||
promiseThrows(testColorConversion("YUV420P", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV420P to DEPTH"),
|
||||
promiseThrows(testColorConversion("YUV420SP_NV12", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV420SP_NV12 to DEPTH"),
|
||||
promiseThrows(testColorConversion("YUV420SP_NV21", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV420SP_NV21 to DEPTH"),
|
||||
promiseThrows(testColorConversion("HSV", "DEPTH", undefined, true), "[Exception] Cannot convert from HSV to DEPTH"),
|
||||
promiseThrows(testColorConversion("Lab", "DEPTH", undefined, true), "[Exception] Cannot convert from Lab to DEPTH"),
|
||||
]);
|
||||
}
|
||||
|
||||
function testInvalidAccess(sources) {
|
||||
|
||||
function callMapDataIntoWithImageBitmapCroppedOutSideOfTheSourceImage(source) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var p = createImageBitmap(source, -1, -1, 2, 2);
|
||||
p.then(
|
||||
function(bitmap) {
|
||||
var format = bitmap.findOptimalFormat();
|
||||
var length = bitmap.mappedDataLength(format);
|
||||
var buffer = new ArrayBuffer(length);
|
||||
bitmap.mapDataInto(format, buffer, 0).then(
|
||||
function(layout) { resolve(); },
|
||||
function(error) { reject(error); }
|
||||
);
|
||||
},
|
||||
function() { resolve(); });
|
||||
});
|
||||
};
|
||||
|
||||
var testCases = sources.map( function(source) {
|
||||
return promiseThrows(callMapDataIntoWithImageBitmapCroppedOutSideOfTheSourceImage(source),
|
||||
"[Exception] mapDataInto() should throw with transparent black."); });
|
||||
|
||||
return Promise.all(testCases);
|
||||
}
|
||||
|
||||
function testColorConversions() {
|
||||
return Promise.all([// From RGBA32
|
||||
testColorConversion("RGBA32", "RGBA32"),
|
||||
testColorConversion("RGBA32", "BGRA32"),
|
||||
testColorConversion("RGBA32", "RGB24"),
|
||||
testColorConversion("RGBA32", "BGR24"),
|
||||
testColorConversion("RGBA32", "GRAY8"),
|
||||
testColorConversion("RGBA32", "YUV444P"),
|
||||
testColorConversion("RGBA32", "YUV422P"),
|
||||
testColorConversion("RGBA32", "YUV420P"),
|
||||
testColorConversion("RGBA32", "YUV420SP_NV12"),
|
||||
testColorConversion("RGBA32", "YUV420SP_NV21"),
|
||||
testColorConversion("RGBA32", "HSV", 0.01),
|
||||
testColorConversion("RGBA32", "Lab", 0.5),
|
||||
|
||||
// From BGRA32
|
||||
testColorConversion("BGRA32", "RGBA32"),
|
||||
testColorConversion("BGRA32", "BGRA32"),
|
||||
testColorConversion("BGRA32", "RGB24"),
|
||||
testColorConversion("BGRA32", "BGR24"),
|
||||
testColorConversion("BGRA32", "GRAY8"),
|
||||
testColorConversion("BGRA32", "YUV444P", 3),
|
||||
testColorConversion("BGRA32", "YUV422P"),
|
||||
testColorConversion("BGRA32", "YUV420P"),
|
||||
testColorConversion("BGRA32", "YUV420SP_NV12"),
|
||||
testColorConversion("BGRA32", "YUV420SP_NV21"),
|
||||
testColorConversion("BGRA32", "HSV", 0.01),
|
||||
testColorConversion("BGRA32", "Lab", 0.5),
|
||||
|
||||
// From RGB24
|
||||
testColorConversion("RGB24", "RGBA32"),
|
||||
testColorConversion("RGB24", "BGRA32"),
|
||||
testColorConversion("RGB24", "RGB24"),
|
||||
testColorConversion("RGB24", "BGR24"),
|
||||
testColorConversion("RGB24", "GRAY8"),
|
||||
testColorConversion("RGB24", "YUV444P"),
|
||||
testColorConversion("RGB24", "YUV422P"),
|
||||
testColorConversion("RGB24", "YUV420P"),
|
||||
testColorConversion("RGB24", "YUV420SP_NV12"),
|
||||
testColorConversion("RGB24", "YUV420SP_NV21"),
|
||||
testColorConversion("RGB24", "HSV", 0.01),
|
||||
testColorConversion("RGB24", "Lab", 0.5),
|
||||
|
||||
// From BGR24
|
||||
testColorConversion("BGR24", "RGBA32"),
|
||||
testColorConversion("BGR24", "BGRA32"),
|
||||
testColorConversion("BGR24", "RGB24"),
|
||||
testColorConversion("BGR24", "BGR24"),
|
||||
testColorConversion("BGR24", "GRAY8"),
|
||||
testColorConversion("BGR24", "YUV444P"),
|
||||
testColorConversion("BGR24", "YUV422P"),
|
||||
testColorConversion("BGR24", "YUV420P"),
|
||||
testColorConversion("BGR24", "YUV420SP_NV12"),
|
||||
testColorConversion("BGR24", "YUV420SP_NV21"),
|
||||
testColorConversion("BGR24", "HSV", 0.01),
|
||||
testColorConversion("BGR24", "Lab", 0.5),
|
||||
|
||||
// From YUV444P
|
||||
testColorConversion("YUV444P", "RGBA32"),
|
||||
testColorConversion("YUV444P", "BGRA32"),
|
||||
testColorConversion("YUV444P", "RGB24"),
|
||||
testColorConversion("YUV444P", "BGR24"),
|
||||
testColorConversion("YUV444P", "GRAY8"),
|
||||
testColorConversion("YUV444P", "YUV444P"),
|
||||
testColorConversion("YUV444P", "YUV422P", 3),
|
||||
testColorConversion("YUV444P", "YUV420P", 3),
|
||||
testColorConversion("YUV444P", "YUV420SP_NV12", 3),
|
||||
testColorConversion("YUV444P", "YUV420SP_NV21", 3),
|
||||
testColorConversion("YUV444P", "HSV", 0.01),
|
||||
testColorConversion("YUV444P", "Lab", 0.01),
|
||||
|
||||
// From YUV422P
|
||||
testColorConversion("YUV422P", "RGBA32"),
|
||||
testColorConversion("YUV422P", "BGRA32"),
|
||||
testColorConversion("YUV422P", "RGB24"),
|
||||
testColorConversion("YUV422P", "BGR24"),
|
||||
testColorConversion("YUV422P", "GRAY8"),
|
||||
testColorConversion("YUV422P", "YUV444P", 3),
|
||||
testColorConversion("YUV422P", "YUV422P"),
|
||||
testColorConversion("YUV422P", "YUV420P"),
|
||||
testColorConversion("YUV422P", "YUV420SP_NV12"),
|
||||
testColorConversion("YUV422P", "YUV420SP_NV21"),
|
||||
testColorConversion("YUV422P", "HSV", 0.01),
|
||||
testColorConversion("YUV422P", "Lab", 0.01),
|
||||
|
||||
// From YUV420P
|
||||
testColorConversion("YUV420P", "RGBA32"),
|
||||
testColorConversion("YUV420P", "BGRA32"),
|
||||
testColorConversion("YUV420P", "RGB24"),
|
||||
testColorConversion("YUV420P", "BGR24"),
|
||||
testColorConversion("YUV420P", "GRAY8"),
|
||||
testColorConversion("YUV420P", "YUV444P", 3),
|
||||
testColorConversion("YUV420P", "YUV422P"),
|
||||
testColorConversion("YUV420P", "YUV420P"),
|
||||
testColorConversion("YUV420P", "YUV420SP_NV12"),
|
||||
testColorConversion("YUV420P", "YUV420SP_NV21"),
|
||||
testColorConversion("YUV420P", "HSV", 0.01),
|
||||
testColorConversion("YUV420P", "Lab", 0.01),
|
||||
|
||||
// From NV12
|
||||
testColorConversion("YUV420SP_NV12", "RGBA32"),
|
||||
testColorConversion("YUV420SP_NV12", "BGRA32"),
|
||||
testColorConversion("YUV420SP_NV12", "RGB24"),
|
||||
testColorConversion("YUV420SP_NV12", "BGR24"),
|
||||
testColorConversion("YUV420SP_NV12", "GRAY8"),
|
||||
testColorConversion("YUV420SP_NV12", "YUV444P", 3),
|
||||
testColorConversion("YUV420SP_NV12", "YUV422P"),
|
||||
testColorConversion("YUV420SP_NV12", "YUV420P"),
|
||||
testColorConversion("YUV420SP_NV12", "YUV420SP_NV12"),
|
||||
testColorConversion("YUV420SP_NV12", "YUV420SP_NV21"),
|
||||
testColorConversion("YUV420SP_NV12", "HSV", 0.01),
|
||||
testColorConversion("YUV420SP_NV12", "Lab", 0.01),
|
||||
|
||||
// From NV21
|
||||
testColorConversion("YUV420SP_NV21", "RGBA32"),
|
||||
testColorConversion("YUV420SP_NV21", "BGRA32"),
|
||||
testColorConversion("YUV420SP_NV21", "RGB24"),
|
||||
testColorConversion("YUV420SP_NV21", "BGR24"),
|
||||
testColorConversion("YUV420SP_NV21", "GRAY8"),
|
||||
testColorConversion("YUV420SP_NV21", "YUV444P", 3),
|
||||
testColorConversion("YUV420SP_NV21", "YUV422P"),
|
||||
testColorConversion("YUV420SP_NV21", "YUV420P"),
|
||||
testColorConversion("YUV420SP_NV21", "YUV420SP_NV12"),
|
||||
testColorConversion("YUV420SP_NV21", "YUV420SP_NV21"),
|
||||
testColorConversion("YUV420SP_NV21", "HSV", 0.01),
|
||||
testColorConversion("YUV420SP_NV21", "Lab", 0.01),
|
||||
|
||||
// From HSV
|
||||
testColorConversion("HSV", "RGBA32"),
|
||||
testColorConversion("HSV", "BGRA32"),
|
||||
testColorConversion("HSV", "RGB24"),
|
||||
testColorConversion("HSV", "BGR24"),
|
||||
testColorConversion("HSV", "GRAY8"),
|
||||
testColorConversion("HSV", "YUV444P"),
|
||||
testColorConversion("HSV", "YUV422P"),
|
||||
testColorConversion("HSV", "YUV420P"),
|
||||
testColorConversion("HSV", "YUV420SP_NV12"),
|
||||
testColorConversion("HSV", "YUV420SP_NV21"),
|
||||
testColorConversion("HSV", "HSV", 0),
|
||||
testColorConversion("HSV", "Lab", 0.5),
|
||||
|
||||
// From Lab
|
||||
testColorConversion("Lab", "RGBA32", 1),
|
||||
testColorConversion("Lab", "BGRA32", 1),
|
||||
testColorConversion("Lab", "RGB24", 1),
|
||||
testColorConversion("Lab", "BGR24", 1),
|
||||
testColorConversion("Lab", "GRAY8", 1),
|
||||
testColorConversion("Lab", "YUV444P", 1),
|
||||
testColorConversion("Lab", "YUV422P", 1),
|
||||
testColorConversion("Lab", "YUV420P", 1),
|
||||
testColorConversion("Lab", "YUV420SP_NV12", 1),
|
||||
testColorConversion("Lab", "YUV420SP_NV21", 1),
|
||||
testColorConversion("Lab", "HSV", 0.5),
|
||||
testColorConversion("Lab", "Lab", 0),
|
||||
|
||||
// From GRAY8
|
||||
testColorConversion("GRAY8", "GRAY8"),
|
||||
|
||||
// From DEPTH
|
||||
testColorConversion("DEPTH", "DEPTH", 0, Uint16Array),
|
||||
]);
|
||||
}
|
||||
|
||||
function testDraw() {
|
||||
return Promise.all([doOneDrawTest("RGB24"),
|
||||
doOneDrawTest("BGR24"),
|
||||
doOneDrawTest("YUV444P", 5),
|
||||
doOneDrawTest("YUV422P", 2),
|
||||
doOneDrawTest("YUV420P", 2),
|
||||
doOneDrawTest("YUV420SP_NV12", 2),
|
||||
doOneDrawTest("YUV420SP_NV21", 2),
|
||||
doOneDrawTest("HSV", 2),
|
||||
doOneDrawTest("Lab", 2)]);
|
||||
}
|
||||
|
||||
// Create an ImageBitmap, _bitmap_, from the _source_.
|
||||
// Read the underlying data of _bitmap_ into _bitmapBuffer_.
|
||||
// Compare the _bitmapBuffer_ with gGroundTruthImageData.
|
||||
function testAccessing_randomTest(sourceType, source, duration) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var p = createImageBitmap(source);
|
||||
p.then(
|
||||
function(bitmap) {
|
||||
bitmapFormat = "RGBA32";
|
||||
var bitmapBufferLength = bitmap.mappedDataLength(bitmapFormat);
|
||||
|
||||
var bitmapBuffer = new ArrayBuffer(bitmapBufferLength);
|
||||
var bitmapBufferView = new Uint8ClampedArray(bitmapBuffer, 0, bitmapBufferLength);
|
||||
var promise = bitmap.mapDataInto(bitmapFormat, bitmapBuffer, 0);
|
||||
promise.then(
|
||||
function(bitmapPixelLayout) {
|
||||
// Prepare.
|
||||
bitmapImageData = new ImageData(bitmapBufferView, bitmap.width, bitmap.height);
|
||||
|
||||
// Test.
|
||||
for (var t = 0; t < 50; ++t) {
|
||||
var randomX = Math.floor(Math.random() * 240);
|
||||
var randomY = Math.floor(Math.random() * 175);
|
||||
isPixel(sourceType, "RGBA32", gGroundTruthImageData, bitmapImageData, randomX, randomY, duration);
|
||||
}
|
||||
|
||||
resolve();
|
||||
},
|
||||
function(ev) { failed(ev); reject(); });
|
||||
},
|
||||
function(ev) { failed(ev); reject(); });
|
||||
});
|
||||
}
|
||||
|
||||
// Create an ImageBitmap, _bitmap_, from the _source_.
|
||||
// Read the underlying data of _bitmap_ into _bitmapBuffer_.
|
||||
// Create another ImageBitmap, _bitmap2_, from _bitmapBuffer_.
|
||||
// Read the underlying data of _bitmap2_ into _bitmapBuffer2_.
|
||||
// Compare the _bitmapBuffer2_ with gGroundTruthImageData.
|
||||
function testCreateFromArrayBffer_randomTest(sourceType, source, duration) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var p = createImageBitmap(source);
|
||||
p.then(
|
||||
function(bitmap) {
|
||||
bitmapFormat = "RGBA32";
|
||||
var bitmapBufferLength = bitmap.mappedDataLength(bitmapFormat);
|
||||
|
||||
var bitmapBuffer = new ArrayBuffer(bitmapBufferLength);
|
||||
var bitmapBufferView = new Uint8ClampedArray(bitmapBuffer, 0, bitmapBufferLength);
|
||||
var promiseMapDataInto = bitmap.mapDataInto(bitmapFormat, bitmapBuffer, 0);
|
||||
promiseMapDataInto.then(
|
||||
function(bitmapPixelLayout) {
|
||||
// Create a new ImageBitmap from an ArrayBuffer.
|
||||
var p2 = createImageBitmap(bitmapBufferView,
|
||||
0,
|
||||
bitmapBufferLength,
|
||||
bitmapFormat,
|
||||
bitmapPixelLayout);
|
||||
|
||||
p2.then(
|
||||
function(bitmap2) {
|
||||
bitmapFormat2 = "RGBA32";
|
||||
var bitmapBufferLength2 = bitmap2.mappedDataLength(bitmapFormat2);
|
||||
|
||||
var bitmapBuffer2 = new ArrayBuffer(bitmapBufferLength2);
|
||||
var bitmapBufferView2 = new Uint8ClampedArray(bitmapBuffer2, 0, bitmapBufferLength2);
|
||||
var promise2 = bitmap2.mapDataInto(bitmapFormat2, bitmapBuffer2, 0);
|
||||
promise2.then(
|
||||
function(bitmapPixelLayout2) {
|
||||
// Prepare.
|
||||
var bitmapImageData2 = new ImageData(bitmapBufferView2, bitmap2.width, bitmap2.height);
|
||||
|
||||
// Test.
|
||||
for (var t = 0; t < 50; ++t) {
|
||||
var randomX = Math.floor(Math.random() * 240);
|
||||
var randomY = Math.floor(Math.random() * 175);
|
||||
isPixel(sourceType, "RGBA32", gGroundTruthImageData, bitmapImageData2, randomX, randomY, duration);
|
||||
}
|
||||
|
||||
resolve();
|
||||
},
|
||||
function(ev) { failed(ev); reject(); });
|
||||
},
|
||||
function(ev) { console.log("p2 rejected!"); failed(ev); reject(); });
|
||||
},
|
||||
function(ev) { console.log("promiseMapDataInto rejected!"); failed(ev); reject(); });
|
||||
},
|
||||
function(ev) { failed(ev); reject(); });
|
||||
});
|
||||
}
|
||||
|
||||
function testColorConversion(sourceFromat, destinationFormat, tolerance, shouldThrow) {
|
||||
|
||||
tolerance = tolerance || 0;
|
||||
shouldThrow = shouldThrow || false;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var [srcData, dstData] = getTestData(sourceFromat, destinationFormat);
|
||||
|
||||
ok(!!srcData, "Get valid srcData of type:" + sourceFromat);
|
||||
ok(!!dstData, "Get valid dstData of type:" + destinationFormat);
|
||||
|
||||
// printInfo(sourceFromat, srcData);
|
||||
// printInfo(destinationFormat, dstData);
|
||||
|
||||
// Create a new ImageBitmap from an ArrayBuffer.
|
||||
var p = createImageBitmap(srcData.buffer,
|
||||
0,
|
||||
srcData.bufferLength,
|
||||
srcData.format,
|
||||
srcData.pixelLayout);
|
||||
|
||||
p.then(
|
||||
function(srcBitmap) {
|
||||
ok(!!srcBitmap, "Should get a valid srcBitmap.");
|
||||
ok(srcBitmap.findOptimalFormat() == sourceFromat, "srcBitmap.findOptimalFormat():" + srcBitmap.findOptimalFormat() +
|
||||
" should equal to sourceFromat:" + sourceFromat);
|
||||
|
||||
var dstBufferLength = srcBitmap.mappedDataLength(destinationFormat);
|
||||
var dstBuffer = new ArrayBuffer(dstBufferLength);
|
||||
var dstBufferView = new dstData.ArrayType(dstBuffer, 0, dstBufferLength / dstData.ArrayType.BYTES_PER_ELEMENT);
|
||||
|
||||
// Do color conversion here.
|
||||
var p2 = srcBitmap.mapDataInto(destinationFormat, dstBuffer, 0);
|
||||
p2.then(
|
||||
function(dstPixelLayout) {
|
||||
var dataPixalLayout = dstData.pixelLayout;
|
||||
|
||||
// Check pixel layout.
|
||||
ok(dstPixelLayout.length == dstData.channelCount, "dstPixelLayout.length:" + dstPixelLayout.length +
|
||||
" should equal to dstData.channelCount:" + dstData.channelCount);
|
||||
|
||||
for (var c = 0; c < dstData.channelCount; ++c) {
|
||||
var dstChannelLayout = dstPixelLayout[c];
|
||||
var dataChannelLayout = dataPixalLayout[c];
|
||||
ok(dstChannelLayout.width == dataChannelLayout.width, "channel[" + c + "] dstChannelLayout.width:" + dstChannelLayout.width + " should equal to dataChannelLayout.width:" + dataChannelLayout.width);
|
||||
ok(dstChannelLayout.height == dataChannelLayout.height, "channel[" + c + "] dstChannelLayout.height:" + dstChannelLayout.height + " should equal to dataChannelLayout.height:" + dataChannelLayout.height);
|
||||
ok(dstChannelLayout.skip == dataChannelLayout.skip, "channel[" + c + "] dstChannelLayout.skip:" + dstChannelLayout.skip + " should equal to dataChannelLayout.skip:" + dataChannelLayout.skip);
|
||||
|
||||
for (var i = 0; i < dstChannelLayout.height; ++i) {
|
||||
for (var j = 0; j < dstChannelLayout.width; ++j) {
|
||||
var byteOffset = dstChannelLayout.offset + i * dstChannelLayout.stride + j * (dstChannelLayout.skip + 1) * dstData.ArrayType.BYTES_PER_ELEMENT;
|
||||
var view = new dstData.ArrayType(dstBuffer, byteOffset, 1);
|
||||
var dstBufferViewValue = view[0];
|
||||
var dstDataValue = dstData.getPixelValue(i, j, c);
|
||||
ok(Math.abs(dstBufferViewValue - dstDataValue) <= tolerance,
|
||||
"[" + sourceFromat + " -> " + destinationFormat + "] pixel(" + i + "," + j + ") channnel(" + c +
|
||||
"): dstBufferViewValue:" + dstBufferViewValue +
|
||||
" should equal to dstDataValue:" + dstDataValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
},
|
||||
function(ev) {
|
||||
// If the "mapDataInto" throws, the flow goes here.
|
||||
if (!shouldThrow) { failed(ev); }
|
||||
reject();
|
||||
}
|
||||
);
|
||||
},
|
||||
function(ev) {
|
||||
reject(ev);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function doOneDrawTest(sourceFromat, tolerance) {
|
||||
tolerance = tolerance || 0;
|
||||
var destinationFormat = "RGBA32";
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
var [srcData, dstData] = getTestData(sourceFromat, destinationFormat);
|
||||
ok(!!srcData, "Get valid srcData of type:" + sourceFromat);
|
||||
ok(!!dstData, "Get valid dstData of type:" + destinationFormat);
|
||||
|
||||
var p = createImageBitmap(srcData.buffer,
|
||||
0,
|
||||
srcData.bufferLength,
|
||||
srcData.format,
|
||||
srcData.pixelLayout);
|
||||
|
||||
p.then(
|
||||
function(srcBitmap) {
|
||||
ok(!!srcBitmap, "Should get a valid srcBitmap.");
|
||||
ok(srcBitmap.findOptimalFormat() == sourceFromat, "srcBitmap.findOptimalFormat():" + srcBitmap.findOptimalFormat() +
|
||||
" should equal to sourceFromat:" + sourceFromat);
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = srcBitmap.width;
|
||||
canvas.height = srcBitmap.height;
|
||||
var ctx = canvas.getContext("2d");
|
||||
|
||||
ctx.drawImage(srcBitmap, 0, 0, srcBitmap.width, srcBitmap.height);
|
||||
|
||||
// Get an ImageData from the canvas.
|
||||
var imageData = ctx.getImageData(0, 0, srcBitmap.width, srcBitmap.height);
|
||||
|
||||
for (var i = 0; i < srcBitmap.height; ++i) {
|
||||
for (var j = 0; j < srcBitmap.width; ++j) {
|
||||
var pixelOffset = i * srcBitmap.width * dstData.channelCount + j * dstData.channelCount;
|
||||
var dstImageDataValue_R = imageData.data[pixelOffset + 0];
|
||||
var dstImageDataValue_G = imageData.data[pixelOffset + 1];
|
||||
var dstImageDataValue_B = imageData.data[pixelOffset + 2];
|
||||
var dstImageDataValue_A = imageData.data[pixelOffset + 3];
|
||||
|
||||
var logPrefix = "[" + sourceFromat + " -> " + destinationFormat + "] pixel(" + i + "," + j + ")";
|
||||
|
||||
var dstDataValue_R = dstData.getPixelValue(i, j, 0);
|
||||
var dstDataValue_G = dstData.getPixelValue(i, j, 1);
|
||||
var dstDataValue_B = dstData.getPixelValue(i, j, 2);
|
||||
var dstDataValue_A = dstData.getPixelValue(i, j, 3);
|
||||
ok(Math.abs(dstImageDataValue_R - dstDataValue_R) <= tolerance,
|
||||
logPrefix + "channnel(R): dstImageDataValue:" + dstImageDataValue_R + " should equal to dstDataValue_R: " + dstDataValue_R);
|
||||
ok(Math.abs(dstImageDataValue_G - dstDataValue_G) <= tolerance,
|
||||
logPrefix + "channnel(G): dstImageDataValue:" + dstImageDataValue_G + " should equal to dstDataValue_G: " + dstDataValue_G);
|
||||
ok(Math.abs(dstImageDataValue_B - dstDataValue_B) <= tolerance,
|
||||
logPrefix + "channnel(B): dstImageDataValue:" + dstImageDataValue_B + " should equal to dstDataValue_B: " + dstDataValue_B);
|
||||
ok(Math.abs(dstImageDataValue_A - dstDataValue_A) <= tolerance,
|
||||
logPrefix + "channnel(A): dstImageDataValue:" + dstImageDataValue_A + " should equal to dstDataValue_A: " + dstDataValue_A);
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
},
|
||||
function(ev) {
|
||||
failed(ev);
|
||||
reject(ev);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,47 @@
|
|||
importScripts("imagebitmap_extensions_data.js");
|
||||
importScripts("imagebitmap_extensions.js");
|
||||
|
||||
var gGroundTruthImageData;
|
||||
var gImageData;
|
||||
var gImageBitmap;
|
||||
var gPNGBlob;
|
||||
var gJPEGBlob;
|
||||
|
||||
onmessage = function(event) {
|
||||
if (event.data.type == "setSources") {
|
||||
gGroundTruthImageData = event.data.groundTruthImageData;
|
||||
gImageData = event.data.imageData;
|
||||
gImageBitmap = event.data.imageBitmap;
|
||||
gPNGBlob = event.data.pngBlob;
|
||||
gJPEGBlob = event.data.jpegBlob;
|
||||
|
||||
ok(!!gGroundTruthImageData, "Get gGroundTruthImageData!");
|
||||
ok(!!gImageData, "Get gImageData!");
|
||||
ok(!!gImageBitmap, "Get gImageBitmap!");
|
||||
ok(!!gPNGBlob, "Get gPNGBlob!");
|
||||
ok(!!gJPEGBlob, "Get gJPEGBlob!");
|
||||
|
||||
runTests();
|
||||
}
|
||||
};
|
||||
|
||||
function ok(expect, msg) {
|
||||
postMessage({"type": "status", status: !!expect, msg: msg});
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
testExceptions().
|
||||
then(testColorConversions()).
|
||||
then( function() { return Promise.all([testAccessing_randomTest("ImageData", gImageData, 0),
|
||||
testAccessing_randomTest("ImageBitmap", gImageBitmap, 0),
|
||||
testAccessing_randomTest("PNG", gPNGBlob, 0),
|
||||
testAccessing_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
|
||||
]); }).
|
||||
then( function() { return Promise.all([testCreateFromArrayBffer_randomTest("ImageData", gImageData, 0),
|
||||
testCreateFromArrayBffer_randomTest("ImageBitmap", gImageBitmap, 0),
|
||||
testCreateFromArrayBffer_randomTest("PNG", gPNGBlob, 0),
|
||||
testCreateFromArrayBffer_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
|
||||
]); }).
|
||||
then(function() { return testInvalidAccess([gImageData, gImageBitmap, gPNGBlob, gJPEGBlob]); } ).
|
||||
then(function() {postMessage({"type": "finish"});}, function(ev) { failed(ev); postMessage({"type": "finish"}); });
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
var gImage;
|
||||
var gVideo;
|
||||
var gCanvas;
|
||||
var gCtx;
|
||||
var gImageData;
|
||||
var gImageBitmap;
|
||||
var gPNGBlob;
|
||||
var gJPEGBlob;
|
||||
|
||||
var gGroundTruthImageData;
|
||||
|
||||
function prepareSources() {
|
||||
gVideo = document.createElement("video");
|
||||
gVideo.src = "http://example.com/tests/dom/canvas/test/crossorigin/video.sjs?name=tests/dom/media/test/320x240.ogv&type=video/ogg&cors=anonymous";
|
||||
gVideo.crossOrigin = "anonymous";
|
||||
gVideo.autoplay = "true"
|
||||
|
||||
|
||||
gCanvas = document.createElement("canvas");
|
||||
gCtx = gCanvas.getContext("2d");
|
||||
|
||||
var resolver;
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
resolver = resolve;
|
||||
});
|
||||
|
||||
// Prepare video.
|
||||
gVideo.onloadeddata = function() {
|
||||
ok(gVideo, "[Prepare Sources] gVideo is ok.");
|
||||
|
||||
// Prepare canvas.
|
||||
gCanvas.width = gVideo.videoWidth;
|
||||
gCanvas.height = gVideo.videoHeight;
|
||||
gCtx.drawImage(gVideo, 0, 0);
|
||||
ok(gCanvas, "[Prepare Sources] gCanvas is ok.");
|
||||
ok(gCtx, "[Prepare Sources] gCtx is ok.");
|
||||
|
||||
// Prepare gGroundTruthImageData.
|
||||
gGroundTruthImageData = gCtx.getImageData(0, 0, gCanvas.width, gCanvas.height);
|
||||
ok(gGroundTruthImageData, "[Prepare Sources] gGroundTruthImageData is ok.");
|
||||
|
||||
// Prepare image.
|
||||
gImage = document.createElement("img");
|
||||
gImage.src = gCanvas.toDataURL();
|
||||
var resolverImage;
|
||||
var promiseImage = new Promise(function(resolve, reject) {
|
||||
resolverImage = resolve;
|
||||
});
|
||||
gImage.onload = function() {
|
||||
resolverImage(true);
|
||||
}
|
||||
|
||||
// Prepare ImageData.
|
||||
gImageData = gCtx.getImageData(0, 0, gCanvas.width, gCanvas.height);
|
||||
ok(gImageData, "[Prepare Sources] gImageData is ok.");
|
||||
|
||||
// Prepapre PNG Blob.
|
||||
var promisePNGBlob = new Promise(function(resolve, reject) {
|
||||
gCanvas.toBlob(function(blob) {
|
||||
gPNGBlob = blob;
|
||||
ok(gPNGBlob, "[Prepare Sources] gPNGBlob is ok.");
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
// Prepare JPEG Blob.
|
||||
var promiseJPEGBlob = new Promise(function(resolve, reject) {
|
||||
gCanvas.toBlob(function(blob) {
|
||||
gJPEGBlob = blob;
|
||||
ok(gJPEGBlob, "[Prepare Sources] gJPEGBlob is ok.");
|
||||
resolve(true);
|
||||
}, "image/jpeg", 1.00);
|
||||
});
|
||||
|
||||
// Prepare ImageBitmap.
|
||||
var promiseImageBitmap = new Promise(function(resolve, reject) {
|
||||
var p = createImageBitmap(gCanvas);
|
||||
p.then(function(bitmap) {
|
||||
gImageBitmap = bitmap;
|
||||
ok(gImageBitmap, "[Prepare Sources] gImageBitmap is ok.");
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
resolver(Promise.all([
|
||||
promiseImage,
|
||||
promisePNGBlob,
|
||||
promiseJPEGBlob,
|
||||
promiseImageBitmap
|
||||
]))
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
|
@ -26,6 +26,11 @@ support-files =
|
|||
image_yellow75.png
|
||||
imagebitmap_bug1239300.js
|
||||
imagebitmap_bug1239752.js
|
||||
imagebitmap_extensions.html
|
||||
imagebitmap_extensions.js
|
||||
imagebitmap_extensions_data.js
|
||||
imagebitmap_extensions_on_worker.js
|
||||
imagebitmap_extensions_prepareSources.js
|
||||
imagebitmap_on_worker.js
|
||||
imagebitmap_structuredclone.js
|
||||
imagebitmap_structuredclone_iframe.html
|
||||
|
@ -230,6 +235,10 @@ tags = imagebitmap
|
|||
tags = imagebitmap
|
||||
[test_imagebitmap_cropping.html]
|
||||
tags = imagebitmap
|
||||
[test_imagebitmap_extensions.html]
|
||||
tags = imagebitmap
|
||||
[test_imagebitmap_extensions_on_worker.html]
|
||||
tags = imagebitmap
|
||||
[test_imagebitmap_on_worker.html]
|
||||
tags = imagebitmap
|
||||
[test_imagebitmap_structuredclone.html]
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE HTML>
|
||||
<heand>
|
||||
<title>Test ImageBitmap Extensions (Bug 1141979)</title>
|
||||
<meta charset="utf-8">
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"><div>
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// The createImageBitmap() method is part of Window whose
|
||||
// prototype was created before the preference is set. So I create another
|
||||
// iframe with the right preference setting so that the
|
||||
// createImageBitmap() will be visible.
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['canvas.imagebitmap_extensions.enabled', true]
|
||||
]}, function() {
|
||||
var div = document.getElementById("content");
|
||||
ok(div, "Parent exists");
|
||||
|
||||
var ifr = document.createElement("iframe");
|
||||
ifr.setAttribute('src', "imagebitmap_extensions.html");
|
||||
div.appendChild(ifr);
|
||||
});
|
||||
|
||||
window.onmessage = function(event) {
|
||||
if (event.data.type == "status") {
|
||||
ok(event.data.status, event.data.msg);
|
||||
} else if (event.data.type == "finish") {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE HTML>
|
||||
<heand>
|
||||
<title>Test ImageBitmap Extensions On Worker (Bug 1141979)</title>
|
||||
<meta charset="utf-8">
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"><div>
|
||||
<script src="imagebitmap_extensions_prepareSources.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var worker;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['canvas.imagebitmap_extensions.enabled', true]
|
||||
]}, function() {
|
||||
worker = new Worker("imagebitmap_extensions_on_worker.js");
|
||||
worker.onmessage = function(event) {
|
||||
if (event.data.type == "status") {
|
||||
ok(event.data.status, event.data.msg);
|
||||
} else if (event.data.type == "finish") {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
ok(!!worker, "Worker created successfully.");
|
||||
prepareSources().then(function() {
|
||||
worker.postMessage({"type": "setSources",
|
||||
"groundTruthImageData": gGroundTruthImageData,
|
||||
"imageData": gImageData,
|
||||
"imageBitmap": gImageBitmap,
|
||||
"pngBlob": gPNGBlob,
|
||||
"jpegBlob": gJPEGBlob});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
|
@ -55,6 +55,12 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
extern bool IsCurrentThreadRunningChromeWorker();
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
|
||||
using namespace dom;
|
||||
using namespace hal;
|
||||
|
||||
|
@ -1339,14 +1345,19 @@ EventListenerManager::Disconnect()
|
|||
}
|
||||
|
||||
static EventListenerFlags
|
||||
GetEventListenerFlagsFromOptions(const EventListenerOptions& aOptions)
|
||||
GetEventListenerFlagsFromOptions(const EventListenerOptions& aOptions,
|
||||
bool aIsMainThread)
|
||||
{
|
||||
EventListenerFlags flags;
|
||||
flags.mCapture = aOptions.mCapture;
|
||||
if (aOptions.mMozSystemGroup) {
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
MOZ_ASSERT(cx, "Not being called from JS?");
|
||||
flags.mInSystemGroup = IsChromeOrXBL(cx, nullptr);
|
||||
if (aIsMainThread) {
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
MOZ_ASSERT(cx, "Not being called from JS?");
|
||||
flags.mInSystemGroup = IsChromeOrXBL(cx, nullptr);
|
||||
} else {
|
||||
flags.mInSystemGroup = workers::IsCurrentThreadRunningChromeWorker();
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
@ -1376,7 +1387,7 @@ EventListenerManager::AddEventListener(
|
|||
flags.mCapture = aOptions.GetAsBoolean();
|
||||
} else {
|
||||
const auto& options = aOptions.GetAsAddEventListenerOptions();
|
||||
flags = GetEventListenerFlagsFromOptions(options);
|
||||
flags = GetEventListenerFlagsFromOptions(options, mIsMainThreadELM);
|
||||
flags.mPassive = options.mPassive;
|
||||
}
|
||||
flags.mAllowUntrustedEvents = aWantsUntrusted;
|
||||
|
@ -1405,7 +1416,7 @@ EventListenerManager::RemoveEventListener(
|
|||
flags.mCapture = aOptions.GetAsBoolean();
|
||||
} else {
|
||||
const auto& options = aOptions.GetAsEventListenerOptions();
|
||||
flags = GetEventListenerFlagsFromOptions(options);
|
||||
flags = GetEventListenerFlagsFromOptions(options, mIsMainThreadELM);
|
||||
}
|
||||
RemoveEventListenerByType(aListenerHolder, aType, flags);
|
||||
}
|
||||
|
|
|
@ -48,35 +48,6 @@ ToChar(bool aBool)
|
|||
return aBool ? "true" : "false";
|
||||
}
|
||||
|
||||
static const char*
|
||||
ToChar(IMEMessage aIMEMessage)
|
||||
{
|
||||
switch (aIMEMessage) {
|
||||
case NOTIFY_IME_OF_NOTHING:
|
||||
return "NOTIFY_IME_OF_NOTHING";
|
||||
case NOTIFY_IME_OF_FOCUS:
|
||||
return "NOTIFY_IME_OF_FOCUS";
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
return "NOTIFY_IME_OF_BLUR";
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
return "NOTIFY_IME_OF_SELECTION_CHANGE";
|
||||
case NOTIFY_IME_OF_TEXT_CHANGE:
|
||||
return "NOTIFY_IME_OF_TEXT_CHANGE";
|
||||
case NOTIFY_IME_OF_COMPOSITION_UPDATE:
|
||||
return "NOTIFY_IME_OF_COMPOSITION_UPDATE";
|
||||
case NOTIFY_IME_OF_POSITION_CHANGE:
|
||||
return "NOTIFY_IME_OF_POSITION_CHANGE";
|
||||
case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
|
||||
return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT";
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
return "REQUEST_TO_COMMIT_COMPOSITION";
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
return "REQUEST_TO_CANCEL_COMPOSITION";
|
||||
default:
|
||||
return "Unexpected value";
|
||||
}
|
||||
}
|
||||
|
||||
class WritingModeToString final : public nsAutoCString
|
||||
{
|
||||
public:
|
||||
|
@ -207,6 +178,7 @@ IMEContentObserver::IMEContentObserver()
|
|||
, mNeedsToNotifyIMEOfTextChange(false)
|
||||
, mNeedsToNotifyIMEOfSelectionChange(false)
|
||||
, mNeedsToNotifyIMEOfPositionChange(false)
|
||||
, mNeedsToNotifyIMEOfCompositionEventHandled(false)
|
||||
, mIsHandlingQueryContentEvent(false)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
|
@ -590,12 +562,31 @@ IMEContentObserver::MaybeReinitialize(nsIWidget* aWidget,
|
|||
|
||||
bool
|
||||
IMEContentObserver::IsManaging(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
nsIContent* aContent) const
|
||||
{
|
||||
return GetState() == eState_Observing &&
|
||||
IsObservingContent(aPresContext, aContent);
|
||||
}
|
||||
|
||||
bool
|
||||
IMEContentObserver::IsManaging(const TextComposition* aComposition) const
|
||||
{
|
||||
if (GetState() != eState_Observing) {
|
||||
return false;
|
||||
}
|
||||
nsPresContext* presContext = aComposition->GetPresContext();
|
||||
if (NS_WARN_IF(!presContext)) {
|
||||
return false;
|
||||
}
|
||||
if (presContext != GetPresContext()) {
|
||||
return false; // observing different document
|
||||
}
|
||||
nsINode* targetNode = aComposition->GetEventTargetNode();
|
||||
nsIContent* targetContent =
|
||||
targetNode && targetNode->IsContent() ? targetNode->AsContent() : nullptr;
|
||||
return IsObservingContent(presContext, targetContent);
|
||||
}
|
||||
|
||||
IMEContentObserver::State
|
||||
IMEContentObserver::GetState() const
|
||||
{
|
||||
|
@ -1248,6 +1239,17 @@ IMEContentObserver::MaybeNotifyIMEOfPositionChange()
|
|||
FlushMergeableNotifications();
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::MaybeNotifyCompositionEventHandled()
|
||||
{
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::MaybeNotifyCompositionEventHandled()",
|
||||
this));
|
||||
|
||||
PostCompositionEventHandledNotification();
|
||||
FlushMergeableNotifications();
|
||||
}
|
||||
|
||||
bool
|
||||
IMEContentObserver::UpdateSelectionCache()
|
||||
{
|
||||
|
@ -1294,6 +1296,16 @@ IMEContentObserver::PostPositionChangeNotification()
|
|||
mNeedsToNotifyIMEOfPositionChange = true;
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::PostCompositionEventHandledNotification()
|
||||
{
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::"
|
||||
"PostCompositionEventHandledNotification()", this));
|
||||
|
||||
mNeedsToNotifyIMEOfCompositionEventHandled = true;
|
||||
}
|
||||
|
||||
bool
|
||||
IMEContentObserver::IsReflowLocked() const
|
||||
{
|
||||
|
@ -1421,6 +1433,9 @@ IMEContentObserver::AChangeEvent::CanNotifyIME(
|
|||
if (NS_WARN_IF(!mIMEContentObserver)) {
|
||||
return false;
|
||||
}
|
||||
if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
|
||||
return mIMEContentObserver->mWidget != nullptr;
|
||||
}
|
||||
State state = mIMEContentObserver->GetState();
|
||||
// If it's not initialized, we should do nothing.
|
||||
if (state == eState_NotObserving) {
|
||||
|
@ -1463,6 +1478,8 @@ IMEContentObserver::AChangeEvent::IsSafeToNotifyIME(
|
|||
if (NS_WARN_IF(state != eState_Initializing && state != eState_Observing)) {
|
||||
return false;
|
||||
}
|
||||
} else if (aChangeEventType == eChangeEventType_CompositionEventHandled) {
|
||||
// It doesn't need to check the observing status.
|
||||
} else if (state != eState_Observing) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1548,6 +1565,18 @@ IMEContentObserver::IMENotificationSender::Run()
|
|||
}
|
||||
}
|
||||
|
||||
// Composition event handled notification should be sent after all the
|
||||
// other notifications because this notifies widget of finishing all pending
|
||||
// events are handled completely.
|
||||
if (!mIMEContentObserver->mNeedsToNotifyIMEOfTextChange &&
|
||||
!mIMEContentObserver->mNeedsToNotifyIMEOfSelectionChange &&
|
||||
!mIMEContentObserver->mNeedsToNotifyIMEOfPositionChange) {
|
||||
if (mIMEContentObserver->mNeedsToNotifyIMEOfCompositionEventHandled) {
|
||||
mIMEContentObserver->mNeedsToNotifyIMEOfCompositionEventHandled = false;
|
||||
SendCompositionEventHandled();
|
||||
}
|
||||
}
|
||||
|
||||
mIMEContentObserver->mQueuedSender = nullptr;
|
||||
|
||||
// If notifications caused some new change, we should notify them now.
|
||||
|
@ -1774,4 +1803,44 @@ IMEContentObserver::IMENotificationSender::SendPositionChange()
|
|||
"SendPositionChange(), sent NOTIFY_IME_OF_POSITION_CHANGE", this));
|
||||
}
|
||||
|
||||
void
|
||||
IMEContentObserver::IMENotificationSender::SendCompositionEventHandled()
|
||||
{
|
||||
if (!CanNotifyIME(eChangeEventType_CompositionEventHandled)) {
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::"
|
||||
"SendCompositionEventHandled(), FAILED, due to impossible to notify "
|
||||
"IME of composition event handled", this));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsSafeToNotifyIME(eChangeEventType_CompositionEventHandled)) {
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::"
|
||||
"SendCompositionEventHandled(), retrying to send "
|
||||
"NOTIFY_IME_OF_POSITION_CHANGE...", this));
|
||||
mIMEContentObserver->PostCompositionEventHandledNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Info,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::"
|
||||
"SendCompositionEventHandled(), sending "
|
||||
"NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED...", this));
|
||||
|
||||
MOZ_RELEASE_ASSERT(mIMEContentObserver->mSendingNotification ==
|
||||
NOTIFY_IME_OF_NOTHING);
|
||||
mIMEContentObserver->mSendingNotification =
|
||||
NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED;
|
||||
IMEStateManager::NotifyIME(
|
||||
IMENotification(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED),
|
||||
mIMEContentObserver->mWidget);
|
||||
mIMEContentObserver->mSendingNotification = NOTIFY_IME_OF_NOTHING;
|
||||
|
||||
MOZ_LOG(sIMECOLog, LogLevel::Debug,
|
||||
("IMECO: 0x%p IMEContentObserver::IMENotificationSender::"
|
||||
"SendCompositionEventHandled(), sent "
|
||||
"NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED", this));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -29,6 +29,7 @@ class nsPresContext;
|
|||
namespace mozilla {
|
||||
|
||||
class EventStateManager;
|
||||
class TextComposition;
|
||||
|
||||
// IMEContentObserver notifies widget of any text and selection changes
|
||||
// in the currently focused editor
|
||||
|
@ -91,7 +92,8 @@ public:
|
|||
nsPresContext* aPresContext,
|
||||
nsIContent* aContent,
|
||||
nsIEditor* aEditor);
|
||||
bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent);
|
||||
bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent) const;
|
||||
bool IsManaging(const TextComposition* aTextComposition) const;
|
||||
bool IsEditorHandlingEventForComposition() const;
|
||||
bool KeepAliveDuringDeactive() const
|
||||
{
|
||||
|
@ -111,6 +113,12 @@ public:
|
|||
*/
|
||||
void TryToFlushPendingNotifications();
|
||||
|
||||
/**
|
||||
* MaybeNotifyCompositionEventHandled() posts composition event handled
|
||||
* notification into the pseudo queue.
|
||||
*/
|
||||
void MaybeNotifyCompositionEventHandled();
|
||||
|
||||
private:
|
||||
~IMEContentObserver() {}
|
||||
|
||||
|
@ -143,6 +151,7 @@ private:
|
|||
bool aOccurredDuringComposition);
|
||||
void PostPositionChangeNotification();
|
||||
void MaybeNotifyIMEOfPositionChange();
|
||||
void PostCompositionEventHandledNotification();
|
||||
|
||||
void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
|
||||
void ObserveEditableNode();
|
||||
|
@ -161,6 +170,7 @@ private:
|
|||
mNeedsToNotifyIMEOfTextChange = false;
|
||||
mNeedsToNotifyIMEOfSelectionChange = false;
|
||||
mNeedsToNotifyIMEOfPositionChange = false;
|
||||
mNeedsToNotifyIMEOfCompositionEventHandled = false;
|
||||
mTextChangeData.Clear();
|
||||
}
|
||||
bool NeedsToNotifyIMEOfSomething() const
|
||||
|
@ -168,7 +178,8 @@ private:
|
|||
return mNeedsToNotifyIMEOfFocusSet ||
|
||||
mNeedsToNotifyIMEOfTextChange ||
|
||||
mNeedsToNotifyIMEOfSelectionChange ||
|
||||
mNeedsToNotifyIMEOfPositionChange;
|
||||
mNeedsToNotifyIMEOfPositionChange ||
|
||||
mNeedsToNotifyIMEOfCompositionEventHandled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,7 +215,7 @@ private:
|
|||
eChangeEventType_Selection,
|
||||
eChangeEventType_Text,
|
||||
eChangeEventType_Position,
|
||||
eChangeEventType_FlushPendingEvents
|
||||
eChangeEventType_CompositionEventHandled
|
||||
};
|
||||
|
||||
explicit AChangeEvent(IMEContentObserver* aIMEContentObserver)
|
||||
|
@ -241,6 +252,7 @@ private:
|
|||
void SendSelectionChange();
|
||||
void SendTextChange();
|
||||
void SendPositionChange();
|
||||
void SendCompositionEventHandled();
|
||||
|
||||
bool mIsRunning;
|
||||
};
|
||||
|
@ -327,6 +339,7 @@ private:
|
|||
bool mNeedsToNotifyIMEOfTextChange;
|
||||
bool mNeedsToNotifyIMEOfSelectionChange;
|
||||
bool mNeedsToNotifyIMEOfPositionChange;
|
||||
bool mNeedsToNotifyIMEOfCompositionEventHandled;
|
||||
// mIsHandlingQueryContentEvent is true when IMEContentObserver is handling
|
||||
// WidgetQueryContentEvent with ContentEventHandler.
|
||||
bool mIsHandlingQueryContentEvent;
|
||||
|
|
|
@ -135,33 +135,6 @@ GetIMEStateSetOpenName(IMEState::Open aOpen)
|
|||
}
|
||||
}
|
||||
|
||||
static const char*
|
||||
GetNotifyIMEMessageName(IMEMessage aMessage)
|
||||
{
|
||||
switch (aMessage) {
|
||||
case NOTIFY_IME_OF_FOCUS:
|
||||
return "NOTIFY_IME_OF_FOCUS";
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
return "NOTIFY_IME_OF_BLUR";
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
return "NOTIFY_IME_OF_SELECTION_CHANGE";
|
||||
case NOTIFY_IME_OF_TEXT_CHANGE:
|
||||
return "NOTIFY_IME_OF_TEXT_CHANGE";
|
||||
case NOTIFY_IME_OF_COMPOSITION_UPDATE:
|
||||
return "NOTIFY_IME_OF_COMPOSITION_UPDATE";
|
||||
case NOTIFY_IME_OF_POSITION_CHANGE:
|
||||
return "NOTIFY_IME_OF_POSITION_CHANGE";
|
||||
case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
|
||||
return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT";
|
||||
case REQUEST_TO_COMMIT_COMPOSITION:
|
||||
return "REQUEST_TO_COMMIT_COMPOSITION";
|
||||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
return "REQUEST_TO_CANCEL_COMPOSITION";
|
||||
default:
|
||||
return "unacceptable IME notification message";
|
||||
}
|
||||
}
|
||||
|
||||
StaticRefPtr<nsIContent> IMEStateManager::sContent;
|
||||
nsPresContext* IMEStateManager::sPresContext = nullptr;
|
||||
nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
|
||||
|
@ -1234,6 +1207,13 @@ IMEStateManager::DispatchCompositionEvent(
|
|||
}
|
||||
}
|
||||
|
||||
// static
|
||||
IMEContentObserver*
|
||||
IMEStateManager::GetActiveContentObserver()
|
||||
{
|
||||
return sActiveIMEContentObserver;
|
||||
}
|
||||
|
||||
// static
|
||||
nsIContent*
|
||||
IMEStateManager::GetRootContent(nsPresContext* aPresContext)
|
||||
|
@ -1353,7 +1333,7 @@ IMEStateManager::NotifyIME(const IMENotification& aNotification,
|
|||
("ISM: IMEStateManager::NotifyIME(aNotification={ mMessage=%s }, "
|
||||
"aWidget=0x%p, aOriginIsRemote=%s), sFocusedIMEWidget=0x%p, "
|
||||
"sRemoteHasFocus=%s",
|
||||
GetNotifyIMEMessageName(aNotification.mMessage), aWidget,
|
||||
ToChar(aNotification.mMessage), aWidget,
|
||||
GetBoolName(aOriginIsRemote), sFocusedIMEWidget,
|
||||
GetBoolName(sRemoteHasFocus)));
|
||||
|
||||
|
@ -1477,7 +1457,7 @@ IMEStateManager::NotifyIME(const IMENotification& aNotification,
|
|||
case REQUEST_TO_CANCEL_COMPOSITION:
|
||||
return composition ?
|
||||
composition->RequestToCommit(aWidget, true) : NS_OK;
|
||||
case NOTIFY_IME_OF_COMPOSITION_UPDATE:
|
||||
case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
|
||||
if (!aOriginIsRemote && (!composition || isSynthesizedForTests)) {
|
||||
MOZ_LOG(sISMLog, LogLevel::Info,
|
||||
("ISM: IMEStateManager::NotifyIME(), FAILED, received content "
|
||||
|
@ -1508,8 +1488,7 @@ IMEStateManager::NotifyIME(IMEMessage aMessage,
|
|||
MOZ_LOG(sISMLog, LogLevel::Info,
|
||||
("ISM: IMEStateManager::NotifyIME(aMessage=%s, aPresContext=0x%p, "
|
||||
"aOriginIsRemote=%s)",
|
||||
GetNotifyIMEMessageName(aMessage), aPresContext,
|
||||
GetBoolName(aOriginIsRemote)));
|
||||
ToChar(aMessage), aPresContext, GetBoolName(aOriginIsRemote)));
|
||||
|
||||
NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
|
||||
|
||||
|
|
|
@ -224,6 +224,12 @@ public:
|
|||
static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
|
||||
nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Returns active IMEContentObserver but may be nullptr if focused content
|
||||
* isn't editable or focus in a remote process.
|
||||
*/
|
||||
static IMEContentObserver* GetActiveContentObserver();
|
||||
|
||||
protected:
|
||||
static nsresult OnChangeFocusInternal(nsPresContext* aPresContext,
|
||||
nsIContent* aContent,
|
||||
|
|
|
@ -250,16 +250,10 @@ DEFINE_KEYNAME_WITH_SAME_NAME(AudioFaderRear)
|
|||
DEFINE_KEYNAME_WITH_SAME_NAME(AudioSurroundModeNext)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(AudioTrebleDown)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(AudioTrebleUp)
|
||||
#ifndef MOZ_B2G
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(AudioVolumeDown)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(AudioVolumeUp)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(AudioVolumeMute)
|
||||
#else
|
||||
// Temporarily, remaining for B2G
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(VolumeDown)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(VolumeUp)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(VolumeMute)
|
||||
#endif
|
||||
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneToggle)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneVolumeDown)
|
||||
DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneVolumeUp)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ContentEventHandler.h"
|
||||
#include "IMEContentObserver.h"
|
||||
#include "IMEStateManager.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIEditor.h"
|
||||
|
@ -397,8 +399,7 @@ TextComposition::DispatchCompositionEvent(
|
|||
MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
|
||||
}
|
||||
|
||||
// Notify composition update to widget if possible
|
||||
NotityUpdateComposition(aCompositionEvent);
|
||||
OnCompositionEventHandled(aCompositionEvent);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -426,7 +427,7 @@ TextComposition::HandleSelectionEvent(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
void
|
||||
TextComposition::NotityUpdateComposition(
|
||||
TextComposition::OnCompositionEventHandled(
|
||||
const WidgetCompositionEvent* aCompositionEvent)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mTabParent);
|
||||
|
@ -456,7 +457,23 @@ TextComposition::NotityUpdateComposition(
|
|||
return;
|
||||
}
|
||||
|
||||
NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE);
|
||||
RefPtr<IMEContentObserver> contentObserver =
|
||||
IMEStateManager::GetActiveContentObserver();
|
||||
// When IMEContentObserver is managing the editor which has this composition,
|
||||
// composition event handled notification should be sent after the observer
|
||||
// notifies all pending notifications. Therefore, we should use it.
|
||||
// XXX If IMEContentObserver suddenly loses focus after here and notifying
|
||||
// widget of pending notifications, we won't notify widget of composition
|
||||
// event handled. Although, this is a bug but it should be okay since
|
||||
// destroying IMEContentObserver notifies IME of blur. So, native IME
|
||||
// handler can treat it as this notification too.
|
||||
if (contentObserver && contentObserver->IsManaging(this)) {
|
||||
contentObserver->MaybeNotifyCompositionEventHandled();
|
||||
return;
|
||||
}
|
||||
// Otherwise, e.g., this composition is in non-active window, we should
|
||||
// notify widget directly.
|
||||
NotifyIME(NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -374,7 +374,8 @@ private:
|
|||
/**
|
||||
* Calculate composition offset then notify composition update to widget
|
||||
*/
|
||||
void NotityUpdateComposition(const WidgetCompositionEvent* aCompositionEvent);
|
||||
void OnCompositionEventHandled(
|
||||
const WidgetCompositionEvent* aCompositionEvent);
|
||||
|
||||
/**
|
||||
* CompositionEventDispatcher dispatches the specified composition (or text)
|
||||
|
|
|
@ -239,6 +239,10 @@ const kEventConstructors = {
|
|||
return new ErrorEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
FlyWebFetchEvent: { create: null, // Cannot create untrusted event from JS.
|
||||
},
|
||||
FlyWebWebSocketEvent: { create: null, // Cannot create untrusted event from JS.
|
||||
},
|
||||
FocusEvent: { create: function (aName, aProps) {
|
||||
return new FocusEvent(aName, aProps);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsTHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "mozilla/dom/FlyWebDiscoveryManager.h"
|
||||
#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static LazyLogModule gFlyWebDiscoveryManagerLog("FlyWebDiscoveryManager");
|
||||
#undef LOG_I
|
||||
#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebDiscoveryManagerLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
#undef LOG_E
|
||||
#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebDiscoveryManagerLog, mozilla::LogLevel::Error, (__VA_ARGS__))
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(FlyWebDiscoveryManager)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(FlyWebDiscoveryManager)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(FlyWebDiscoveryManager)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FlyWebDiscoveryManager)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
FlyWebDiscoveryManager::FlyWebDiscoveryManager(nsISupports* aParent,
|
||||
FlyWebService* aService)
|
||||
: mParent(aParent)
|
||||
, mService(aService)
|
||||
, mNextId(0)
|
||||
{
|
||||
}
|
||||
|
||||
FlyWebDiscoveryManager::~FlyWebDiscoveryManager()
|
||||
{
|
||||
mService->UnregisterDiscoveryManager(this);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
FlyWebDiscoveryManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return FlyWebDiscoveryManagerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
FlyWebDiscoveryManager::GetParentObject() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<FlyWebDiscoveryManager>
|
||||
FlyWebDiscoveryManager::Constructor(const GlobalObject& aGlobal, ErrorResult& rv)
|
||||
{
|
||||
RefPtr<FlyWebService> service = FlyWebService::GetOrCreate();
|
||||
if (!service) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<FlyWebDiscoveryManager> result = new FlyWebDiscoveryManager(
|
||||
aGlobal.GetAsSupports(), service);
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebDiscoveryManager::ListServices(nsTArray<FlyWebDiscoveredService>& aServices)
|
||||
{
|
||||
return mService->ListDiscoveredServices(aServices);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FlyWebDiscoveryManager::StartDiscovery(FlyWebDiscoveryCallback& aCallback)
|
||||
{
|
||||
uint32_t id = GenerateId();
|
||||
mCallbackMap.Put(id, &aCallback);
|
||||
mService->RegisterDiscoveryManager(this);
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebDiscoveryManager::StopDiscovery(uint32_t aId)
|
||||
{
|
||||
mCallbackMap.Remove(aId);
|
||||
if (mCallbackMap.Count() == 0) {
|
||||
mService->UnregisterDiscoveryManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebDiscoveryManager::PairWithService(const nsAString& aServiceId,
|
||||
FlyWebPairingCallback& aCallback)
|
||||
{
|
||||
mService->PairWithService(aServiceId, aCallback);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebDiscoveryManager::NotifyDiscoveredServicesChanged()
|
||||
{
|
||||
nsTArray<FlyWebDiscoveredService> services;
|
||||
ListServices(services);
|
||||
Sequence<FlyWebDiscoveredService> servicesSeq;
|
||||
servicesSeq.SwapElements(services);
|
||||
for (auto iter = mCallbackMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
FlyWebDiscoveryCallback *callback = iter.UserData();
|
||||
ErrorResult err;
|
||||
callback->OnDiscoveredServicesChanged(servicesSeq, err);
|
||||
ENSURE_SUCCESS_VOID(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_FlyWebDiscoveryManager_h
|
||||
#define mozilla_dom_FlyWebDiscoveryManager_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "FlyWebDiscoveryManagerBinding.h"
|
||||
#include "FlyWebService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FlyWebDiscoveryManager final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FlyWebDiscoveryManager)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
nsISupports* GetParentObject() const;
|
||||
|
||||
static already_AddRefed<FlyWebDiscoveryManager> Constructor(const GlobalObject& aGlobal,
|
||||
ErrorResult& rv);
|
||||
|
||||
void ListServices(nsTArray<FlyWebDiscoveredService>& aServices);
|
||||
uint32_t StartDiscovery(FlyWebDiscoveryCallback& aCallback);
|
||||
void StopDiscovery(uint32_t aId);
|
||||
|
||||
void PairWithService(const nsAString& aServiceId,
|
||||
FlyWebPairingCallback& callback);
|
||||
|
||||
void NotifyDiscoveredServicesChanged();
|
||||
|
||||
private:
|
||||
FlyWebDiscoveryManager(nsISupports* mParent, FlyWebService* aService);
|
||||
~FlyWebDiscoveryManager();
|
||||
|
||||
uint32_t GenerateId() {
|
||||
return ++mNextId;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
RefPtr<FlyWebService> mService;
|
||||
|
||||
uint32_t mNextId;
|
||||
|
||||
nsRefPtrHashtable<nsUint32HashKey, FlyWebDiscoveryCallback> mCallbackMap;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FlyWebDiscoveryManager_h
|
|
@ -0,0 +1,212 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/FlyWebPublishedServer.h"
|
||||
#include "mozilla/dom/FlyWebPublishBinding.h"
|
||||
#include "mozilla/dom/FlyWebService.h"
|
||||
#include "mozilla/dom/Request.h"
|
||||
#include "mozilla/dom/FlyWebServerEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
static LazyLogModule gFlyWebPublishedServerLog("FlyWebPublishedServer");
|
||||
#undef LOG_I
|
||||
#define LOG_I(...) MOZ_LOG(mozilla::dom::gFlyWebPublishedServerLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
||||
#undef LOG_E
|
||||
#define LOG_E(...) MOZ_LOG(mozilla::dom::gFlyWebPublishedServerLog, mozilla::LogLevel::Error, (__VA_ARGS__))
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(FlyWebPublishedServer, mozilla::DOMEventTargetHelper)
|
||||
|
||||
FlyWebPublishedServer::FlyWebPublishedServer(nsPIDOMWindowInner* aOwner,
|
||||
const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions,
|
||||
Promise* aPublishPromise)
|
||||
: mozilla::DOMEventTargetHelper(aOwner)
|
||||
, mOwnerWindowID(aOwner ? aOwner->WindowID() : 0)
|
||||
, mPublishPromise(aPublishPromise)
|
||||
, mName(aName)
|
||||
, mCategory(aOptions.mCategory)
|
||||
, mHttp(aOptions.mHttp)
|
||||
, mMessage(aOptions.mMessage)
|
||||
, mUiUrl(aOptions.mUiUrl)
|
||||
, mIsRegistered(true) // Registered by the FlyWebService
|
||||
{
|
||||
if (mCategory.IsEmpty()) {
|
||||
mCategory.SetIsVoid(true);
|
||||
}
|
||||
|
||||
mHttpServer = new HttpServer();
|
||||
mHttpServer->Init(-1, Preferences::GetBool("flyweb.use-tls", false), this);
|
||||
}
|
||||
|
||||
FlyWebPublishedServer::~FlyWebPublishedServer()
|
||||
{
|
||||
// Make sure to unregister to avoid dangling pointers
|
||||
Close();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
FlyWebPublishedServer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return FlyWebPublishedServerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::Close()
|
||||
{
|
||||
// Unregister from server.
|
||||
if (mIsRegistered) {
|
||||
FlyWebService::GetOrCreate()->UnregisterServer(this);
|
||||
mIsRegistered = false;
|
||||
}
|
||||
|
||||
if (mMDNSCancelRegister) {
|
||||
mMDNSCancelRegister->Cancel(NS_BINDING_ABORTED);
|
||||
mMDNSCancelRegister = nullptr;
|
||||
}
|
||||
|
||||
if (mHttpServer) {
|
||||
RefPtr<HttpServer> server = mHttpServer.forget();
|
||||
server->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::OnServerStarted(nsresult aStatus)
|
||||
{
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
FlyWebService::GetOrCreate()->StartDiscoveryOf(this);
|
||||
} else {
|
||||
DiscoveryStarted(aStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::OnServerClose()
|
||||
{
|
||||
mHttpServer = nullptr;
|
||||
Close();
|
||||
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("close"));
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::OnRequest(InternalRequest* aRequest)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
RefPtr<FlyWebFetchEvent> e = new FlyWebFetchEvent(this,
|
||||
new Request(global, aRequest),
|
||||
aRequest);
|
||||
e->Init(this);
|
||||
e->InitEvent(NS_LITERAL_STRING("fetch"), false, false);
|
||||
|
||||
DispatchTrustedEvent(e);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::OnWebSocket(InternalRequest* aConnectRequest)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
RefPtr<FlyWebFetchEvent> e = new FlyWebWebSocketEvent(this,
|
||||
new Request(global, aConnectRequest),
|
||||
aConnectRequest);
|
||||
e->Init(this);
|
||||
e->InitEvent(NS_LITERAL_STRING("websocket"), false, false);
|
||||
|
||||
DispatchTrustedEvent(e);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::OnFetchResponse(InternalRequest* aRequest,
|
||||
InternalResponse* aResponse)
|
||||
{
|
||||
MOZ_ASSERT(aRequest);
|
||||
MOZ_ASSERT(aResponse);
|
||||
|
||||
LOG_I("FlyWebPublishedMDNSServer::OnFetchResponse(%p)", this);
|
||||
|
||||
if (mHttpServer) {
|
||||
mHttpServer->SendResponse(aRequest, aResponse);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<WebSocket>
|
||||
FlyWebPublishedServer::OnWebSocketAccept(InternalRequest* aConnectRequest,
|
||||
const Optional<nsAString>& aProtocol,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aConnectRequest);
|
||||
|
||||
LOG_I("FlyWebPublishedMDNSServer::OnWebSocketAccept(%p)", this);
|
||||
|
||||
if (!mHttpServer) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoCString negotiatedExtensions;
|
||||
nsCOMPtr<nsITransportProvider> provider =
|
||||
mHttpServer->AcceptWebSocket(aConnectRequest,
|
||||
aProtocol,
|
||||
negotiatedExtensions,
|
||||
aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(provider);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetOwner());
|
||||
AutoJSContext cx;
|
||||
GlobalObject global(cx, nsGlobalWindow::Cast(window)->FastGetGlobalJSObject());
|
||||
|
||||
nsCString url;
|
||||
aConnectRequest->GetURL(url);
|
||||
Sequence<nsString> protocols;
|
||||
if (aProtocol.WasPassed() &&
|
||||
!protocols.AppendElement(aProtocol.Value(), fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return WebSocket::ConstructorCommon(global,
|
||||
NS_ConvertUTF8toUTF16(url),
|
||||
protocols,
|
||||
provider,
|
||||
negotiatedExtensions,
|
||||
aRv);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebPublishedServer::OnWebSocketResponse(InternalRequest* aConnectRequest,
|
||||
InternalResponse* aResponse)
|
||||
{
|
||||
MOZ_ASSERT(aConnectRequest);
|
||||
MOZ_ASSERT(aResponse);
|
||||
|
||||
LOG_I("FlyWebPublishedMDNSServer::OnWebSocketResponse(%p)", this);
|
||||
|
||||
if (mHttpServer) {
|
||||
mHttpServer->SendWebSocketResponse(aConnectRequest, aResponse);
|
||||
}
|
||||
}
|
||||
|
||||
void FlyWebPublishedServer::DiscoveryStarted(nsresult aStatus)
|
||||
{
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
mPublishPromise->MaybeResolve(this);
|
||||
} else {
|
||||
Close();
|
||||
mPublishPromise->MaybeReject(aStatus);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_FlyWebPublishedServer_h
|
||||
#define mozilla_dom_FlyWebPublishedServer_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "HttpServer.h"
|
||||
#include "mozilla/dom/WebSocket.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class InternalResponse;
|
||||
class InternalRequest;
|
||||
struct FlyWebPublishOptions;
|
||||
class FlyWebPublishedServer;
|
||||
|
||||
class FlyWebPublishedServer final : public mozilla::DOMEventTargetHelper
|
||||
, public HttpServerListener
|
||||
{
|
||||
public:
|
||||
FlyWebPublishedServer(nsPIDOMWindowInner* aOwner,
|
||||
const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions,
|
||||
Promise* aPublishPromise);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
uint64_t OwnerWindowID() const {
|
||||
return mOwnerWindowID;
|
||||
}
|
||||
|
||||
int32_t Port()
|
||||
{
|
||||
return mHttpServer ? mHttpServer->GetPort() : 0;
|
||||
}
|
||||
void GetCertKey(nsACString& aKey) {
|
||||
if (mHttpServer) {
|
||||
mHttpServer->GetCertKey(aKey);
|
||||
} else {
|
||||
aKey.Truncate();
|
||||
}
|
||||
}
|
||||
|
||||
void GetName(nsAString& aName)
|
||||
{
|
||||
aName = mName;
|
||||
}
|
||||
nsAString& Name()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
void GetCategory(nsAString& aCategory)
|
||||
{
|
||||
aCategory = mCategory;
|
||||
}
|
||||
|
||||
bool Http()
|
||||
{
|
||||
return mHttp;
|
||||
}
|
||||
|
||||
bool Message()
|
||||
{
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
void GetUiUrl(nsAString& aUiUrl)
|
||||
{
|
||||
aUiUrl = mUiUrl;
|
||||
}
|
||||
|
||||
void OnFetchResponse(InternalRequest* aRequest,
|
||||
InternalResponse* aResponse);
|
||||
already_AddRefed<WebSocket>
|
||||
OnWebSocketAccept(InternalRequest* aConnectRequest,
|
||||
const Optional<nsAString>& aProtocol,
|
||||
ErrorResult& aRv);
|
||||
void OnWebSocketResponse(InternalRequest* aConnectRequest,
|
||||
InternalResponse* aResponse);
|
||||
|
||||
void SetCancelRegister(nsICancelable* aCancelRegister)
|
||||
{
|
||||
mMDNSCancelRegister = aCancelRegister;
|
||||
}
|
||||
|
||||
void Close();
|
||||
|
||||
// HttpServerListener
|
||||
virtual void OnServerStarted(nsresult aStatus) override;
|
||||
virtual void OnRequest(InternalRequest* aRequest) override;
|
||||
virtual void OnWebSocket(InternalRequest* aConnectRequest) override;
|
||||
virtual void OnServerClose() override;
|
||||
|
||||
IMPL_EVENT_HANDLER(fetch)
|
||||
IMPL_EVENT_HANDLER(websocket)
|
||||
IMPL_EVENT_HANDLER(close)
|
||||
|
||||
void DiscoveryStarted(nsresult aStatus);
|
||||
|
||||
private:
|
||||
~FlyWebPublishedServer();
|
||||
|
||||
uint64_t mOwnerWindowID;
|
||||
RefPtr<HttpServer> mHttpServer;
|
||||
RefPtr<Promise> mPublishPromise;
|
||||
nsCOMPtr<nsICancelable> mMDNSCancelRegister;
|
||||
|
||||
nsString mName;
|
||||
nsString mCategory;
|
||||
bool mHttp;
|
||||
bool mMessage;
|
||||
nsString mUiUrl;
|
||||
|
||||
bool mIsRegistered;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FlyWebPublishedServer_h
|
|
@ -0,0 +1,141 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/EventBinding.h"
|
||||
#include "mozilla/dom/FlyWebFetchEventBinding.h"
|
||||
#include "mozilla/dom/FlyWebPublishedServer.h"
|
||||
#include "mozilla/dom/FlyWebServerEvents.h"
|
||||
#include "mozilla/dom/FlyWebWebSocketEventBinding.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Response.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(FlyWebFetchEvent)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(FlyWebFetchEvent, Event)
|
||||
NS_IMPL_RELEASE_INHERITED(FlyWebFetchEvent, Event)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FlyWebFetchEvent, Event)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FlyWebFetchEvent, Event)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FlyWebFetchEvent, Event)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FlyWebFetchEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(Event)
|
||||
|
||||
FlyWebFetchEvent::FlyWebFetchEvent(FlyWebPublishedServer* aServer,
|
||||
class Request* aRequest,
|
||||
InternalRequest* aInternalRequest)
|
||||
: Event(aServer, nullptr, nullptr)
|
||||
, mRequest(aRequest)
|
||||
, mInternalRequest(aInternalRequest)
|
||||
, mServer(aServer)
|
||||
, mResponded(false)
|
||||
{
|
||||
MOZ_ASSERT(aServer);
|
||||
MOZ_ASSERT(aRequest);
|
||||
MOZ_ASSERT(aInternalRequest);
|
||||
}
|
||||
|
||||
FlyWebFetchEvent::~FlyWebFetchEvent()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
FlyWebFetchEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return FlyWebFetchEventBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebFetchEvent::RespondWith(Promise& aArg, ErrorResult& aRv)
|
||||
{
|
||||
if (mResponded) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mResponded = true;
|
||||
|
||||
aArg.AppendNativeHandler(this);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebFetchEvent::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
RefPtr<Response> response;
|
||||
if (aValue.isObject()) {
|
||||
UNWRAP_OBJECT(Response, &aValue.toObject(), response);
|
||||
}
|
||||
|
||||
RefPtr<InternalResponse> intResponse;
|
||||
if (response && response->Type() != ResponseType::Opaque) {
|
||||
intResponse = response->GetInternalResponse();
|
||||
}
|
||||
|
||||
if (!intResponse) {
|
||||
intResponse = InternalResponse::NetworkError();
|
||||
}
|
||||
|
||||
NotifyServer(intResponse);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebFetchEvent::NotifyServer(InternalResponse* aResponse)
|
||||
{
|
||||
mServer->OnFetchResponse(mInternalRequest, aResponse);
|
||||
}
|
||||
|
||||
void
|
||||
FlyWebFetchEvent::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
|
||||
{
|
||||
RefPtr<InternalResponse> err = InternalResponse::NetworkError();
|
||||
|
||||
NotifyServer(err);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
FlyWebWebSocketEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return FlyWebWebSocketEventBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<WebSocket>
|
||||
FlyWebWebSocketEvent::Accept(const Optional<nsAString>& aProtocol,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mResponded) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mResponded = true;
|
||||
|
||||
return mServer->OnWebSocketAccept(mInternalRequest, aProtocol, aRv);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FlyWebWebSocketEvent::NotifyServer(InternalResponse* aResponse)
|
||||
{
|
||||
mServer->OnWebSocketResponse(mInternalRequest, aResponse);
|
||||
}
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_FlyWebFetchEvent_h
|
||||
#define mozilla_dom_FlyWebFetchEvent_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/FlyWebFetchEventBinding.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/WebSocket.h"
|
||||
|
||||
struct JSContext;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Request;
|
||||
class Response;
|
||||
class FlyWebPublishedServer;
|
||||
class InternalRequest;
|
||||
class InternalResponse;
|
||||
|
||||
class FlyWebFetchEvent : public Event
|
||||
, public PromiseNativeHandler
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FlyWebFetchEvent, Event)
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual void
|
||||
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
||||
virtual void
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
|
||||
|
||||
class Request* Request() const
|
||||
{
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
void RespondWith(Promise& aArg, ErrorResult& aRv);
|
||||
|
||||
FlyWebFetchEvent(FlyWebPublishedServer* aServer,
|
||||
class Request* aRequest,
|
||||
InternalRequest* aInternalRequest);
|
||||
|
||||
protected:
|
||||
virtual ~FlyWebFetchEvent();
|
||||
|
||||
virtual void NotifyServer(InternalResponse* aResponse);
|
||||
|
||||
RefPtr<class Request> mRequest;
|
||||
RefPtr<InternalRequest> mInternalRequest;
|
||||
RefPtr<FlyWebPublishedServer> mServer;
|
||||
|
||||
bool mResponded;
|
||||
};
|
||||
|
||||
class FlyWebWebSocketEvent final : public FlyWebFetchEvent
|
||||
{
|
||||
public:
|
||||
FlyWebWebSocketEvent(FlyWebPublishedServer* aServer,
|
||||
class Request* aRequest,
|
||||
InternalRequest* aInternalRequest)
|
||||
: FlyWebFetchEvent(aServer, aRequest, aInternalRequest)
|
||||
{}
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<WebSocket> Accept(const Optional<nsAString>& aProtocol,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
~FlyWebWebSocketEvent() {};
|
||||
|
||||
virtual void NotifyServer(InternalResponse* aResponse) override;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FlyWebFetchEvent_h
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,109 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_FlyWebService_h
|
||||
#define mozilla_dom_FlyWebService_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIDNSServiceDiscovery.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
class nsIProxyInfo;
|
||||
class nsISocketTransport;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
struct FlyWebPublishOptions;
|
||||
struct FlyWebFilter;
|
||||
class FlyWebPublishedServer;
|
||||
class FlyWebPairingCallback;
|
||||
class FlyWebDiscoveryManager;
|
||||
class FlyWebMDNSService;
|
||||
|
||||
class FlyWebService final : public nsIObserver
|
||||
{
|
||||
friend class FlyWebMDNSService;
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static FlyWebService* GetExisting();
|
||||
static FlyWebService* GetOrCreate();
|
||||
static already_AddRefed<FlyWebService> GetOrCreateAddRefed()
|
||||
{
|
||||
return do_AddRef(GetOrCreate());
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> PublishServer(const nsAString& aName,
|
||||
const FlyWebPublishOptions& aOptions,
|
||||
nsPIDOMWindowInner* aWindow,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void UnregisterServer(FlyWebPublishedServer* aServer);
|
||||
|
||||
bool HasConnectionOrServer(uint64_t aWindowID);
|
||||
|
||||
void ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices);
|
||||
void PairWithService(const nsAString& aServiceId, FlyWebPairingCallback& aCallback);
|
||||
nsresult CreateTransportForHost(const char **types,
|
||||
uint32_t typeCount,
|
||||
const nsACString &host,
|
||||
int32_t port,
|
||||
const nsACString &hostRoute,
|
||||
int32_t portRoute,
|
||||
nsIProxyInfo *proxyInfo,
|
||||
nsISocketTransport **result);
|
||||
|
||||
already_AddRefed<FlyWebPublishedServer> FindPublishedServerByName(
|
||||
const nsAString& aName);
|
||||
|
||||
void RegisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager);
|
||||
void UnregisterDiscoveryManager(FlyWebDiscoveryManager* aDiscoveryManager);
|
||||
|
||||
// Should only be called by FlyWebPublishedServer
|
||||
void StartDiscoveryOf(FlyWebPublishedServer* aServer);
|
||||
|
||||
private:
|
||||
FlyWebService();
|
||||
~FlyWebService();
|
||||
|
||||
ErrorResult Init();
|
||||
|
||||
void NotifyDiscoveredServicesChanged();
|
||||
|
||||
// Might want to make these hashes for perf
|
||||
nsTArray<FlyWebPublishedServer*> mServers;
|
||||
|
||||
RefPtr<FlyWebMDNSService> mMDNSHttpService;
|
||||
RefPtr<FlyWebMDNSService> mMDNSFlywebService;
|
||||
|
||||
struct PairedInfo
|
||||
{
|
||||
FlyWebPairedService mService;
|
||||
nsCOMPtr<nsIDNSServiceInfo> mDNSServiceInfo;
|
||||
};
|
||||
nsClassHashtable<nsCStringHashKey, PairedInfo>
|
||||
mPairedServiceTable;
|
||||
ReentrantMonitor mMonitor; // Protecting mPairedServiceTable
|
||||
|
||||
nsTHashtable<nsPtrHashKey<FlyWebDiscoveryManager>> mDiscoveryManagerTable;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_FlyWebService_h
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,192 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_HttpServer_h
|
||||
#define mozilla_dom_HttpServer_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsITLSServerSocket.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "nsIRequestObserver.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "nsITransportProvider.h"
|
||||
#include "nsILocalCertService.h"
|
||||
|
||||
class nsIX509Cert;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class InternalRequest;
|
||||
class InternalResponse;
|
||||
|
||||
class HttpServerListener
|
||||
{
|
||||
public:
|
||||
// switch to NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING when that lands
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
|
||||
|
||||
virtual void OnServerStarted(nsresult aStatus) = 0;
|
||||
virtual void OnRequest(InternalRequest* aRequest) = 0;
|
||||
virtual void OnWebSocket(InternalRequest* aConnectRequest) = 0;
|
||||
virtual void OnServerClose() = 0;
|
||||
};
|
||||
|
||||
class HttpServer final : public nsIServerSocketListener,
|
||||
public nsILocalCertGetCallback
|
||||
{
|
||||
public:
|
||||
HttpServer();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVERSOCKETLISTENER
|
||||
NS_DECL_NSILOCALCERTGETCALLBACK
|
||||
|
||||
void Init(int32_t aPort, bool aHttps, HttpServerListener* aListener);
|
||||
|
||||
void SendResponse(InternalRequest* aRequest, InternalResponse* aResponse);
|
||||
already_AddRefed<nsITransportProvider>
|
||||
AcceptWebSocket(InternalRequest* aConnectRequest,
|
||||
const Optional<nsAString>& aProtocol,
|
||||
nsACString& aNegotiatedExtensions,
|
||||
ErrorResult& aRv);
|
||||
void SendWebSocketResponse(InternalRequest* aConnectRequest,
|
||||
InternalResponse* aResponse);
|
||||
|
||||
void Close();
|
||||
|
||||
void GetCertKey(nsACString& aKey);
|
||||
|
||||
int32_t GetPort()
|
||||
{
|
||||
return mPort;
|
||||
}
|
||||
|
||||
private:
|
||||
~HttpServer();
|
||||
|
||||
nsresult StartServerSocket(nsIX509Cert* aCert);
|
||||
void NotifyStarted(nsresult aStatus);
|
||||
|
||||
class TransportProvider final : public nsITransportProvider
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITRANSPORTPROVIDER
|
||||
|
||||
void SetTransport(nsISocketTransport* aTransport,
|
||||
nsIAsyncInputStream* aInput,
|
||||
nsIAsyncOutputStream* aOutput);
|
||||
|
||||
private:
|
||||
virtual ~TransportProvider();
|
||||
void MaybeNotify();
|
||||
|
||||
nsCOMPtr<nsIHttpUpgradeListener> mListener;
|
||||
nsCOMPtr<nsISocketTransport> mTransport;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInput;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mOutput;
|
||||
};
|
||||
|
||||
class Connection final : public nsIInputStreamCallback
|
||||
, public nsIOutputStreamCallback
|
||||
, public nsITLSServerSecurityObserver
|
||||
{
|
||||
public:
|
||||
Connection(nsISocketTransport* aTransport,
|
||||
HttpServer* aServer,
|
||||
nsresult& rv);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIOUTPUTSTREAMCALLBACK
|
||||
NS_DECL_NSITLSSERVERSECURITYOBSERVER
|
||||
|
||||
bool TryHandleResponse(InternalRequest* aRequest,
|
||||
InternalResponse* aResponse);
|
||||
already_AddRefed<nsITransportProvider>
|
||||
HandleAcceptWebSocket(const Optional<nsAString>& aProtocol,
|
||||
nsACString& aNegotiatedExtensions,
|
||||
ErrorResult& aRv);
|
||||
void HandleWebSocketResponse(InternalResponse* aResponse);
|
||||
bool HasPendingWebSocketRequest(InternalRequest* aRequest)
|
||||
{
|
||||
return aRequest == mPendingWebSocketRequest;
|
||||
}
|
||||
|
||||
void Close();
|
||||
|
||||
private:
|
||||
~Connection();
|
||||
|
||||
void SetSecurityObserver(bool aListen);
|
||||
|
||||
static NS_METHOD ReadSegmentsFunc(nsIInputStream* aIn,
|
||||
void* aClosure,
|
||||
const char* aBuffer,
|
||||
uint32_t aToOffset,
|
||||
uint32_t aCount,
|
||||
uint32_t* aWriteCount);
|
||||
nsresult ConsumeInput(const char*& aBuffer,
|
||||
const char* aEnd);
|
||||
nsresult ConsumeLine(const char* aBuffer,
|
||||
size_t aLength);
|
||||
void MaybeAddPendingHeader();
|
||||
|
||||
void QueueResponse(InternalResponse* aResponse);
|
||||
|
||||
RefPtr<HttpServer> mServer;
|
||||
nsCOMPtr<nsISocketTransport> mTransport;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInput;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mOutput;
|
||||
|
||||
enum { eRequestLine, eHeaders, eBody, ePause } mState;
|
||||
RefPtr<InternalRequest> mPendingReq;
|
||||
uint32_t mPendingReqVersion;
|
||||
nsCString mInputBuffer;
|
||||
nsCString mPendingHeaderName;
|
||||
nsCString mPendingHeaderValue;
|
||||
uint32_t mRemainingBodySize;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mCurrentRequestBody;
|
||||
bool mCloseAfterRequest;
|
||||
|
||||
typedef Pair<RefPtr<InternalRequest>,
|
||||
RefPtr<InternalResponse>> PendingRequest;
|
||||
nsTArray<PendingRequest> mPendingRequests;
|
||||
RefPtr<MozPromise<nsresult, bool, false>> mOutputCopy;
|
||||
|
||||
RefPtr<InternalRequest> mPendingWebSocketRequest;
|
||||
RefPtr<TransportProvider> mWebSocketTransportProvider;
|
||||
|
||||
struct OutputBuffer {
|
||||
nsCString mString;
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
bool mChunked;
|
||||
};
|
||||
|
||||
nsTArray<OutputBuffer> mOutputBuffers;
|
||||
};
|
||||
|
||||
friend class Connection;
|
||||
|
||||
RefPtr<HttpServerListener> mListener;
|
||||
nsCOMPtr<nsIServerSocket> mServerSocket;
|
||||
nsCOMPtr<nsIX509Cert> mCert;
|
||||
|
||||
nsTArray<RefPtr<Connection>> mConnections;
|
||||
|
||||
int32_t mPort;
|
||||
bool mHttps;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_HttpServer_h
|
|
@ -0,0 +1,36 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'FlyWebDiscoveryManager.h',
|
||||
'FlyWebPublishedServer.h',
|
||||
'FlyWebServerEvents.h',
|
||||
'FlyWebService.h',
|
||||
'HttpServer.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'FlyWebDiscoveryManager.cpp',
|
||||
'FlyWebPublishedServer.cpp',
|
||||
'FlyWebServerEvents.cpp',
|
||||
'FlyWebService.cpp',
|
||||
'HttpServer.cpp'
|
||||
]
|
||||
|
||||
#include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/netwerk/base',
|
||||
'/netwerk/dns',
|
||||
'/netwerk/protocol/websocket',
|
||||
'/xpcom/io'
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
|
@ -776,6 +776,11 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
||||
|
||||
//TODO: Apply the rules for text track cue rendering Bug 865407
|
||||
if (mTextTrackManager) {
|
||||
mTextTrackManager->GetTextTracks()->SetCuesInactive();
|
||||
}
|
||||
|
||||
if (fireTimeUpdate) {
|
||||
// Since we destroyed the decoder above, the current playback position
|
||||
// will now be reported as 0. The playback position was non-zero when
|
||||
|
@ -2285,6 +2290,11 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
|
|||
mIsAudioTrackAudible(false),
|
||||
mAudible(IsAudible())
|
||||
{
|
||||
ErrorResult rv;
|
||||
|
||||
double defaultVolume = Preferences::GetFloat("media.default_volume", 1.0);
|
||||
SetVolume(defaultVolume, rv);
|
||||
|
||||
mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
|
||||
|
||||
mPaused.SetOuter(this);
|
||||
|
@ -3737,13 +3747,13 @@ void HTMLMediaElement::SeekCompleted()
|
|||
{
|
||||
mPlayingBeforeSeek = false;
|
||||
SetPlayedOrSeeked(true);
|
||||
if (mTextTrackManager) {
|
||||
mTextTrackManager->DidSeek();
|
||||
}
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
|
||||
// We changed whether we're seeking so we need to AddRemoveSelfReference
|
||||
AddRemoveSelfReference();
|
||||
if (mTextTrackManager) {
|
||||
mTextTrackManager->DidSeek();
|
||||
}
|
||||
if (mCurrentPlayRangeStart == -1.0) {
|
||||
mCurrentPlayRangeStart = CurrentTime();
|
||||
}
|
||||
|
@ -4744,12 +4754,10 @@ already_AddRefed<TimeRanges>
|
|||
HTMLMediaElement::Buffered() const
|
||||
{
|
||||
RefPtr<TimeRanges> ranges = new TimeRanges(ToSupports(OwnerDoc()));
|
||||
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||
if (mDecoder) {
|
||||
media::TimeIntervals buffered = mDecoder->GetBuffered();
|
||||
if (!buffered.IsInvalid()) {
|
||||
buffered.ToTimeRanges(ranges);
|
||||
}
|
||||
if (mDecoder) {
|
||||
media::TimeIntervals buffered = mDecoder->GetBuffered();
|
||||
if (!buffered.IsInvalid()) {
|
||||
buffered.ToTimeRanges(ranges);
|
||||
}
|
||||
}
|
||||
return ranges.forget();
|
||||
|
|
|
@ -700,6 +700,11 @@ public:
|
|||
mTextTrackManager->AddCue(aCue);
|
||||
}
|
||||
}
|
||||
void NotifyCueRemoved(TextTrackCue& aCue) {
|
||||
if (mTextTrackManager) {
|
||||
mTextTrackManager->NotifyCueRemoved(aCue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A public wrapper for FinishDecoderSetup()
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(TextTrackManager::ShutdownObserverProxy, nsIObserver);
|
||||
|
||||
CompareTextTracks::CompareTextTracks(HTMLMediaElement* aMediaElement)
|
||||
{
|
||||
mMediaElement = aMediaElement;
|
||||
|
@ -76,7 +78,8 @@ CompareTextTracks::LessThan(TextTrack* aOne, TextTrack* aTwo) const
|
|||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(TextTrackManager, mMediaElement, mTextTracks,
|
||||
mPendingTextTracks, mNewCues)
|
||||
mPendingTextTracks, mNewCues,
|
||||
mLastActiveCues)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackManager)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
|
||||
|
@ -89,7 +92,11 @@ StaticRefPtr<nsIWebVTTParserWrapper> TextTrackManager::sParserWrapper;
|
|||
|
||||
TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
|
||||
: mMediaElement(aMediaElement)
|
||||
, mHasSeeked(false)
|
||||
, mLastTimeMarchesOnCalled(0.0)
|
||||
, mTimeMarchesOnDispatched(false)
|
||||
, performedTrackSelection(false)
|
||||
, mShutdown(false)
|
||||
{
|
||||
nsISupports* parentObject =
|
||||
mMediaElement->OwnerDoc()->GetParentObject();
|
||||
|
@ -98,6 +105,7 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
|
|||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
|
||||
mNewCues = new TextTrackCueList(window);
|
||||
mLastActiveCues = new TextTrackCueList(window);
|
||||
mTextTracks = new TextTrackList(window, this);
|
||||
mPendingTextTracks = new TextTrackList(window, this);
|
||||
|
||||
|
@ -107,10 +115,12 @@ TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
|
|||
sParserWrapper = parserWrapper;
|
||||
ClearOnShutdown(&sParserWrapper);
|
||||
}
|
||||
mShutdownProxy = new ShutdownObserverProxy(this);
|
||||
}
|
||||
|
||||
TextTrackManager::~TextTrackManager()
|
||||
{
|
||||
nsContentUtils::UnregisterShutdownObserver(mShutdownProxy);
|
||||
}
|
||||
|
||||
TextTrackList*
|
||||
|
@ -167,6 +177,7 @@ TextTrackManager::AddCues(TextTrack* aTextTrack)
|
|||
for (uint32_t i = 0; i < cueList->Length(); ++i) {
|
||||
mNewCues->AddCue(*cueList->IndexedGetter(i, dummy));
|
||||
}
|
||||
DispatchTimeMarchesOn();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +194,14 @@ TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly)
|
|||
}
|
||||
|
||||
mTextTracks->RemoveTextTrack(aTextTrack);
|
||||
// Remove the cues in mNewCues belong to aTextTrack.
|
||||
TextTrackCueList* removeCueList = aTextTrack->GetCues();
|
||||
if (removeCueList) {
|
||||
for (uint32_t i = 0; i < removeCueList->Length(); ++i) {
|
||||
mNewCues->RemoveCue(*((*removeCueList)[i]));
|
||||
}
|
||||
DispatchTimeMarchesOn();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -191,6 +210,10 @@ TextTrackManager::DidSeek()
|
|||
if (mTextTracks) {
|
||||
mTextTracks->DidSeek();
|
||||
}
|
||||
if (mMediaElement) {
|
||||
mLastTimeMarchesOnCalled = mMediaElement->CurrentTime();
|
||||
}
|
||||
mHasSeeked = true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -229,6 +252,10 @@ TextTrackManager::UpdateCueDisplay()
|
|||
} else if (overlay->Length() > 0) {
|
||||
nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true);
|
||||
}
|
||||
// Call TimeMarchesOn() directly instead DispatchTimeMarchesOn()
|
||||
// because we had render the new cue, so we must run
|
||||
// TimeMarchesOn immediately.
|
||||
TimeMarchesOn();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -237,6 +264,16 @@ TextTrackManager::AddCue(TextTrackCue& aCue)
|
|||
if (mNewCues) {
|
||||
mNewCues->AddCue(aCue);
|
||||
}
|
||||
DispatchTimeMarchesOn();
|
||||
}
|
||||
|
||||
void
|
||||
TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue)
|
||||
{
|
||||
if (mNewCues) {
|
||||
mNewCues->RemoveCue(aCue);
|
||||
}
|
||||
DispatchTimeMarchesOn();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -380,5 +417,301 @@ TextTrackManager::HandleEvent(nsIDOMEvent* aEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
class SimpleTextTrackEvent : public Runnable
|
||||
{
|
||||
public:
|
||||
friend class CompareSimpleTextTrackEvents;
|
||||
SimpleTextTrackEvent(const nsAString& aEventName, double aTime,
|
||||
TextTrack* aTrack, TextTrackCue* aCue)
|
||||
: mName(aEventName),
|
||||
mTime(aTime),
|
||||
mTrack(aTrack),
|
||||
mCue(aCue)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mCue->DispatchTrustedEvent(mName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mName;
|
||||
double mTime;
|
||||
TextTrack* mTrack;
|
||||
RefPtr<TextTrackCue> mCue;
|
||||
};
|
||||
|
||||
class CompareSimpleTextTrackEvents {
|
||||
private:
|
||||
int32_t TrackChildPosition(SimpleTextTrackEvent* aEvent) const
|
||||
{
|
||||
HTMLTrackElement* trackElement = aEvent->mTrack->GetTrackElement();;
|
||||
if (!trackElement) {
|
||||
return -1;
|
||||
}
|
||||
return mMediaElement->IndexOf(trackElement);
|
||||
}
|
||||
HTMLMediaElement* mMediaElement;
|
||||
public:
|
||||
explicit CompareSimpleTextTrackEvents(HTMLMediaElement* aMediaElement)
|
||||
{
|
||||
mMediaElement = aMediaElement;
|
||||
}
|
||||
|
||||
bool Equals(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LessThan(SimpleTextTrackEvent* aOne, SimpleTextTrackEvent* aTwo) const
|
||||
{
|
||||
if (aOne->mTime < aTwo->mTime) {
|
||||
return true;
|
||||
} else if (aOne->mTime > aTwo->mTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t positionOne = TrackChildPosition(aOne);
|
||||
int32_t positionTwo = TrackChildPosition(aTwo);
|
||||
if (positionOne < positionTwo) {
|
||||
return true;
|
||||
} else if (positionOne > positionTwo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aOne->mName.EqualsLiteral("enter") ||
|
||||
aTwo->mName.EqualsLiteral("exit")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class TextTrackListInternal
|
||||
{
|
||||
public:
|
||||
void AddTextTrack(TextTrack* aTextTrack,
|
||||
const CompareTextTracks& aCompareTT)
|
||||
{
|
||||
if (!mTextTracks.Contains(aTextTrack)) {
|
||||
mTextTracks.InsertElementSorted(aTextTrack, aCompareTT);
|
||||
}
|
||||
}
|
||||
uint32_t Length() const
|
||||
{
|
||||
return mTextTracks.Length();
|
||||
}
|
||||
TextTrack* operator[](uint32_t aIndex)
|
||||
{
|
||||
return mTextTracks.SafeElementAt(aIndex, nullptr);
|
||||
}
|
||||
private:
|
||||
nsTArray<RefPtr<TextTrack>> mTextTracks;
|
||||
};
|
||||
|
||||
void
|
||||
TextTrackManager::DispatchTimeMarchesOn()
|
||||
{
|
||||
// Run the algorithm if no previous instance is still running, otherwise
|
||||
// enqueue the current playback position and whether only that changed
|
||||
// through its usual monotonic increase during normal playback; current
|
||||
// executing call upon completion will check queue for further 'work'.
|
||||
if (!mTimeMarchesOnDispatched && !mShutdown) {
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::TimeMarchesOn));
|
||||
mTimeMarchesOnDispatched = true;
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#time-marches-on
|
||||
void
|
||||
TextTrackManager::TimeMarchesOn()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
mTimeMarchesOnDispatched = false;
|
||||
|
||||
nsISupports* parentObject =
|
||||
mMediaElement->OwnerDoc()->GetParentObject();
|
||||
if (NS_WARN_IF(!parentObject)) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(parentObject);
|
||||
|
||||
if (mMediaElement &&
|
||||
(!(mMediaElement->GetPlayedOrSeeked())|| mMediaElement->Seeking())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
double currentPlaybackTime = mMediaElement->CurrentTime();
|
||||
bool hasNormalPlayback = !mHasSeeked;
|
||||
mHasSeeked = false;
|
||||
|
||||
// Step 1, 2.
|
||||
RefPtr<TextTrackCueList> currentCues =
|
||||
new TextTrackCueList(window);
|
||||
RefPtr<TextTrackCueList> otherCues =
|
||||
new TextTrackCueList(window);
|
||||
bool dummy;
|
||||
for (uint32_t index = 0; index < mTextTracks->Length(); ++index) {
|
||||
TextTrack* ttrack = mTextTracks->IndexedGetter(index, dummy);
|
||||
if (ttrack && dummy) {
|
||||
// TODO: call GetCueListByTimeInterval on mNewCues?
|
||||
TextTrackCueList* activeCueList = ttrack->GetActiveCues();
|
||||
if (activeCueList) {
|
||||
for (uint32_t i = 0; i < activeCueList->Length(); ++i) {
|
||||
currentCues->AddCue(*((*activeCueList)[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Populate otherCues with 'non-active" cues.
|
||||
if (hasNormalPlayback) {
|
||||
media::Interval<double> interval(mLastTimeMarchesOnCalled,
|
||||
currentPlaybackTime);
|
||||
otherCues = mNewCues->GetCueListByTimeInterval(interval);;
|
||||
} else {
|
||||
// Seek case. Put the mLastActiveCues into otherCues.
|
||||
otherCues = mLastActiveCues;
|
||||
}
|
||||
for (uint32_t i = 0; i < currentCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*currentCues)[i];
|
||||
otherCues->RemoveCue(*cue);
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
RefPtr<TextTrackCueList> missedCues = new TextTrackCueList(window);
|
||||
if (hasNormalPlayback) {
|
||||
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*otherCues)[i];
|
||||
if (cue->StartTime() >= mLastTimeMarchesOnCalled &&
|
||||
cue->EndTime() <= currentPlaybackTime) {
|
||||
missedCues->AddCue(*cue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5. Empty now.
|
||||
// TODO: Step 6: fire timeupdate?
|
||||
|
||||
// Step 7. Abort steps if condition 1, 2, 3 are satisfied.
|
||||
// 1. All of the cues in current cues have their active flag set.
|
||||
// 2. None of the cues in other cues have their active flag set.
|
||||
// 3. Missed cues is empty.
|
||||
bool c1 = true;
|
||||
for (uint32_t i = 0; i < currentCues->Length(); ++i) {
|
||||
if (!(*currentCues)[i]->GetActive()) {
|
||||
c1 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool c2 = true;
|
||||
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
|
||||
if ((*otherCues)[i]->GetActive()) {
|
||||
c2 = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool c3 = (missedCues->Length() == 0);
|
||||
if (c1 && c2 && c3) {
|
||||
mLastTimeMarchesOnCalled = currentPlaybackTime;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 8. Respect PauseOnExit flag if not seek.
|
||||
if (hasNormalPlayback) {
|
||||
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*otherCues)[i];
|
||||
if (cue && cue->PauseOnExit() && cue->GetActive()) {
|
||||
mMediaElement->Pause();
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < missedCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*missedCues)[i];
|
||||
if (cue && cue->PauseOnExit()) {
|
||||
mMediaElement->Pause();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 15.
|
||||
// Sort text tracks in the same order as the text tracks appear
|
||||
// in the media element's list of text tracks, and remove
|
||||
// duplicates.
|
||||
TextTrackListInternal affectedTracks;
|
||||
// Step 13, 14.
|
||||
nsTArray<RefPtr<SimpleTextTrackEvent>> eventList;
|
||||
// Step 9, 10.
|
||||
// For each text track cue in missed cues, prepare an event named
|
||||
// enter for the TextTrackCue object with the cue start time.
|
||||
for (uint32_t i = 0; i < missedCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*missedCues)[i];
|
||||
if (cue) {
|
||||
SimpleTextTrackEvent* event =
|
||||
new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"),
|
||||
cue->StartTime(), cue->GetTrack(),
|
||||
cue);
|
||||
eventList.InsertElementSorted(event,
|
||||
CompareSimpleTextTrackEvents(mMediaElement));
|
||||
affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 11, 17.
|
||||
for (uint32_t i = 0; i < otherCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*otherCues)[i];
|
||||
if (cue->GetActive() ||
|
||||
missedCues->GetCueById(cue->Id()) != nullptr) {
|
||||
double time = cue->StartTime() > cue->EndTime() ? cue->StartTime()
|
||||
: cue->EndTime();
|
||||
SimpleTextTrackEvent* event =
|
||||
new SimpleTextTrackEvent(NS_LITERAL_STRING("exit"), time,
|
||||
cue->GetTrack(), cue);
|
||||
eventList.InsertElementSorted(event,
|
||||
CompareSimpleTextTrackEvents(mMediaElement));
|
||||
affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
|
||||
}
|
||||
cue->SetActive(false);
|
||||
}
|
||||
|
||||
// Step 12, 17.
|
||||
for (uint32_t i = 0; i < currentCues->Length(); ++i) {
|
||||
TextTrackCue* cue = (*currentCues)[i];
|
||||
if (!cue->GetActive()) {
|
||||
SimpleTextTrackEvent* event =
|
||||
new SimpleTextTrackEvent(NS_LITERAL_STRING("enter"),
|
||||
cue->StartTime(), cue->GetTrack(),
|
||||
cue);
|
||||
eventList.InsertElementSorted(event,
|
||||
CompareSimpleTextTrackEvents(mMediaElement));
|
||||
affectedTracks.AddTextTrack(cue->GetTrack(), CompareTextTracks(mMediaElement));
|
||||
}
|
||||
cue->SetActive(true);
|
||||
}
|
||||
|
||||
// Fire the eventList
|
||||
for (uint32_t i = 0; i < eventList.Length(); ++i) {
|
||||
NS_DispatchToMainThread(eventList[i].forget());
|
||||
}
|
||||
|
||||
// Step 16.
|
||||
for (uint32_t i = 0; i < affectedTracks.Length(); ++i) {
|
||||
TextTrack* ttrack = affectedTracks[i];
|
||||
if (ttrack) {
|
||||
ttrack->DispatchTrustedEvent(NS_LITERAL_STRING("cuechange"));
|
||||
HTMLTrackElement* trackElement = ttrack->GetTrackElement();
|
||||
if (trackElement) {
|
||||
trackElement->DispatchTrackRunnable(NS_LITERAL_STRING("cuechange"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mLastTimeMarchesOnCalled = currentPlaybackTime;
|
||||
mLastActiveCues = currentCues;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/dom/TextTrackList.h"
|
||||
#include "mozilla/dom/TextTrackCueList.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
class nsIWebVTTParserWrapper;
|
||||
|
||||
|
@ -22,9 +23,9 @@ class HTMLMediaElement;
|
|||
class CompareTextTracks {
|
||||
private:
|
||||
HTMLMediaElement* mMediaElement;
|
||||
int32_t TrackChildPosition(TextTrack* aTrack) const;
|
||||
public:
|
||||
explicit CompareTextTracks(HTMLMediaElement* aMediaElement);
|
||||
int32_t TrackChildPosition(TextTrack* aTrack) const;
|
||||
bool Equals(TextTrack* aOne, TextTrack* aTwo) const;
|
||||
bool LessThan(TextTrack* aOne, TextTrack* aTwo) const;
|
||||
};
|
||||
|
@ -57,7 +58,7 @@ public:
|
|||
|
||||
void AddCue(TextTrackCue& aCue);
|
||||
void AddCues(TextTrack* aTextTrack);
|
||||
|
||||
void NotifyCueRemoved(TextTrackCue& aCue);
|
||||
/**
|
||||
* Overview of WebVTT cuetext and anonymous content setup.
|
||||
*
|
||||
|
@ -94,13 +95,35 @@ public:
|
|||
|
||||
// The HTMLMediaElement that this TextTrackManager manages the TextTracks of.
|
||||
RefPtr<HTMLMediaElement> mMediaElement;
|
||||
|
||||
void DispatchTimeMarchesOn();
|
||||
|
||||
void NotifyShutdown()
|
||||
{
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
private:
|
||||
void TimeMarchesOn();
|
||||
|
||||
// List of the TextTrackManager's owning HTMLMediaElement's TextTracks.
|
||||
RefPtr<TextTrackList> mTextTracks;
|
||||
// List of text track objects awaiting loading.
|
||||
RefPtr<TextTrackList> mPendingTextTracks;
|
||||
// List of newly introduced Text Track cues.
|
||||
|
||||
// Contain all cues for a MediaElement.
|
||||
RefPtr<TextTrackCueList> mNewCues;
|
||||
// The active cues for the last TimeMarchesOn iteration.
|
||||
RefPtr<TextTrackCueList> mLastActiveCues;
|
||||
|
||||
// True if the media player playback changed due to seeking prior to and
|
||||
// during running the "Time Marches On" algorithm.
|
||||
bool mHasSeeked;
|
||||
// Playback position at the time of last "Time Marches On" call
|
||||
double mLastTimeMarchesOnCalled;
|
||||
|
||||
bool mTimeMarchesOnDispatched;
|
||||
|
||||
static StaticRefPtr<nsIWebVTTParserWrapper> sParserWrapper;
|
||||
|
||||
|
@ -118,6 +141,35 @@ private:
|
|||
void GetTextTracksOfKind(TextTrackKind aTextTrackKind,
|
||||
nsTArray<TextTrack*>& aTextTracks);
|
||||
bool TrackIsDefault(TextTrack* aTextTrack);
|
||||
|
||||
class ShutdownObserverProxy final : public nsIObserver
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
public:
|
||||
explicit ShutdownObserverProxy(TextTrackManager* aManager)
|
||||
: mManager(aManager)
|
||||
{
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
mManager->NotifyShutdown();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~ShutdownObserverProxy() {};
|
||||
TextTrackManager* mManager;
|
||||
};
|
||||
|
||||
RefPtr<ShutdownObserverProxy> mShutdownProxy;
|
||||
bool mShutdown;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -478,19 +478,26 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # just copy the conditions f
|
|||
[test_html_attributes_reflection.html]
|
||||
[test_htmlcollection.html]
|
||||
[test_iframe_sandbox_general.html]
|
||||
tags = openwindow
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
||||
[test_iframe_sandbox_inheritance.html]
|
||||
tags = openwindow
|
||||
[test_iframe_sandbox_modal.html]
|
||||
tags = openwindow
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #modal tests fail on android # b2g(modal tests fail on B2G) b2g-debug(modal tests fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
|
||||
[test_iframe_sandbox_navigation.html]
|
||||
tags = openwindow
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #time out on b2g desktop specific
|
||||
[test_iframe_sandbox_navigation2.html]
|
||||
tags = openwindow
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #time out on b2g desktop specific
|
||||
[test_iframe_sandbox_plugins.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(plugins not supported) b2g-debug(plugins not supported) b2g-desktop(plugins not supported)
|
||||
[test_iframe_sandbox_popups.html]
|
||||
tags = openwindow
|
||||
skip-if = buildapp == 'b2g' # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
|
||||
[test_iframe_sandbox_popups_inheritance.html]
|
||||
tags = openwindow
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage) android(bug 939642)
|
||||
[test_iframe_sandbox_redirect.html]
|
||||
[test_iframe_sandbox_refresh.html]
|
||||
|
@ -599,6 +606,7 @@ support-files = file_bug871161-1.html file_bug871161-2.html
|
|||
[test_hash_encoded.html]
|
||||
[test_bug1081037.html]
|
||||
[test_window_open_close.html]
|
||||
tags = openwindow
|
||||
skip-if = buildapp == 'b2g' # bug 1129014
|
||||
[test_img_complete.html]
|
||||
[test_viewport_resize.html]
|
||||
|
|
|
@ -202,21 +202,12 @@ function guessKeyNameFromKeyCode(KeyboardEvent, aKeyCode) {
|
|||
return "NumLock";
|
||||
case KeyboardEvent.DOM_VK_SCROLL_LOCK:
|
||||
return "ScrollLock";
|
||||
#ifndef MOZ_B2G
|
||||
case KeyboardEvent.DOM_VK_VOLUME_MUTE:
|
||||
return "AudioVolumeMute";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_DOWN:
|
||||
return "AudioVolumeDown";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_UP:
|
||||
return "AudioVolumeUp";
|
||||
#else
|
||||
case KeyboardEvent.DOM_VK_VOLUME_MUTE:
|
||||
return "VolumeMute";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_DOWN:
|
||||
return "VolumeDown";
|
||||
case KeyboardEvent.DOM_VK_VOLUME_UP:
|
||||
return "VolumeUp";
|
||||
#endif
|
||||
case KeyboardEvent.DOM_VK_META:
|
||||
return "Meta";
|
||||
case KeyboardEvent.DOM_VK_ALTGR:
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче