Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2016-06-03 12:40:11 +02:00
Родитель 701f112af0 71eb3110f9
Коммит 2a6cd87e82
601 изменённых файлов: 32123 добавлений и 6799 удалений

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

@ -179,3 +179,5 @@ end
def ft
call $arg0->DumpFrameTree()
end
source .gdbinit_python

5
.gdbinit_python Normal file
Просмотреть файл

@ -0,0 +1,5 @@
python
import sys
sys.path.append('python/gdbpp/')
import gdbpp
end

3
.gitignore поставляемый
Просмотреть файл

@ -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);
}
}

6
build/.gdbinit_python.in Normal file
Просмотреть файл

@ -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

291
dom/canvas/ImageUtils.cpp Normal file
Просмотреть файл

@ -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

73
dom/canvas/ImageUtils.h Normal file
Просмотреть файл

@ -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

1127
dom/flyweb/FlyWebService.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

109
dom/flyweb/FlyWebService.h Normal file
Просмотреть файл

@ -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

1315
dom/flyweb/HttpServer.cpp Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

192
dom/flyweb/HttpServer.h Normal file
Просмотреть файл

@ -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

36
dom/flyweb/moz.build Normal file
Просмотреть файл

@ -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:

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше