merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-06-15 06:24:33 +01:00
commit 58167fd092
254 changed files with 9540 additions and 2946 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Merge day clobber
Bug 1276696 - New Android dependencies

View File

@ -90,19 +90,6 @@ DocAccessible::UpdateText(nsIContent* aTextNode)
mNotificationController->ScheduleTextUpdate(aTextNode);
}
inline void
DocAccessible::UpdateRootElIfNeeded()
{
dom::Element* rootEl = mDocumentNode->GetBodyElement();
if (!rootEl) {
rootEl = mDocumentNode->GetRootElement();
}
if (rootEl != mContent) {
mContent = rootEl;
SetRoleMapEntry(aria::GetRoleMap(rootEl));
}
}
inline void
DocAccessible::AddScrollListener()
{

View File

@ -1599,6 +1599,10 @@ DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
// the document has loaded. In this case we just update the role map entry.
if (mContent == aElement) {
SetRoleMapEntry(aria::GetRoleMap(aElement));
if (mIPCDoc) {
mIPCDoc->SendRoleChangedEvent(Role());
}
return true;
}
@ -1634,6 +1638,22 @@ DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return false;
}
void
DocAccessible::UpdateRootElIfNeeded()
{
dom::Element* rootEl = mDocumentNode->GetBodyElement();
if (!rootEl) {
rootEl = mDocumentNode->GetRootElement();
}
if (rootEl != mContent) {
mContent = rootEl;
SetRoleMapEntry(aria::GetRoleMap(rootEl));
if (mIPCDoc) {
mIPCDoc->SendRoleChangedEvent(Role());
}
}
}
/**
* Content insertion helper.
*/

View File

@ -326,6 +326,18 @@ DocAccessibleParent::RecvSelectionEvent(const uint64_t& aID,
return true;
}
bool
DocAccessibleParent::RecvRoleChangedEvent(const uint32_t& aRole)
{
if (aRole >= roles::LAST_ROLE) {
NS_ERROR("child sent bad role in RoleChangedEvent");
return false;
}
mRole = static_cast<a11y::role>(aRole);
return true;
}
bool
DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
{

View File

@ -70,6 +70,8 @@ public:
const uint64_t& aWidgetID,
const uint32_t& aType) override;
virtual bool RecvRoleChangedEvent(const uint32_t& aRole) override final;
virtual bool RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
void Unbind()
{

View File

@ -62,6 +62,7 @@ parent:
async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
bool aIsInsert, bool aFromUser);
async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType);
async RoleChangedEvent(uint32_t aRole);
/*
* Tell the parent document to bind the existing document as a new child

View File

@ -431,7 +431,11 @@ private:
DocAccessibleParent* mDoc;
uintptr_t mWrapper;
uint64_t mID;
protected:
// XXX DocAccessibleParent gets to change this to change the role of
// documents.
role mRole : 29;
private:
bool mOuterDoc : 1;
public:

View File

@ -1343,7 +1343,11 @@ pref("privacy.trackingprotection.introCount", 0);
pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/");
// Enable Contextual Identity Containers
#ifdef NIGHTLY_BUILD
pref("privacy.userContext.enabled", true);
#else
pref("privacy.userContext.enabled", false);
#endif
#ifndef RELEASE_BUILD
// At the moment, autostart.2 is used, while autostart.1 is unused.

View File

@ -12,6 +12,7 @@ let LOGIN_FILL_ITEMS = [
];
let hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
let hasContainers = Services.prefs.getBoolPref("privacy.userContext.enabled");
add_task(function* test_setup() {
const example_base = "http://example.com/browser/browser/base/content/test/general/";
@ -62,6 +63,10 @@ add_task(function* test_plaintext() {
add_task(function* test_link() {
yield test_contextmenu("#test-link",
["context-openlinkintab", true,
...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
// We need a blank entry here because the containers submenu is
// dynamically generated with no ids.
...(hasContainers ? ["", null] : []),
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,
@ -622,6 +627,10 @@ add_task(function* test_select_text_link() {
yield test_contextmenu("#test-select-text-link",
["context-openlinkincurrent", true,
"context-openlinkintab", true,
...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
// We need a blank entry here because the containers submenu is
// dynamically generated with no ids.
...(hasContainers ? ["", null] : []),
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,
@ -652,6 +661,10 @@ add_task(function* test_select_text_link() {
add_task(function* test_imagelink() {
yield test_contextmenu("#test-image-link",
["context-openlinkintab", true,
...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
// We need a blank entry here because the containers submenu is
// dynamically generated with no ids.
...(hasContainers ? ["", null] : []),
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,

View File

@ -20,4 +20,3 @@ tags = openwindow
[browser_broadcastchannel.js]
[browser_blobUrl.js]
[browser_middleClick.js]
[browser_imageCache.js]

View File

@ -1,59 +0,0 @@
let Cu = Components.utils;
let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
const NUM_USER_CONTEXTS = 3;
let gHits = 0;
let server = new HttpServer();
server.registerPathHandler('/image.png', imageHandler);
server.registerPathHandler('/file.html', fileHandler);
server.start(-1);
let BASE_URI = 'http://localhost:' + server.identity.primaryPort;
let IMAGE_URI = BASE_URI + '/image.png';
let FILE_URI = BASE_URI + '/file.html';
function imageHandler(metadata, response) {
gHits++;
response.setHeader("Cache-Control", "max-age=10000", false);
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "image/png", false);
var body = "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
response.bodyOutputStream.write(body, body.length);
}
function fileHandler(metadata, response) {
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/html", false);
let body = `<html><body><image src=${IMAGE_URI}></body></html>`;
response.bodyOutputStream.write(body, body.length);
}
add_task(function* setup() {
// make sure userContext is enabled.
yield SpecialPowers.pushPrefEnv({"set": [["privacy.userContext.enabled", true]]});
});
// opens `uri' in a new tab with the provided userContextId and focuses it.
// returns the newly opened tab
function* openTabInUserContext(uri, userContextId) {
// open the tab in the correct userContextId
let tab = gBrowser.addTab(uri, {userContextId});
// select tab and make sure its browser is focused
gBrowser.selectedTab = tab;
tab.ownerDocument.defaultView.focus();
let browser = gBrowser.getBrowserForTab(tab);
yield BrowserTestUtils.browserLoaded(browser);
return tab;
}
add_task(function* test() {
for (let userContextId = 0; userContextId < NUM_USER_CONTEXTS; userContextId++) {
let tab = yield* openTabInUserContext(FILE_URI, userContextId);
gBrowser.removeTab(tab);
}
is(gHits, NUM_USER_CONTEXTS, "should get an image request for each user contexts");
});

View File

@ -457,7 +457,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY openFrameCmd.label "Open Frame in New Window">
<!ENTITY openFrameCmd.accesskey "W">
<!ENTITY openLinkCmdInContainerTab.label "Open Link in New Container Tab">
<!ENTITY openLinkCmdInContainerTab.accesskey "C">
<!ENTITY openLinkCmdInContainerTab.accesskey "z">
<!ENTITY showOnlyThisFrameCmd.label "Show Only This Frame">
<!ENTITY showOnlyThisFrameCmd.accesskey "S">
<!ENTITY reloadCmd.commandkey "r">

View File

@ -341,6 +341,7 @@ case "$target" in
AC_SUBST(ANDROID_TOOLS)
AC_SUBST(ANDROID_BUILD_TOOLS_VERSION)
MOZ_ANDROID_AAR(customtabs, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(appcompat-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(cardview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(design, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)

View File

@ -903,6 +903,7 @@ ReadFormData(JSContext* aCx,
ErrorResult rv;
formData->Append(name, *blob, thirdArg, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return nullptr;
}
@ -920,6 +921,7 @@ ReadFormData(JSContext* aCx,
ErrorResult rv;
formData->Append(name, value, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return nullptr;
}
}
@ -1066,6 +1068,11 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
{
Directory* directory = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Directory, aObj, directory))) {
if (mSupportedContext != SameProcessSameThread &&
!directory->ClonableToDifferentThreadOrProcess()) {
return false;
}
return WriteDirectory(aWriter, directory);
}
}
@ -1120,6 +1127,7 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
RefPtr<MessagePort> port =
MessagePort::Create(global, portIdentifier, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
}

View File

@ -1069,9 +1069,9 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
return 0;
}
bool negate = false;
int sign = 1;
if (*iter == char16_t('-')) {
negate = true;
sign = -1;
++iter;
} else if (*iter == char16_t('+')) {
result |= eParseHTMLInteger_NonStandard;
@ -1095,7 +1095,7 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
while (iter != end) {
if (*iter >= char16_t('0') && *iter <= char16_t('9')) {
value = (value * 10) + (*iter - char16_t('0'));
value = (value * 10) + (*iter - char16_t('0')) * sign;
++iter;
if (!value.isValid()) {
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
@ -1116,16 +1116,9 @@ nsContentUtils::ParseHTMLInteger(const nsAString& aValue,
result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
}
if (value.isValid() && negate) {
value = -value;
// Checking the special case of -0.
if (value == 0) {
result |= eParseHTMLInteger_NonStandard;
}
}
if (value.isValid() &&
(leadingZeros > 1 || (leadingZeros == 1 && !(value == 0)))) {
((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
(sign == -1 && value == 0))) {
result |= eParseHTMLInteger_NonStandard;
}

View File

@ -303,8 +303,9 @@ const kEventConstructors = {
return new MediaStreamEvent(aName, aProps);
},
},
MediaStreamTrackEvent: {
// Difficult to test required arguments.
MediaStreamTrackEvent: { create: function (aName, aProps) {
return new MediaStreamTrackEvent(aName, aProps);
},
},
MessageEvent: { create: function (aName, aProps) {
var e = new MessageEvent("messageevent", { bubbles: aProps.bubbles,

View File

@ -1466,19 +1466,19 @@ HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv)
{
LOG(LogLevel::Debug, ("Reporting telemetry VIDEO_FASTSEEK_USED"));
Telemetry::Accumulate(Telemetry::VIDEO_FASTSEEK_USED, 1);
Seek(aTime, SeekTarget::PrevSyncPoint, aRv);
RefPtr<Promise> tobeDropped = Seek(aTime, SeekTarget::PrevSyncPoint, aRv);
}
void
already_AddRefed<Promise>
HTMLMediaElement::SeekToNextFrame(ErrorResult& aRv)
{
Seek(CurrentTime(), SeekTarget::NextFrame, aRv);
return Seek(CurrentTime(), SeekTarget::NextFrame, aRv);
}
void
HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv)
{
Seek(aCurrentTime, SeekTarget::Accurate, aRv);
RefPtr<Promise> tobeDropped = Seek(aCurrentTime, SeekTarget::Accurate, aRv);
}
/**
@ -1519,7 +1519,7 @@ IsInRanges(dom::TimeRanges& aRanges,
return NS_OK;
}
void
already_AddRefed<Promise>
HTMLMediaElement::Seek(double aTime,
SeekTarget::Type aSeekType,
ErrorResult& aRv)
@ -1527,6 +1527,19 @@ HTMLMediaElement::Seek(double aTime,
// aTime should be non-NaN.
MOZ_ASSERT(!mozilla::IsNaN(aTime));
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
// Detect if user has interacted with element by seeking so that
// play will not be blocked when initiated by a script.
if (EventStateManager::IsHandlingUserInput() || nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
@ -1537,7 +1550,8 @@ HTMLMediaElement::Seek(double aTime,
if (mSrcStream) {
// do nothing since media streams have an empty Seekable range.
return;
promise->MaybeRejectWithUndefined();
return promise.forget();
}
if (mPlayed && mCurrentPlayRangeStart != -1.0) {
@ -1554,27 +1568,30 @@ HTMLMediaElement::Seek(double aTime,
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mDefaultPlaybackStartPosition = aTime;
return;
promise->MaybeRejectWithUndefined();
return promise.forget();
}
if (!mDecoder) {
// mDecoder must always be set in order to reach this point.
NS_ASSERTION(mDecoder, "SetCurrentTime failed: no decoder");
return;
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// Clamp the seek target to inside the seekable ranges.
RefPtr<dom::TimeRanges> seekable = new dom::TimeRanges(ToSupports(OwnerDoc()));
media::TimeIntervals seekableIntervals = mDecoder->GetSeekable();
if (seekableIntervals.IsInvalid()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); // This will reject the promise.
return promise.forget();
}
seekableIntervals.ToTimeRanges(seekable);
uint32_t length = 0;
seekable->GetLength(&length);
if (!length) {
return;
promise->MaybeRejectWithUndefined();
return promise.forget();
}
// If the position we want to seek to is not in a seekable range, we seek
@ -1585,8 +1602,8 @@ HTMLMediaElement::Seek(double aTime,
int32_t range = 0;
bool isInRange = false;
if (NS_FAILED(IsInRanges(*seekable, aTime, isInRange, range))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); // This will reject the promise.
return promise.forget();
}
if (!isInRange) {
if (range != -1) {
@ -1596,11 +1613,11 @@ HTMLMediaElement::Seek(double aTime,
double leftBound, rightBound;
if (NS_FAILED(seekable->End(range, &leftBound))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
return promise.forget();
}
if (NS_FAILED(seekable->Start(range + 1, &rightBound))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
return promise.forget();
}
double distanceLeft = Abs(leftBound - aTime);
double distanceRight = Abs(rightBound - aTime);
@ -1615,7 +1632,7 @@ HTMLMediaElement::Seek(double aTime,
// Clamp the seek target to the end of the last seekable range.
if (NS_FAILED(seekable->End(length - 1, &aTime))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
return promise.forget();
}
}
} else {
@ -1641,13 +1658,15 @@ HTMLMediaElement::Seek(double aTime,
// The media backend is responsible for dispatching the timeupdate
// event if it changes the playback position as a result of the seek.
LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) starting seek", this, aTime));
nsresult rv = mDecoder->Seek(aTime, aSeekType);
nsresult rv = mDecoder->Seek(aTime, aSeekType, promise);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
// We changed whether we're seeking so we need to AddRemoveSelfReference.
AddRemoveSelfReference();
return promise.forget();
}
NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)

View File

@ -482,7 +482,7 @@ public:
void FastSeek(double aTime, ErrorResult& aRv);
void SeekToNextFrame(ErrorResult& aRv);
already_AddRefed<Promise> SeekToNextFrame(ErrorResult& aRv);
double Duration() const;
@ -1125,7 +1125,7 @@ protected:
// seek target, or PrevSyncPoint if a quicker but less precise seek is
// desired, and we'll seek to the sync point (keyframe and/or start of the
// next block of audio samples) preceeding seek target.
void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
already_AddRefed<Promise> Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
// A method to check if we are playing through the AudioChannel.
bool IsPlayingThroughTheAudioChannel() const;

View File

@ -566,14 +566,8 @@ function reflectInt(aParameters)
is(element.getAttribute(attr), expectedGetAttributeResult(v), element.localName + ".setAttribute(" +
attr + ", " + v + "), " + element.localName + ".getAttribute(" + attr + ") ");
if (intValue == -2147483648 && element[attr] == defaultValue) {
//TBD: Bug 586761: .setAttribute(attr, -2147483648) --> element[attr] == defaultValue instead of -2147483648
todo_is(element[attr], intValue, "Bug 586761: " + element.localName +
".setAttribute(value, " + v + "), " + element.localName + "[" + attr + "] ");
} else {
is(element[attr], intValue, element.localName +
".setAttribute(" + attr + ", " + v + "), " + element.localName + "[" + attr + "] ");
}
is(element[attr], intValue, element.localName +
".setAttribute(" + attr + ", " + v + "), " + element.localName + "[" + attr + "] ");
element.removeAttribute(attr);
if (nonNegative && expectedIdlAttributeResult(v) < 0) {
@ -588,17 +582,11 @@ function reflectInt(aParameters)
}
} else {
element[attr] = v;
if (expectedIdlAttributeResult(v) == -2147483648 && element[attr] == defaultValue) {
//TBD: Bug 586761: .setAttribute(attr, -2147483648) --> element[attr] == defaultValue instead of -2147483648
todo_is(element[attr], expectedIdlAttributeResult(v), "Bug 586761: " + element.localName + "[" +
attr + "] = " + v + ", " + element.localName + "[" + attr + "] ");
} else {
is(element[attr], expectedIdlAttributeResult(v), element.localName + "[" + attr + "] = " + v +
", " + element.localName + "[" + attr + "] ");
is(element.getAttribute(attr), String(expectedIdlAttributeResult(v)),
element.localName + "[" + attr + "] = " + v + ", " +
element.localName + ".getAttribute(" + attr + ") ");
}
is(element[attr], expectedIdlAttributeResult(v), element.localName + "[" + attr + "] = " + v +
", " + element.localName + "[" + attr + "] ");
is(element.getAttribute(attr), String(expectedIdlAttributeResult(v)),
element.localName + "[" + attr + "] = " + v + ", " +
element.localName + ".getAttribute(" + attr + ") ");
}
element.removeAttribute(attr);
});

View File

@ -1,8 +1,9 @@
[DEFAULT]
support-files =
manifestLoader.html
file_reg_install_event.html
file_testserver.sjs
manifestLoader.html
resource.sjs
[browser_ManifestFinder_browserHasManifestLink.js]
[browser_ManifestObtainer_obtain.js]
[browser_fire_install_event.js]

View File

@ -1,83 +1,54 @@
//Used by JSHint:
/*global Cu, BrowserTestUtils, is, ok, add_task, gBrowser, ManifestFinder */
/*global Cu, BrowserTestUtils, ok, add_task, gBrowser */
"use strict";
Cu.import("resource://gre/modules/ManifestFinder.jsm", this); // jshint ignore:line
const { ManifestFinder } = Cu.import("resource://gre/modules/ManifestFinder.jsm", {});
const defaultURL = new URL("http://example.org/browser/dom/manifest/test/resource.sjs");
defaultURL.searchParams.set("Content-Type", "text/html; charset=utf-8");
const defaultURL =
"http://example.org/tests/dom/manifest/test/resource.sjs";
const tests = [{
expected: "Document has a web manifest.",
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
"Content-Type=text/html; charset=utf-8",
];
const URL = `${defaultURL}?${query.join("&")}`;
return URL;
},
body: `
<link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>
<link rel="foo bar manifest bar test" href='${defaultURL}?body={"name":"value"}'>
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>
`,
run(result) {
is(result, true, this.expected);
ok(result, "Document has a web manifest.");
},
testData: `
<link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>
<link rel="foo bar manifest bar test" href='${defaultURL}?body={"name":"value"}'>
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
}, {
expected: "Document does not have a web manifest.",
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
"Content-Type=text/html; charset=utf-8",
];
const URL = `${defaultURL}?${query.join("&")}`;
return URL;
},
body: `
<link rel="amanifista" href='${defaultURL}?body={"name":"fail"}'>
<link rel="foo bar manifesto bar test" href='${defaultURL}?body={"name":"pass-1"}'>
<link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>`,
run(result) {
is(result, false, this.expected);
ok(!result, "Document does not have a web manifest.");
},
testData: `
<link rel="amanifista" href='${defaultURL}?body={"name":"fail"}'>
<link rel="foo bar manifesto bar test" href='${defaultURL}?body={"name":"pass-1"}'>
<link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>`
}, {
expected: "Manifest link is has empty href.",
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
"Content-Type=text/html; charset=utf-8",
];
const URL = `${defaultURL}?${query.join("&")}`;
return URL;
},
body: `
<link rel="manifest" href="">
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`,
run(result) {
is(result, false, this.expected);
ok(!result, "Manifest link is has empty href.");
},
testData: `
<link rel="manifest" href="">
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
}, {
expected: "Manifest link is missing.",
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
"Content-Type=text/html; charset=utf-8",
];
const URL = `${defaultURL}?${query.join("&")}`;
return URL;
},
run(result) {
is(result, false, this.expected);
},
testData: `
body: `
<link rel="manifest">
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`,
run(result) {
ok(!result, "Manifest link is missing.");
},
}];
function makeTestURL({ body }) {
const url = new URL(defaultURL);
url.searchParams.set("body", encodeURIComponent(body));
return url.href;
}
/**
* Test basic API error conditions
*/
add_task(function* () {
let expected = "Invalid types should throw a TypeError.";
add_task(function*() {
const expected = "Invalid types should throw a TypeError.";
for (let invalidValue of [undefined, null, 1, {}, "test"]) {
try {
yield ManifestFinder.contentManifestLink(invalidValue);
@ -94,21 +65,20 @@ add_task(function* () {
}
});
add_task(function* () {
for (let test of tests) {
let tabOptions = {
gBrowser: gBrowser,
url: test.tabURL,
};
yield BrowserTestUtils.withNewTab(
tabOptions,
browser => testHasManifest(browser, test)
add_task(function*() {
const runningTests = tests
.map(
test => ({
gBrowser,
test,
url: makeTestURL(test),
})
)
.map(
tabOptions => BrowserTestUtils.withNewTab(tabOptions, function*(browser) {
const result = yield ManifestFinder.browserHasManifestLink(browser);
tabOptions.test.run(result);
})
);
}
function* testHasManifest(aBrowser, aTest) {
aBrowser.contentWindowAsCPOW.document.head.innerHTML = aTest.testData;
const result = yield ManifestFinder.browserHasManifestLink(aBrowser);
aTest.run(result);
}
yield Promise.all(runningTests);
});

View File

@ -1,104 +1,54 @@
//Used by JSHint:
/*global requestLongerTimeout, Cu, BrowserTestUtils, add_task, SpecialPowers, gBrowser, Assert*/ 'use strict';
const {
ManifestObtainer
} = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {});
/*global ok, is, Cu, BrowserTestUtils, add_task, gBrowser, makeTestURL, requestLongerTimeout*/
'use strict';
const { ManifestObtainer } = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {});
const remoteURL = 'http://mochi.test:8888/browser/dom/manifest/test/resource.sjs';
const defaultURL = new URL('http://example.org/browser/dom/manifest/test/resource.sjs');
defaultURL.searchParams.set('Content-Type', 'text/html; charset=utf-8');
requestLongerTimeout(4);
requestLongerTimeout(4); // e10s tests take time.
const defaultURL =
'http://example.org/tests/dom/manifest/test/resource.sjs';
const remoteURL =
'http://mochi.test:8888/tests/dom/manifest/test/resource.sjs';
const tests = [
// Fetch tests.
{
expected: 'Manifest is first `link` where @rel contains token manifest.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
body: `
<link rel="manifesto" href='resource.sjs?body={"name":"fail"}'>
<link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-1"}'>
<link rel="manifest" href='resource.sjs?body={"name":"fail"}'>`,
run(manifest) {
Assert.strictEqual(manifest.name, 'pass-1', this.expected);
},
testData: `
<link rel="manifesto" href='${defaultURL}?body={"name":"fail"}'>
<link rel="foo bar manifest bar test" href='${defaultURL}?body={"name":"pass-1"}'>
<link rel="manifest" href='${defaultURL}?body={"name":"fail"}'>`
is(manifest.name, 'pass-1', 'Manifest is first `link` where @rel contains token manifest.');
}
}, {
expected: 'Manifest is first `link` where @rel contains token manifest.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
run(manifest) {
Assert.strictEqual(manifest.name, 'pass-2', this.expected);
},
testData: `
body: `
<link rel="foo bar manifest bar test" href='resource.sjs?body={"name":"pass-2"}'>
<link rel="manifest" href='resource.sjs?body={"name":"fail"}'>
<link rel="manifest foo bar test" href='resource.sjs?body={"name":"fail"}'>`
<link rel="manifest foo bar test" href='resource.sjs?body={"name":"fail"}'>`,
run(manifest) {
is(manifest.name, 'pass-2', 'Manifest is first `link` where @rel contains token manifest.');
},
}, {
expected: 'By default, manifest cannot load cross-origin.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
body: `<link rel="manifest" href='${remoteURL}?body={"name":"pass-3"}'>`,
run(err) {
Assert.strictEqual(err.name, 'TypeError', this.expected);
is(err.name, 'TypeError', 'By default, manifest cannot load cross-origin.');
},
testData: `<link rel="manifest" href='${remoteURL}?body={"name":"pass-3"}'>`
},
// CORS Tests.
{
expected: 'CORS enabled, manifest must be fetched.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
run(manifest) {
Assert.strictEqual(manifest.name, 'pass-4', this.expected);
},
get testData() {
get body() {
const body = 'body={"name": "pass-4"}';
const CORS =
`Access-Control-Allow-Origin=${new URL(this.tabURL).origin}`;
`Access-Control-Allow-Origin=${defaultURL.origin}`;
const link =
`<link
crossorigin=anonymous
rel="manifest"
href='${remoteURL}?${body}&${CORS}'>`;
return link;
}
},
run(manifest) {
is(manifest.name, 'pass-4', 'CORS enabled, manifest must be fetched.');
},
}, {
expected: 'Fetch blocked by CORS - origin does not match.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
run(err) {
Assert.strictEqual(err.name, 'TypeError', this.expected);
},
get testData() {
get body() {
const body = 'body={"name": "fail"}';
const CORS = 'Access-Control-Allow-Origin=http://not-here';
const link =
@ -107,82 +57,60 @@ const tests = [
rel="manifest"
href='${remoteURL}?${body}&${CORS}'>`;
return link;
}
},{
expected: 'Trying to load from about:whatever is a TypeError.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
run(err) {
Assert.strictEqual(err.name, 'TypeError', this.expected);
},
testData: `<link rel="manifest" href='about:whatever'>`
},
{
expected: 'Trying to load from file://whatever is a TypeError.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
is(err.name, 'TypeError', 'Fetch blocked by CORS - origin does not match.');
},
}, {
body: `<link rel="manifest" href='about:whatever'>`,
run(err) {
Assert.strictEqual(err.name, 'TypeError', this.expected);
is(err.name, 'TypeError', 'Trying to load from about:whatever is TypeError.');
},
}, {
body: `<link rel="manifest" href='file://manifest'>`,
run(err) {
is(err.name, 'TypeError', 'Trying to load from file://whatever is a TypeError.');
},
testData: `<link rel="manifest" href='file://manifest'>`
},
//URL parsing tests
{
expected: 'Trying to load invalid URL is a TypeError.',
get tabURL() {
let query = [
`body=<h1>${this.expected}</h1>`,
'Content-Type=text/html; charset=utf-8',
];
const URL = `${defaultURL}?${query.join('&')}`;
return URL;
},
body: `<link rel="manifest" href='http://[12.1212.21.21.12.21.12]'>`,
run(err) {
Assert.strictEqual(err.name, 'TypeError', this.expected);
is(err.name, 'TypeError', 'Trying to load invalid URL is a TypeError.');
},
testData: `<link rel="manifest" href='http://[12.1212.21.21.12.21.12]'>`
},
];
add_task(function*() {
yield new Promise(resolve => {
SpecialPowers.pushPrefEnv({
'set': [
['dom.fetch.enabled', true]
]
}, resolve);
});
for (let test of tests) {
let tabOptions = {
gBrowser: gBrowser,
url: test.tabURL,
};
yield BrowserTestUtils.withNewTab(
tabOptions,
browser => testObtainingManifest(browser, test)
);
}
function makeTestURL({ body }) {
const url = new URL(defaultURL);
url.searchParams.set('body', encodeURIComponent(body));
return url.href;
}
function* testObtainingManifest(aBrowser, aTest) {
aBrowser.contentWindowAsCPOW.document.head.innerHTML = aTest.testData;
try {
const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
aTest.run(manifest);
} catch (e) {
aTest.run(e);
}
add_task(function*() {
const promises = tests
.map(test => ({
gBrowser,
testRunner: testObtainingManifest(test),
url: makeTestURL(test)
}))
.reduce((collector, tabOpts) => {
const promise = BrowserTestUtils.withNewTab(tabOpts, tabOpts.testRunner);
collector.push(promise);
return collector;
}, []);
const results = yield Promise.all(promises);
function testObtainingManifest(aTest) {
return function*(aBrowser) {
try {
const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
aTest.run(manifest);
} catch (e) {
aTest.run(e);
}
};
}
});
@ -192,36 +120,36 @@ add_task(function*() {
* in each tab. They should all return pass.
*/
add_task(function*() {
const defaultPath = '/tests/dom/manifest/test/manifestLoader.html';
const defaultPath = '/browser/dom/manifest/test/manifestLoader.html';
const tabURLs = [
`http://test:80${defaultPath}`,
`http://mochi.test:8888${defaultPath}`,
`http://test1.mochi.test:8888${defaultPath}`,
`http://sub1.test1.mochi.test:8888${defaultPath}`,
`http://sub2.xn--lt-uia.mochi.test:8888${defaultPath}`,
`http://test2.mochi.test:8888${defaultPath}`,
`http://example.org:80${defaultPath}`,
`http://test1.example.org:80${defaultPath}`,
`http://test2.example.org:80${defaultPath}`,
`http://sub1.test1.example.org:80${defaultPath}`,
`http://sub1.test2.example.org:80${defaultPath}`,
`http://sub2.test1.example.org:80${defaultPath}`,
`http://sub2.test2.example.org:80${defaultPath}`,
`http://example.org:8000${defaultPath}`,
`http://test1.example.org:8000${defaultPath}`,
`http://test2.example.org:8000${defaultPath}`,
`http://sub1.test1.example.org:8000${defaultPath}`,
`http://sub1.test2.example.org:8000${defaultPath}`,
`http://sub2.test1.example.org:8000${defaultPath}`,
`http://sub2.test2.example.org:8000${defaultPath}`,
`http://example.com:80${defaultPath}`,
`http://www.example.com:80${defaultPath}`,
`http://test1.example.com:80${defaultPath}`,
`http://test2.example.com:80${defaultPath}`,
`http://example.org:80${defaultPath}`,
`http://example.org:8000${defaultPath}`,
`http://mochi.test:8888${defaultPath}`,
`http://sub1.test1.example.com:80${defaultPath}`,
`http://sub1.test1.example.org:80${defaultPath}`,
`http://sub1.test1.example.org:8000${defaultPath}`,
`http://sub1.test1.mochi.test:8888${defaultPath}`,
`http://sub1.test2.example.com:80${defaultPath}`,
`http://sub1.test2.example.org:80${defaultPath}`,
`http://sub1.test2.example.org:8000${defaultPath}`,
`http://sub2.test1.example.com:80${defaultPath}`,
`http://sub2.test1.example.org:80${defaultPath}`,
`http://sub2.test1.example.org:8000${defaultPath}`,
`http://sub2.test2.example.com:80${defaultPath}`,
`http://sub2.test2.example.org:80${defaultPath}`,
`http://sub2.test2.example.org:8000${defaultPath}`,
`http://sub2.xn--lt-uia.mochi.test:8888${defaultPath}`,
`http://test1.example.com:80${defaultPath}`,
`http://test1.example.org:80${defaultPath}`,
`http://test1.example.org:8000${defaultPath}`,
`http://test1.mochi.test:8888${defaultPath}`,
`http://test2.example.com:80${defaultPath}`,
`http://test2.example.org:80${defaultPath}`,
`http://test2.example.org:8000${defaultPath}`,
`http://test2.mochi.test:8888${defaultPath}`,
`http://test:80${defaultPath}`,
`http://www.example.com:80${defaultPath}`,
];
// Open tabs an collect corresponding browsers
let browsers = [
@ -234,11 +162,10 @@ add_task(function*() {
// Flood random browsers with requests. Once promises settle, check that
// responses all pass.
const results = yield Promise.all((
for (browser of randBrowsers(browsers, 100)) ManifestObtainer.browserObtainManifest(browser)
for (browser of randBrowsers(browsers, 50)) ManifestObtainer.browserObtainManifest(browser)
));
const expected = 'Expect every manifest to have name equal to `pass`.';
const pass = results.every(manifest => manifest.name === 'pass');
Assert.ok(pass, expected);
ok(pass, 'Expect every manifest to have name equal to `pass`.');
//cleanup
browsers
.map(browser => gBrowser.getTabForBrowser(browser))

View File

@ -20,6 +20,7 @@
*/
//global handleRequest
'use strict';
Components.utils.importGlobalProperties(["URLSearchParams"]);
const HTTPStatus = new Map([
[100, 'Continue'],
[101, 'Switching Protocol'],
@ -66,7 +67,7 @@ const HTTPStatus = new Map([
]);
function handleRequest(request, response) {
const queryMap = createQueryMap(request);
const queryMap = new URLSearchParams(request.queryString);
if (queryMap.has('statusCode')) {
let statusCode = parseInt(queryMap.get('statusCode'));
let statusText = HTTPStatus.get(statusCode);
@ -76,18 +77,9 @@ function handleRequest(request, response) {
if (queryMap.has('body')) {
let body = queryMap.get('body') || '';
queryMap.delete('body');
response.write(body);
response.write(decodeURIComponent(body));
}
for (let [key, value] of queryMap) {
for (let [key, value] of queryMap.entries()) {
response.setHeader(key, value);
}
function createQueryMap(request) {
const queryMap = new Map();
request.queryString.split('&')
//split on first "="
.map((component) => component.split(/=(.+)?/))
.forEach(pair => queryMap.set(pair[0], decodeURIComponent(pair[1])));
return queryMap;
}
}

View File

@ -10,7 +10,6 @@
#include "nsIUUIDGenerator.h"
#include "nsPIDOMWindow.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/MediaStreamTrackEvent.h"
#include "mozilla/dom/LocalMediaStreamBinding.h"
#include "mozilla/dom/AudioNode.h"
#include "AudioChannelAgent.h"
@ -99,10 +98,10 @@ DOMMediaStream::TrackPort::GetSourceTrackId() const
}
already_AddRefed<Pledge<bool>>
DOMMediaStream::TrackPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
DOMMediaStream::TrackPort::BlockSourceTrackId(TrackID aTrackId)
{
if (mInputPort) {
return mInputPort->BlockSourceTrackId(aTrackId, aBlockingMode);
return mInputPort->BlockSourceTrackId(aTrackId);
}
RefPtr<Pledge<bool>> rejected = new Pledge<bool>();
rejected->Reject(NS_ERROR_FAILURE);
@ -143,7 +142,7 @@ public:
}
MediaStreamTrack* track =
mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID);
if (!track) {
// Track had not been created on main thread before, create it now.
NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(),
@ -166,8 +165,7 @@ public:
}
}
void DoNotifyTrackEnded(MediaStream* aInputStream, TrackID aInputTrackID,
TrackID aTrackID)
void DoNotifyTrackEnded(MediaStream* aInputStream, TrackID aInputTrackID)
{
MOZ_ASSERT(NS_IsMainThread());
@ -176,7 +174,7 @@ public:
}
RefPtr<MediaStreamTrack> track =
mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID);
NS_ASSERTION(track, "Owned MediaStreamTracks must be known by the DOMMediaStream");
if (track) {
LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.",
@ -199,9 +197,9 @@ public:
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
} else if (aTrackEvents & TRACK_EVENT_ENDED) {
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<MediaStream*, TrackID, TrackID>(
NewRunnableMethod<MediaStream*, TrackID>(
this, &OwnedStreamListener::DoNotifyTrackEnded,
aInputStream, aInputTrackID, aID);
aInputStream, aInputTrackID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}
@ -600,11 +598,7 @@ DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack)
// to block it in the port. Doing this for a locked track is still OK as it
// will first block the track, then destroy the port. Both cause the track to
// end.
// If the track has already ended, it's input port might be gone, so in those
// cases blocking the underlying track should be avoided.
if (!aTrack.Ended()) {
BlockPlaybackTrack(toRemove);
}
BlockPlaybackTrack(toRemove);
DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
MOZ_ASSERT(removed);
@ -1007,9 +1001,6 @@ DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
new TrackPort(mPlaybackPort, track, TrackPort::InputPortOwnership::EXTERNAL));
NotifyTrackAdded(track);
DispatchTrackEvent(NS_LITERAL_STRING("addtrack"), track);
return track;
}
@ -1044,17 +1035,6 @@ DOMMediaStream::CloneDOMTrack(MediaStreamTrack& aTrack,
NotifyTrackAdded(newTrack);
newTrack->SetEnabled(aTrack.Enabled());
newTrack->SetReadyState(aTrack.ReadyState());
if (aTrack.Ended()) {
// For extra suspenders, make sure that we don't forward data by mistake
// to the clone when the original has already ended.
// We only block END_EXISTING to allow any pending clones to end.
RefPtr<Pledge<bool, nsresult>> blockingPledge =
inputPort->BlockSourceTrackId(inputTrackID,
BlockingMode::END_EXISTING);
Unused << blockingPledge;
}
return newTrack.forget();
}
@ -1073,16 +1053,14 @@ FindTrackPortAmongTracks(const MediaStreamTrack& aTrack,
MediaStreamTrack*
DOMMediaStream::FindOwnedDOMTrack(MediaStream* aInputStream,
TrackID aInputTrackID,
TrackID aTrackID) const
TrackID aInputTrackID) const
{
MOZ_RELEASE_ASSERT(mOwnedStream);
for (const RefPtr<TrackPort>& info : mOwnedTracks) {
if (info->GetInputPort() &&
info->GetInputPort()->GetSource() == aInputStream &&
info->GetTrack()->mInputTrackID == aInputTrackID &&
(aTrackID == TRACK_ANY || info->GetTrack()->mTrackID == aTrackID)) {
info->GetTrack()->mInputTrackID == aInputTrackID) {
// This track is owned externally but in our playback stream.
return info->GetTrack();
}
@ -1249,22 +1227,6 @@ DOMMediaStream::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
// track has been confirmed removed by the graph. See BlockPlaybackTrack().
}
nsresult
DOMMediaStream::DispatchTrackEvent(const nsAString& aName,
const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(aName == NS_LITERAL_STRING("addtrack"),
"Only 'addtrack' is supported at this time");
MediaStreamTrackEventInit init;
init.mTrack = aTrack;
RefPtr<MediaStreamTrackEvent> event =
MediaStreamTrackEvent::Constructor(this, aName, init);
return DispatchTrustedEvent(event);
}
void
DOMMediaStream::CreateAndAddPlaybackStreamListener(MediaStream* aStream)
{
@ -1278,9 +1240,7 @@ DOMMediaStream::BlockPlaybackTrack(TrackPort* aTrack)
{
MOZ_ASSERT(aTrack);
++mTracksPendingRemoval;
RefPtr<Pledge<bool>> p =
aTrack->BlockSourceTrackId(aTrack->GetTrack()->mTrackID,
BlockingMode::CREATION);
RefPtr<Pledge<bool>> p = aTrack->BlockSourceTrackId(aTrack->GetTrack()->mTrackID);
RefPtr<DOMMediaStream> self = this;
p->Then([self] (const bool& aIgnore) { self->NotifyPlaybackTrackBlocked(); },
[] (const nsresult& aIgnore) { NS_ERROR("Could not remove track from MSG"); }

View File

@ -34,8 +34,6 @@ class MediaStreamDirectListener;
class MediaStreamGraph;
class ProcessedMediaStream;
enum class BlockingMode;
namespace dom {
class AudioNode;
class HTMLCanvasElement;
@ -306,8 +304,7 @@ public:
* destroyed. Returns a pledge that gets resolved when the MediaStreamGraph
* has applied the block in the playback stream.
*/
already_AddRefed<media::Pledge<bool, nsresult>>
BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode);
already_AddRefed<media::Pledge<bool, nsresult>> BlockSourceTrackId(TrackID aTrackId);
private:
RefPtr<MediaInputPort> mInputPort;
@ -363,8 +360,6 @@ public:
/** Identical to CloneInternal(TrackForwardingOption::EXPLICIT) */
already_AddRefed<DOMMediaStream> Clone();
IMPL_EVENT_HANDLER(addtrack)
// NON-WebIDL
/**
@ -395,17 +390,10 @@ public:
/**
* Returns the corresponding MediaStreamTrack if it's in our mOwnedStream.
* aInputTrackID should match the track's TrackID in its input stream,
* and aTrackID the TrackID in mOwnedStream.
*
* When aTrackID is not supplied or set to TRACK_ANY, we return the first
* MediaStreamTrack that matches the given input track. Note that there may
* be multiple MediaStreamTracks matching the same input track, but that they
* in that case all share the same MediaStreamTrackSource.
* aInputTrackID should match the track's TrackID in its input stream.
*/
MediaStreamTrack* FindOwnedDOMTrack(MediaStream* aInputStream,
TrackID aInputTrackID,
TrackID aTrackID = TRACK_ANY) const;
TrackID aInputTrackID) const;
/**
* Returns the TrackPort connecting aTrack's input stream to mOwnedStream,
@ -525,12 +513,7 @@ public:
* Called for each track in our owned stream to indicate to JS that we
* are carrying that track.
*
* Creates a MediaStreamTrack, adds it to mTracks, raises "addtrack" and
* returns it.
*
* Note that "addtrack" is raised synchronously and only has an effect if
* this MediaStream is already exposed to script. For spec compliance this is
* to be called from an async task.
* Creates a MediaStreamTrack, adds it to mTracks and returns it.
*/
MediaStreamTrack* CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
MediaStreamTrackSource* aSource);
@ -611,10 +594,6 @@ protected:
// Dispatches NotifyTrackRemoved() to all registered track listeners.
void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack);
// Dispatches "addtrack" or "removetrack".
nsresult DispatchTrackEvent(const nsAString& aName,
const RefPtr<MediaStreamTrack>& aTrack);
class OwnedStreamListener;
friend class OwnedStreamListener;

View File

@ -25,6 +25,7 @@
#include "mozilla/dom/AudioTrack.h"
#include "mozilla/dom/AudioTrackList.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "nsPrintfCString.h"
@ -814,7 +815,7 @@ MediaDecoder::Play()
}
nsresult
MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType, dom::Promise* aPromise /*=nullptr*/)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(!mShuttingDown, NS_ERROR_FAILURE);
@ -831,7 +832,7 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
mLogicallySeeking = true;
SeekTarget target = SeekTarget(timeUsecs, aSeekType);
CallSeek(target);
CallSeek(target, aPromise);
if (mPlayState == PLAY_STATE_ENDED) {
PinForSeek();
@ -841,10 +842,48 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
}
void
MediaDecoder::CallSeek(const SeekTarget& aTarget)
MediaDecoder::AsyncResolveSeekDOMPromiseIfExists()
{
MOZ_ASSERT(NS_IsMainThread());
if (mSeekDOMPromise) {
RefPtr<dom::Promise> promise = mSeekDOMPromise;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
promise->MaybeResolve(JS::UndefinedHandleValue);
});
AbstractThread::MainThread()->Dispatch(r.forget());
mSeekDOMPromise = nullptr;
}
}
void
MediaDecoder::AsyncRejectSeekDOMPromiseIfExists()
{
MOZ_ASSERT(NS_IsMainThread());
if (mSeekDOMPromise) {
RefPtr<dom::Promise> promise = mSeekDOMPromise;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
promise->MaybeRejectWithUndefined();
});
AbstractThread::MainThread()->Dispatch(r.forget());
mSeekDOMPromise = nullptr;
}
}
void
MediaDecoder::DiscardOngoingSeekIfExists()
{
MOZ_ASSERT(NS_IsMainThread());
mSeekRequest.DisconnectIfExists();
AsyncRejectSeekDOMPromiseIfExists();
}
void
MediaDecoder::CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise)
{
MOZ_ASSERT(NS_IsMainThread());
DiscardOngoingSeekIfExists();
mSeekDOMPromise = aPromise;
mSeekRequest.Begin(
mDecoderStateMachine->InvokeSeek(aTarget)
->Then(AbstractThread::MainThread(), __func__, this,
@ -1256,12 +1295,22 @@ MediaDecoder::OnSeekResolved(SeekResolveValue aVal)
if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
mOwner->SeekCompleted();
AsyncResolveSeekDOMPromiseIfExists();
if (fireEnded) {
mOwner->PlaybackEnded();
}
}
}
void
MediaDecoder::OnSeekRejected()
{
MOZ_ASSERT(NS_IsMainThread());
mSeekRequest.Complete();
mLogicallySeeking = false;
AsyncRejectSeekDOMPromiseIfExists();
}
void
MediaDecoder::SeekingStarted(MediaDecoderEventVisibility aEventVisibility)
{

View File

@ -43,6 +43,10 @@ class nsIPrincipal;
namespace mozilla {
namespace dom {
class Promise;
}
class VideoFrameContainer;
class MediaDecoderStateMachine;
@ -166,7 +170,8 @@ public:
// Seek to the time position in (seconds) from the start of the video.
// If aDoFastSeek is true, we'll seek to the sync point/keyframe preceeding
// the seek target.
virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType);
virtual nsresult Seek(double aTime, SeekTarget::Type aSeekType,
dom::Promise* aPromise = nullptr);
// Initialize state machine and schedule it.
nsresult InitializeStateMachine();
@ -391,12 +396,7 @@ private:
// Call on the main thread only.
void PlaybackEnded();
void OnSeekRejected()
{
MOZ_ASSERT(NS_IsMainThread());
mSeekRequest.Complete();
mLogicallySeeking = false;
}
void OnSeekRejected();
void OnSeekResolved(SeekResolveValue aVal);
void SeekingChanged()
@ -620,12 +620,24 @@ private:
#endif
protected:
virtual void CallSeek(const SeekTarget& aTarget);
// The promise resolving/rejection is queued as a "micro-task" which will be
// handled immediately after the current JS task and before any pending JS
// tasks.
// At the time we are going to resolve/reject a promise, the "seeking" event
// task should already be queued but might yet be processed, so we queue one
// more task to file the promise resolving/rejection micro-tasks
// asynchronously to make sure that the micro-tasks are processed after the
// "seeking" event task.
void AsyncResolveSeekDOMPromiseIfExists();
void AsyncRejectSeekDOMPromiseIfExists();
void DiscardOngoingSeekIfExists();
virtual void CallSeek(const SeekTarget& aTarget, dom::Promise* aPromise);
// Returns true if heuristic dormant is supported.
bool IsHeuristicDormantSupported() const;
MozPromiseRequestHolder<SeekPromise> mSeekRequest;
RefPtr<dom::Promise> mSeekDOMPromise;
// True when seeking or otherwise moving the play position around in
// such a manner that progress event data is inaccurate. This is set

View File

@ -2380,31 +2380,6 @@ MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
listener->NotifyBlockingChanged(GraphImpl(),
mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
for (StreamTracks::TrackIter it(mTracks); !it.IsEnded(); it.Next()) {
MediaStream* inputStream = nullptr;
TrackID inputTrackID = TRACK_INVALID;
if (ProcessedMediaStream* ps = AsProcessedStream()) {
// The only ProcessedMediaStream where we should have listeners is
// TrackUnionStream - it's what's used as owned stream in DOMMediaStream,
// the only main-thread exposed stream type.
// TrackUnionStream guarantees that each of its tracks has an input track.
// Other types do not implement GetInputStreamFor() and will return null.
inputStream = ps->GetInputStreamFor(it->GetID());
MOZ_ASSERT(inputStream);
inputTrackID = ps->GetInputTrackIDFor(it->GetID());
MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
}
uint32_t flags = MediaStreamListener::TRACK_EVENT_CREATED;
if (it->IsEnded()) {
flags |= MediaStreamListener::TRACK_EVENT_ENDED;
}
nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
flags, *segment,
inputStream, inputTrackID);
}
if (mNotifiedFinished) {
listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
}
@ -3161,26 +3136,24 @@ MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
}
void
MediaInputPort::BlockSourceTrackIdImpl(TrackID aTrackId, BlockingMode aBlockingMode)
MediaInputPort::BlockSourceTrackIdImpl(TrackID aTrackId)
{
mBlockedTracks.AppendElement(Pair<TrackID, BlockingMode>(aTrackId, aBlockingMode));
mBlockedTracks.AppendElement(aTrackId);
}
already_AddRefed<Pledge<bool>>
MediaInputPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
MediaInputPort::BlockSourceTrackId(TrackID aTrackId)
{
class Message : public ControlMessage {
public:
explicit Message(MediaInputPort* aPort,
TrackID aTrackId,
BlockingMode aBlockingMode,
already_AddRefed<nsIRunnable> aRunnable)
: ControlMessage(aPort->GetDestination()),
mPort(aPort), mTrackId(aTrackId), mBlockingMode(aBlockingMode),
mRunnable(aRunnable) {}
mPort(aPort), mTrackId(aTrackId), mRunnable(aRunnable) {}
void Run() override
{
mPort->BlockSourceTrackIdImpl(mTrackId, mBlockingMode);
mPort->BlockSourceTrackIdImpl(mTrackId);
if (mRunnable) {
mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(mRunnable.forget());
}
@ -3191,7 +3164,6 @@ MediaInputPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
}
RefPtr<MediaInputPort> mPort;
TrackID mTrackId;
BlockingMode mBlockingMode;
nsCOMPtr<nsIRunnable> mRunnable;
};
@ -3204,7 +3176,7 @@ MediaInputPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
pledge->Resolve(true);
return NS_OK;
});
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId, aBlockingMode, runnable.forget()));
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId, runnable.forget()));
return pledge.forget();
}
@ -3247,7 +3219,7 @@ ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID,
aInputNumber, aOutputNumber);
if (aBlockedTracks) {
for (TrackID trackID : *aBlockedTracks) {
port->BlockSourceTrackIdImpl(trackID, BlockingMode::CREATION);
port->BlockSourceTrackIdImpl(trackID);
}
}
port->SetGraphImpl(GraphImpl());

View File

@ -1177,23 +1177,6 @@ protected:
bool mNeedsMixing;
};
/**
* The blocking mode decides how a track should be blocked in a MediaInputPort.
*/
enum class BlockingMode
{
/**
* BlockingMode CREATION blocks the source track from being created
* in the destination. It'll end if it already exists.
*/
CREATION,
/**
* BlockingMode END_EXISTING allows a track to be created in the destination
* but will end it before any data has been passed through.
*/
END_EXISTING,
};
/**
* Represents a connection between a ProcessedMediaStream and one of its
* input streams.
@ -1275,39 +1258,16 @@ public:
* Returns a pledge that resolves on the main thread after the track block has
* been applied by the MSG.
*/
already_AddRefed<media::Pledge<bool, nsresult>> BlockSourceTrackId(TrackID aTrackId,
BlockingMode aBlockingMode);
already_AddRefed<media::Pledge<bool, nsresult>> BlockSourceTrackId(TrackID aTrackId);
private:
void BlockSourceTrackIdImpl(TrackID aTrackId, BlockingMode aBlockingMode);
void BlockSourceTrackIdImpl(TrackID aTrackId);
public:
// Returns true if aTrackId has not been blocked for any reason and this port
// has not been locked to another track.
// Returns true if aTrackId has not been blocked and this port has not
// been locked to another track.
bool PassTrackThrough(TrackID aTrackId) {
bool blocked = false;
for (auto pair : mBlockedTracks) {
if (pair.first() == aTrackId &&
(pair.second() == BlockingMode::CREATION ||
pair.second() == BlockingMode::END_EXISTING)) {
blocked = true;
break;
}
}
return !blocked && (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId);
}
// Returns true if aTrackId has not been blocked for track creation and this
// port has not been locked to another track.
bool AllowCreationOf(TrackID aTrackId) {
bool blocked = false;
for (auto pair : mBlockedTracks) {
if (pair.first() == aTrackId &&
pair.second() == BlockingMode::CREATION) {
blocked = true;
break;
}
}
return !blocked && (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId);
return !mBlockedTracks.Contains(aTrackId) &&
(mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId);
}
uint16_t InputNumber() const { return mInputNumber; }
@ -1362,9 +1322,7 @@ private:
// Web Audio.
const uint16_t mInputNumber;
const uint16_t mOutputNumber;
typedef Pair<TrackID, BlockingMode> BlockedTrack;
nsTArray<BlockedTrack> mBlockedTracks;
nsTArray<TrackID> mBlockedTracks;
// Our media stream graph
MediaStreamGraphImpl* mGraph;
@ -1440,8 +1398,6 @@ public:
{
return mInputs.Length();
}
virtual MediaStream* GetInputStreamFor(TrackID aTrackID) { return nullptr; }
virtual TrackID GetInputTrackIDFor(TrackID aTrackID) { return TRACK_NONE; }
void DestroyImpl() override;
/**
* This gets called after we've computed the blocking states for all

View File

@ -115,8 +115,7 @@ MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
: mOwningStream(aStream), mTrackID(aTrackID),
mInputTrackID(aInputTrackID), mSource(aSource),
mPrincipal(aSource->GetPrincipal()),
mReadyState(MediaStreamTrackState::Live),
mEnabled(true), mRemote(aSource->IsRemote())
mEnded(false), mEnabled(true), mRemote(aSource->IsRemote()), mStopped(false)
{
if (!gMediaStreamTrackLog) {
@ -217,8 +216,8 @@ MediaStreamTrack::Stop()
{
LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this));
if (Ended()) {
LOG(LogLevel::Warning, ("MediaStreamTrack %p Already ended", this));
if (mStopped) {
LOG(LogLevel::Warning, ("MediaStreamTrack %p Already stopped", this));
return;
}
@ -237,10 +236,10 @@ MediaStreamTrack::Stop()
MOZ_ASSERT(mOwningStream, "Every MediaStreamTrack needs an owning DOMMediaStream");
DOMMediaStream::TrackPort* port = mOwningStream->FindOwnedTrackPort(*this);
MOZ_ASSERT(port, "A MediaStreamTrack must exist in its owning DOMMediaStream");
RefPtr<Pledge<bool>> p = port->BlockSourceTrackId(mInputTrackID, BlockingMode::CREATION);
RefPtr<Pledge<bool>> p = port->BlockSourceTrackId(mInputTrackID);
Unused << p;
mReadyState = MediaStreamTrackState::Ended;
mStopped = true;
}
already_AddRefed<Promise>
@ -351,22 +350,6 @@ MediaStreamTrack::Clone()
return newStream->CloneDOMTrack(*this, mTrackID);
}
void
MediaStreamTrack::NotifyEnded()
{
MOZ_ASSERT(NS_IsMainThread());
if (Ended()) {
return;
}
LOG(LogLevel::Info, ("MediaStreamTrack %p ended", this));
mReadyState = MediaStreamTrackState::Ended;
DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
}
DOMMediaStream*
MediaStreamTrack::GetInputDOMStream()
{

View File

@ -266,29 +266,10 @@ public:
already_AddRefed<Promise>
ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
already_AddRefed<MediaStreamTrack> Clone();
MediaStreamTrackState ReadyState() { return mReadyState; }
IMPL_EVENT_HANDLER(ended)
/**
* Convenience (and legacy) method for when ready state is "ended".
*/
bool Ended() const { return mReadyState == MediaStreamTrackState::Ended; }
/**
* Forces the ready state to a particular value, for instance when we're
* cloning an already ended track.
*/
void SetReadyState(MediaStreamTrackState aState) { mReadyState = aState; }
/**
* Notified by the MediaStreamGraph, through our owning MediaStream on the
* main thread.
*
* Note that this sets the track to ended and raises the "ended" event
* synchronously.
*/
void NotifyEnded();
bool Ended() const { return mEnded; }
// Notifications from the MediaStreamGraph
void NotifyEnded() { mEnded = true; }
/**
* Get this track's principal.
@ -417,9 +398,10 @@ protected:
nsCOMPtr<nsIPrincipal> mPendingPrincipal;
RefPtr<PrincipalHandleListener> mPrincipalHandleListener;
nsString mID;
MediaStreamTrackState mReadyState;
bool mEnded;
bool mEnabled;
const bool mRemote;
bool mStopped;
};
} // namespace dom

View File

@ -103,6 +103,12 @@ StreamTracks::ForgetUpTo(StreamTime aTime)
for (uint32_t i = 0; i < mTracks.Length(); ++i) {
Track* track = mTracks[i];
if (track->IsEnded() && track->GetEnd() <= aTime) {
mTracks.RemoveElementAt(i);
mTracksDirty = true;
--i;
continue;
}
StreamTime forgetTo = std::min(track->GetEnd() - 1, aTime);
track->ForgetUpTo(forgetTo);
}

View File

@ -5,6 +5,7 @@
#include "mozilla/dom/HTMLTrackElement.h"
#include "mozilla/dom/TextTrackCue.h"
#include "mozilla/dom/TextTrackList.h"
#include "mozilla/dom/TextTrackRegion.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/ClearOnShutdown.h"
@ -34,7 +35,7 @@ TextTrackCue::SetDefaultCueSettings()
{
mPosition = 50;
mPositionAlign = PositionAlignSetting::Center;
mSize = 100;
mSize = 100.0;
mPauseOnExit = false;
mSnapToLines = true;
mLineIsAutoKeyword = true;
@ -169,6 +170,40 @@ TextTrackCue::SetRegion(TextTrackRegion* aRegion)
mReset = true;
}
double
TextTrackCue::ComputedLine()
{
// See spec https://w3c.github.io/webvtt/#cue-computed-line
if (!mLineIsAutoKeyword && !mSnapToLines &&
(mLine < 0.0 || mLine > 100.0)) {
return 100.0;
} else if (!mLineIsAutoKeyword) {
return mLine;
} else if (mLineIsAutoKeyword && !mSnapToLines) {
return 100.0;
} else if (!mTrack ||
!mTrack->GetTextTrackList() ||
!mTrack->GetTextTrackList()->GetMediaElement()) {
return -1.0;
}
RefPtr<TextTrackList> trackList = mTrack->GetTextTrackList();
bool dummy;
uint32_t showingTracksNum = 0;
for (uint32_t idx = 0; idx < trackList->Length(); idx++) {
RefPtr<TextTrack> track = trackList->IndexedGetter(idx, dummy);
if (track->Mode() == TextTrackMode::Showing) {
showingTracksNum++;
}
if (mTrack == track) {
break;
}
}
return (-1.0) * showingTracksNum;
}
PositionAlignSetting
TextTrackCue::ComputedPositionAlign()
{

View File

@ -151,21 +151,21 @@ public:
mSnapToLines = aSnapToLines;
}
void GetLine(OwningLongOrAutoKeyword& aLine) const
void GetLine(OwningDoubleOrAutoKeyword& aLine) const
{
if (mLineIsAutoKeyword) {
aLine.SetAsAutoKeyword() = AutoKeyword::Auto;
return;
}
aLine.SetAsLong() = mLineLong;
aLine.SetAsDouble() = mLine;
}
void SetLine(const LongOrAutoKeyword& aLine)
void SetLine(const DoubleOrAutoKeyword& aLine)
{
if (aLine.IsLong() &&
(mLineIsAutoKeyword || (aLine.GetAsLong() != mLineLong))) {
if (aLine.IsDouble() &&
(mLineIsAutoKeyword || (aLine.GetAsDouble() != mLine))) {
mLineIsAutoKeyword = false;
mLineLong = aLine.GetAsLong();
mLine = aLine.GetAsDouble();
mReset = true;
return;
}
@ -225,18 +225,18 @@ public:
mPositionAlign = aPositionAlign;
}
int32_t Size() const
double Size() const
{
return mSize;
}
void SetSize(int32_t aSize, ErrorResult& aRv)
void SetSize(double aSize, ErrorResult& aRv)
{
if (mSize == aSize) {
return;
}
if (aSize < 0 || aSize > 100) {
if (aSize < 0.0 || aSize > 100.0) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
@ -299,6 +299,7 @@ public:
return mReset;
}
double ComputedLine();
PositionAlignSetting ComputedPositionAlign();
// Helper functions for implementation.
@ -361,13 +362,13 @@ private:
nsString mId;
int32_t mPosition;
PositionAlignSetting mPositionAlign;
int32_t mSize;
double mSize;
bool mPauseOnExit;
bool mSnapToLines;
RefPtr<TextTrackRegion> mRegion;
DirectionSetting mVertical;
bool mLineIsAutoKeyword;
long mLineLong;
double mLine;
AlignSetting mAlign;
LineAlignSetting mLineAlign;

View File

@ -107,7 +107,7 @@ TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) :
break;
}
}
if (!found && mInputs[i]->AllowCreationOf(tracks->GetID())) {
if (!found && mInputs[i]->PassTrackThrough(tracks->GetID())) {
bool trackFinished = false;
trackAdded = true;
uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom);
@ -374,30 +374,6 @@ TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) {
MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled);
}
MediaStream*
TrackUnionStream::GetInputStreamFor(TrackID aTrackID)
{
for (TrackMapEntry& entry : mTrackMap) {
if (entry.mOutputTrackID == aTrackID && entry.mInputPort) {
return entry.mInputPort->GetSource();
}
}
return nullptr;
}
TrackID
TrackUnionStream::GetInputTrackIDFor(TrackID aTrackID)
{
for (TrackMapEntry& entry : mTrackMap) {
if (entry.mOutputTrackID == aTrackID) {
return entry.mInputTrackID;
}
}
return TRACK_NONE;
}
void
TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
TrackID aTrackID)

View File

@ -24,9 +24,6 @@ public:
void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) override;
MediaStream* GetInputStreamFor(TrackID aTrackID) override;
TrackID GetInputTrackIDFor(TrackID aTrackID) override;
protected:
// Only non-ended tracks are allowed to persist in this map.
struct TrackMapEntry {

View File

@ -415,7 +415,7 @@ static nsresult
ConvertSourceSurfaceToNV12(const RefPtr<SourceSurface>& aSurface, uint8_t* aDestination)
{
if (!aSurface) {
CODEC_ERROR("Getting surface %s from image failed");
CODEC_ERROR("Getting surface from image failed");
return NS_ERROR_FAILURE;
}

View File

@ -17,4 +17,4 @@ mozversion==1.4
wptserve==1.3.0
marionette-client==2.2.0
marionette-driver==1.3.0
firefox-puppeteer==4.0.0
firefox-puppeteer~=49.0.0

View File

@ -29,15 +29,28 @@ function startTest(test, token) {
v.src = test.name;
v.name = test.name;
function callSeekToNextFrame() {
v.seekToNextFrame().then(
() => {
ok(v.seenSeeking, "Should have already received seeking event.")
v.seenSeeking = false;
if (!v.seenEnded)
callSeekToNextFrame();
},
() => {
ok(false, "seekToNextFrame() failed.");
}
);
}
var onLoadedmetadata = function(test, v) { return function() {
v.seekToNextFrame();
callSeekToNextFrame();
}}(test, v);
var finish = function() {
v.finished = true;
v.removeEventListener("loadedmetadata", onLoadedmetadata, false);
v.removeEventListener("seeking", onSeeking, false);
v.removeEventListener("seeked", onSeeked, false);
removeNodeAndSource(v);
manager.finished(v.token);
}
@ -52,15 +65,8 @@ function startTest(test, token) {
v.seenSeeking = true;
}}(test, v);
var onSeeked = function(test, v) { return function() {
ok(v.seenSeeking, "Should have already received seeking event.")
v.seenSeeking = false;
v.seekToNextFrame();
}}(test, v);
v.addEventListener("loadedmetadata", onLoadedmetadata, false);
v.addEventListener("seeking", onSeeking, false);
v.addEventListener("seeked", onSeeked, false);
v.addEventListener("ended", onEnded, false);
document.body.appendChild(v);

View File

@ -89,10 +89,10 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
});
var exceptionHappened;
function checkPercentageValue(prop) {
function checkPercentageValue(prop, initialVal) {
ok(prop in cue, prop + " should be a property on VTTCue.");
cue[prop] = 20;
is(cue[prop], 20, "Cue's " + prop + " should now be 20.");
cue[prop] = initialVal;
is(cue[prop], initialVal, "Cue's " + prop + " should now be " + initialVal);
[ 101, -1 ].forEach(function(val) {
exceptionHappened = false;
try {
@ -105,8 +105,11 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
});
}
checkPercentageValue("size");
checkPercentageValue("position");
checkPercentageValue("size", 100.0);
cue.size = 50.5;
is(cue.size, 50.5, "Cue's size should be 50.5.")
checkPercentageValue("position", 50.0);
ok(cue.snapToLines, "Cue's snapToLines should be set by set.");
cue.snapToLines = false;
@ -150,8 +153,8 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
// Check cue.line
is(cue.line, "auto", "Cue's line value should initially be auto.");
cue.line = 12410
is(cue.line, 12410, "Cue's line value should now be 12410.");
cue.line = 0.5;
is(cue.line, 0.5, "Cue's line value should now be 0.5.");
cue.line = "auto";
is(cue.line, "auto", "Cue's line value should now be auto.");

View File

@ -441,8 +441,8 @@ function checkMediaStreamTrackCloneAgainstOriginal(clone, original) {
/*** Utility methods */
/** The dreadful setTimeout, use sparingly */
function wait(time, message) {
return new Promise(r => setTimeout(() => r(message), time));
function wait(time) {
return new Promise(r => setTimeout(r, time));
}
/** The even more dreadful setInterval, use even more sparingly */
@ -482,8 +482,8 @@ var addFinallyToPromise = promise => {
/** Use event listener to call passed-in function on fire until it returns true */
var listenUntil = (target, eventName, onFire) => {
return new Promise(resolve => target.addEventListener(eventName,
function callback(event) {
var result = onFire(event);
function callback() {
var result = onFire();
if (result) {
target.removeEventListener(eventName, callback, false);
resolve(result);
@ -590,27 +590,6 @@ function createOneShotEventWrapper(wrapper, obj, event) {
};
}
/**
* Returns a promise that resolves when `target` has raised an event with the
* given name. Cancel the returned promise by passing in a `cancelPromise` and
* resolve it.
*
* @param {object} target
* The target on which the event should occur.
* @param {string} name
* The name of the event that should occur.
* @param {promise} cancelPromise
* A promise that on resolving rejects the returned promise,
* so we can avoid logging results after a test has finished.
*/
function haveEvent(target, name, cancelPromise) {
var listener;
var p = Promise.race([
(cancelPromise || new Promise()).then(e => Promise.reject(e)),
new Promise(resolve => target.addEventListener(name, listener = resolve))
]);
return p.then(event => (target.removeEventListener(name, listener), event));
};
/**
* This class executes a series of functions in a continuous sequence.

View File

@ -55,17 +55,8 @@ MediaStreamPlayback.prototype = {
});
});
var noTrackEnded = Promise.all(this.mediaStream.getTracks().map(t => {
let onNextLoop = wait(0);
let p = Promise.race([
onNextLoop,
haveEvent(t, "ended", onNextLoop)
.then(() => Promise.reject("Unexpected ended event for track " + t.id),
() => Promise.resolve())
]);
t.stop();
return p;
}));
// TODO (bug 910249) Also check that all the tracks are local.
this.mediaStream.getTracks().forEach(t => t.stop());
// XXX (bug 1208316) When we implement MediaStream.active, do not stop
// the stream. We just do it now so the media element will raise 'ended'.
@ -74,8 +65,7 @@ MediaStreamPlayback.prototype = {
}
this.mediaStream.stop();
return timeout(waitForEnded(), ENDED_TIMEOUT_LENGTH, "ended event never fired")
.then(() => ok(true, "ended event successfully fired"))
.then(() => noTrackEnded);
.then(() => ok(true, "ended event successfully fired"));
},
/**

View File

@ -44,7 +44,6 @@ skip-if = buildapp == 'mulet'
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g emulator seems to be too slow (Bug 1016498 and 1008080), android(Bug 1189784, timeouts on 4.3 emulator)
[test_getUserMedia_addTrackRemoveTrack.html]
skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
[test_getUserMedia_addtrack_removetrack_events.html]
[test_getUserMedia_basicAudio.html]
skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
[test_getUserMedia_basicVideo.html]
@ -80,11 +79,9 @@ skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # copied from basi
[test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html]
[test_getUserMedia_stopVideoStream.html]
[test_getUserMedia_stopVideoStreamWithFollowupVideo.html]
[test_getUserMedia_trackEnded.html]
[test_getUserMedia_peerIdentity.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 1021776, too --ing slow on b2g)
[test_peerConnection_addIceCandidate.html]
[test_peerConnection_addtrack_removetrack_events.html]
[test_peerConnection_basicAudio.html]
skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
[test_peerConnection_basicAudioNATSrflx.html]

View File

@ -134,29 +134,13 @@ PeerConnectionTest.prototype.closePC = function() {
return Promise.resolve();
}
var promise = Promise.all([
new Promise(resolve => {
pc.onsignalingstatechange = e => {
is(e.target.signalingState, "closed", "signalingState is closed");
resolve();
};
}),
Promise.all(pc._pc.getReceivers()
.filter(receiver => receiver.track.readyState == "live")
.map(receiver => {
info("Waiting for track " + receiver.track.id + " (" +
receiver.track.kind + ") to end.");
return haveEvent(receiver.track, "ended", wait(50000))
.then(event => {
is(event.target, receiver.track, "Event target should be the correct track");
info("ended fired for track " + receiver.track.id);
}, e => e ? Promise.reject(e)
: ok(false, "ended never fired for track " +
receiver.track.id));
}))
]);
pc.close();
return promise;
return new Promise(resolve => {
pc.onsignalingstatechange = e => {
is(e.target.signalingState, "closed", "signalingState is closed");
resolve();
};
pc.close();
});
};
return timerGuard(Promise.all([
@ -466,22 +450,19 @@ PeerConnectionTest.prototype.run = function() {
/* We have to modify the chain here to allow tests which modify the default
* test chain instantiating a PeerConnectionTest() */
this.updateChainSteps();
var finished = () => {
if (window.SimpleTest) {
networkTestFinished();
} else {
finish();
}
};
return this.chain.execute()
.then(() => this.close())
.then(() => {
if (window.SimpleTest) {
networkTestFinished();
} else {
finish();
}
})
.catch(e =>
ok(false, 'Error in test execution: ' + e +
((typeof e.stack === 'string') ?
(' ' + e.stack.split('\n').join(' ... ')) : '')))
.then(() => finished())
.catch(e =>
ok(false, "Error in finished()"));
ok(false, 'Error in test execution: ' + e +
((typeof e.stack === 'string') ?
(' ' + e.stack.split('\n').join(' ... ')) : '')));
};
/**

View File

@ -1,109 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
"use strict";
createHTML({
title: "MediaStream's 'addtrack' and 'removetrack' events shouldn't fire on manual operations",
bug: "1208328"
});
var spinEventLoop = () => new Promise(r => setTimeout(r, 0));
var stream;
var clone;
var newStream;
var addTrack = track => {
info("Adding track " + track.id);
stream.addTrack(track);
};
var removeTrack = track => {
info("Removing track " + track.id);
stream.removeTrack(track);
};
var stopTrack = track => {
if (track.readyState == "live") {
info("Stopping track " + track.id);
}
track.stop();
};
runTest(() => getUserMedia({audio: true, video: true})
.then(s => {
stream = s;
clone = s.clone();
stream.addEventListener("addtrack", function onAddtrack(event) {
ok(false, "addtrack fired unexpectedly for track " + event.track.id);
});
stream.addEventListener("removetrack", function onRemovetrack(event) {
ok(false, "removetrack fired unexpectedly for track " + event.track.id);
});
return getUserMedia({audio: true, video: true});
})
.then(s => {
newStream = s;
info("Stopping an original track");
stopTrack(stream.getTracks()[0]);
return spinEventLoop();
})
.then(() => {
info("Removing original tracks");
stream.getTracks().forEach(t => stream.removeTrack(t));
return spinEventLoop();
})
.then(() => {
info("Adding other gUM tracks");
newStream.getTracks().forEach(t => addTrack(t))
return spinEventLoop();
})
.then(() => {
info("Adding cloned tracks");
let clone = stream.clone();
clone.getTracks().forEach(t => addTrack(t));
return spinEventLoop();
})
.then(() => {
info("Removing a clone");
removeTrack(clone.getTracks()[0]);
return spinEventLoop();
})
.then(() => {
info("Stopping clones");
clone.getTracks().forEach(t => stopTrack(t));
return spinEventLoop();
})
.then(() => {
info("Stopping originals");
stream.getTracks().forEach(t => stopTrack(t));
return spinEventLoop();
})
.then(() => {
info("Removing remaining tracks");
stream.getTracks().forEach(t => removeTrack(t));
return spinEventLoop();
})
.then(() => {
// Test MediaStreamTrackEvent required args here.
mustThrowWith("MediaStreamTrackEvent without required args",
"TypeError", () => new MediaStreamTrackEvent("addtrack", {}));
}));
</script>
</pre>
</body>
</html>

View File

@ -52,10 +52,7 @@
var test = createMediaElement('video', 'testClonePlayback');
var playback = new MediaStreamPlayback(test, cloneStream);
return playback.playMediaWithMediaStreamTracksStop(false)
.then(() => info("Testing that clones of ended tracks are ended"))
.then(() => cloneStream.clone().getTracks().forEach(t =>
is(t.readyState, "ended", "Track " + t.id + " should be ended")));
return playback.playMediaWithMediaStreamTracksStop(false);
})
.then(() => getUserMedia({audio: true, video: true})).then(stream => {
info("Test adding many track clones to the original stream");

View File

@ -1,60 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
</head>
<body>
<pre id="test">
<iframe id="iframe" srcdoc="
<script type='application/javascript'>
document.gUM = (constraints, success, failure) =>
navigator.mediaDevices.getUserMedia(constraints).then(success, failure);
</script>">
</iframe>
<script type="application/javascript">
"use strict";
createHTML({
title: "getUserMedia MediaStreamTrack 'ended' event on navigating",
bug: "1208373",
});
runTest(() => {
let iframe = document.getElementById("iframe");
let stream;
// We're passing callbacks into a method in the iframe here, because
// a Promise created in the iframe is unusable after the iframe has
// navigated away (see bug 1269400 for details).
return new Promise((resolve, reject) =>
iframe.contentDocument.gUM({audio: true, video: true}, resolve, reject))
.then(s => {
// We're cloning a stream containing identical tracks (an original
// and its clone) to test that ended works both for originals
// clones when they're both owned by the same MediaStream.
// (Bug 1274221)
stream = new MediaStream([].concat(s.getTracks(), s.getTracks())
.map(t => t.clone())).clone();
var allTracksEnded = Promise.all(stream.getTracks().map(t => {
info("Set up ended handler for track " + t.id);
return haveEvent(t, "ended", wait(5000))
.then(event => {
info("ended handler invoked for track " + t.id);
is(event.target, t, "Target should be correct");
}, e => e ? Promise.reject(e)
: ok(false, "ended event never raised for track " + t.id));
}));
stream.getTracks().forEach(t =>
is(t.readyState, "live",
"Non-ended track should have readyState 'live'"));
iframe.srcdoc = "";
info("iframe has been reset. Waiting for tracks to end.");
return allTracksEnded;
})
.then(() => stream.getTracks().forEach(t =>
is(t.readyState, "ended",
"Ended track should have readyState 'ended'")));
});
</script>
</pre>
</body>
</html>

View File

@ -1,70 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
"use strict";
createHTML({
title: "MediaStream's 'addtrack' and 'removetrack' events with gUM",
bug: "1208328"
});
runNetworkTest(function (options) {
let test = new PeerConnectionTest(options);
let eventsPromise;
addRenegotiation(test.chain,
[
function PC_LOCAL_SWAP_VIDEO_TRACKS(test) {
return getUserMedia({video: true}).then(stream => {
let localStream = test.pcLocal._pc.getLocalStreams()[0];
let remoteStream = test.pcRemote._pc.getRemoteStreams()[0];
let newTrack = stream.getTracks()[0];
let videoSenderIndex =
test.pcLocal._pc.getSenders().findIndex(s => s.track.kind == "video");
isnot(videoSenderIndex, -1, "Should have video sender");
test.pcLocal.removeSender(videoSenderIndex);
test.pcLocal.attachLocalTrack(stream.getTracks()[0], localStream);
let onNextLoop = wait(0);
eventsPromise = haveEvent(remoteStream, "addtrack", wait(50000, "No addtrack event"))
.then(trackEvent => {
ok(trackEvent instanceof MediaStreamTrackEvent,
"Expected event to be instance of MediaStreamTrackEvent");
is(trackEvent.type, "addtrack",
"Expected addtrack event type");
is(trackEvent.track.id, newTrack.id, "Expected track in event");
is(trackEvent.track.readyState, "live",
"added track should be live");
})
.then(() => haveEvent(remoteStream, "addtrack", onNextLoop)
.then(() => Promise.reject("Unexpected addtrack event for remote stream " + remoteStream.id),
() => Promise.resolve())
);
remoteStream.addEventListener("removetrack",
function onRemovetrack(trackEvent) {
ok(false, "UA shouldn't raise 'removetrack' when receiving peer connection");
})
});
},
],
[
function PC_REMOTE_CHECK_EVENTS(test) {
return eventsPromise;
},
]
);
test.setMediaConstraints([{audio: true, video: true}], []);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -73,8 +73,8 @@ function startTest(media, token) {
(e.code ? " (code=" + e.code + ")" : "") +
" in test for " + media.name +
(e.stack ? ":\n" + e.stack : "")))
.then(() => test && test.close())
.then(() => {
if (test) { test.close(); }
removeNodeAndSource(video);
manager.finished(token);
})

View File

@ -679,26 +679,6 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
return "ltr";
}
function computeLinePos(cue) {
if (typeof cue.line === "number" &&
(cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
return cue.line;
}
if (!cue.track || !cue.track.textTrackList ||
!cue.track.textTrackList.mediaElement) {
return -1;
}
var track = cue.track,
trackList = track.textTrackList,
count = 0;
for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
if (trackList[i].mode === "showing") {
count++;
}
}
return ++count * -1;
}
function StyleBox() {
}
@ -1020,7 +1000,7 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
var boxPosition = new BoxPosition(styleBox),
cue = styleBox.cue,
linePos = computeLinePos(cue),
linePos = cue.computedLine,
axis = [];
// If we have a line number to align the cue to.

View File

@ -15,7 +15,7 @@
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/plugins/TaskFactory.h"
#include "mozilla/ipc/TaskFactory.h"
#include "mozilla/TimeStamp.h"
#include "npapi.h"
#include "npfunctions.h"
@ -329,7 +329,7 @@ protected:
NPNetscapeFuncs* mNPNIface;
NPPluginFuncs* mNPPIface;
nsNPAPIPlugin* mPlugin;
TaskFactory<PluginModuleParent> mTaskFactory;
ipc::TaskFactory<PluginModuleParent> mTaskFactory;
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
@ -547,7 +547,7 @@ private:
PluginProcessParent* mSubprocess;
uint32_t mPluginId;
TaskFactory<PluginModuleChromeParent> mChromeTaskFactory;
ipc::TaskFactory<PluginModuleChromeParent> mChromeTaskFactory;
enum HangAnnotationFlags
{

View File

@ -17,7 +17,7 @@
#include "chrome/common/child_process_host.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/plugins/TaskFactory.h"
#include "mozilla/ipc/TaskFactory.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsIRunnable.h"
@ -81,7 +81,7 @@ private:
void RunLaunchCompleteTask();
std::string mPluginFilePath;
TaskFactory<PluginProcessParent> mTaskFactory;
ipc::TaskFactory<PluginProcessParent> mTaskFactory;
UniquePtr<LaunchCompleteTask> mLaunchCompleteTask;
MessageLoop* mMainMsgLoop;
bool mRunCompleteTaskImmediately;

View File

@ -43,7 +43,6 @@ EXPORTS.mozilla.plugins += [
'PluginWidgetParent.h',
'StreamNotifyChild.h',
'StreamNotifyParent.h',
'TaskFactory.h',
]
if CONFIG['OS_ARCH'] == 'WINNT':

View File

@ -940,6 +940,14 @@ Promise::MaybeRejectWithNull()
MaybeSomething(JS::NullHandleValue, &Promise::MaybeReject);
}
void
Promise::MaybeRejectWithUndefined()
{
NS_ASSERT_OWNINGTHREAD(Promise);
MaybeSomething(JS::UndefinedHandleValue, &Promise::MaybeReject);
}
#ifdef SPIDERMONKEY_PROMISE
void

View File

@ -153,6 +153,8 @@ public:
void MaybeRejectWithNull();
void MaybeRejectWithUndefined();
// DO NOT USE MaybeRejectBrokenly with in new code. Promises should be
// rejected with Error instances.
// Note: MaybeRejectBrokenly is a template so we can use it with DOMError

View File

@ -212,7 +212,5 @@ partial interface HTMLMediaElement {
*/
partial interface HTMLMediaElement {
[Throws, Pref="media.seekToNextFrame.enabled"]
void seekToNextFrame(); // This API should be an asynchronous one which
// returns a Promise<void>. Bug 1276272 follows this
// issue.
Promise<void> seekToNextFrame();
};

View File

@ -43,7 +43,7 @@ interface MediaStream : EventTarget {
// readonly attribute boolean active;
// attribute EventHandler onactive;
// attribute EventHandler oninactive;
attribute EventHandler onaddtrack;
// attribute EventHandler onaddtrack;
// attribute EventHandler onremovetrack;
readonly attribute double currentTime;
};

View File

@ -63,11 +63,6 @@ dictionary MediaTrackConstraints : MediaTrackConstraintSet {
sequence<MediaTrackConstraintSet> advanced;
};
enum MediaStreamTrackState {
"live",
"ended"
};
[Exposed=Window]
interface MediaStreamTrack : EventTarget {
readonly attribute DOMString kind;
@ -79,8 +74,8 @@ interface MediaStreamTrack : EventTarget {
// attribute EventHandler onunmute;
// readonly attribute boolean _readonly;
// readonly attribute boolean remote;
readonly attribute MediaStreamTrackState readyState;
attribute EventHandler onended;
// readonly attribute MediaStreamTrackState readyState;
// attribute EventHandler onended;
MediaStreamTrack clone ();
void stop ();
// MediaTrackCapabilities getCapabilities ();

View File

@ -8,12 +8,15 @@
*/
dictionary MediaStreamTrackEventInit : EventInit {
required MediaStreamTrack track;
MediaStreamTrack? track = null;
RTCRtpReceiver? receiver = null;
MediaStream? stream = null;
};
[Exposed=Window,
Constructor (DOMString type, MediaStreamTrackEventInit eventInitDict)]
[Pref="media.peerconnection.enabled",
Constructor(DOMString type, optional MediaStreamTrackEventInit eventInitDict)]
interface MediaStreamTrackEvent : Event {
[SameObject]
readonly attribute MediaStreamTrack track;
readonly attribute RTCRtpReceiver? receiver;
readonly attribute MediaStreamTrack? track;
readonly attribute MediaStream? stream;
};

View File

@ -43,7 +43,7 @@ interface VTTCue : TextTrackCue {
attribute VTTRegion? region;
attribute DirectionSetting vertical;
attribute boolean snapToLines;
attribute (long or AutoKeyword) line;
attribute (double or AutoKeyword) line;
[SetterThrows]
attribute LineAlignSetting lineAlign;
[SetterThrows]
@ -51,7 +51,7 @@ interface VTTCue : TextTrackCue {
[SetterThrows]
attribute PositionAlignSetting positionAlign;
[SetterThrows]
attribute long size;
attribute double size;
attribute AlignSetting align;
attribute DOMString text;
DocumentFragment getCueAsHTML();
@ -64,5 +64,7 @@ partial interface VTTCue {
[ChromeOnly]
readonly attribute boolean hasBeenReset;
[ChromeOnly]
readonly attribute double computedLine;
[ChromeOnly]
readonly attribute PositionAlignSetting computedPositionAlign;
};

View File

@ -270,8 +270,7 @@ nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode* aDOMNode,
nsAutoString bgStringValue;
nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
while (true) {
nsCOMPtr<Element> domElement(do_QueryInterface(domNode));

View File

@ -22,6 +22,7 @@ namespace gfx {
_(D3D9_COMPOSITING, Feature, "Direct3D9 Compositing") \
_(DIRECT2D, Feature, "Direct2D") \
_(D3D11_HW_ANGLE, Feature, "Direct3D11 hardware ANGLE") \
_(GPU_PROCESS, Feature, "GPU Process") \
/* Add new entries above this comment */
enum class Feature : uint32_t {

52
gfx/ipc/GPUChild.cpp Normal file
View File

@ -0,0 +1,52 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "GPUChild.h"
#include "GPUProcessHost.h"
namespace mozilla {
namespace gfx {
GPUChild::GPUChild(GPUProcessHost* aHost)
: mHost(aHost)
{
MOZ_COUNT_CTOR(GPUChild);
}
GPUChild::~GPUChild()
{
MOZ_COUNT_DTOR(GPUChild);
}
void
GPUChild::ActorDestroy(ActorDestroyReason aWhy)
{
mHost->OnChannelClosed();
}
class DeferredDeleteGPUChild : public Runnable
{
public:
explicit DeferredDeleteGPUChild(UniquePtr<GPUChild>&& aChild)
: mChild(Move(aChild))
{
}
NS_IMETHODIMP Run() override {
return NS_OK;
}
private:
UniquePtr<GPUChild> mChild;
};
/* static */ void
GPUChild::Destroy(UniquePtr<GPUChild>&& aChild)
{
NS_DispatchToMainThread(new DeferredDeleteGPUChild(Move(aChild)));
}
} // namespace gfx
} // namespace mozilla

34
gfx/ipc/GPUChild.h Normal file
View File

@ -0,0 +1,34 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 _include_mozilla_gfx_ipc_GPUChild_h_
#define _include_mozilla_gfx_ipc_GPUChild_h_
#include "mozilla/gfx/PGPUChild.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace gfx {
class GPUProcessHost;
class GPUChild final : public PGPUChild
{
public:
explicit GPUChild(GPUProcessHost* aHost);
~GPUChild();
static void Destroy(UniquePtr<GPUChild>&& aChild);
void ActorDestroy(ActorDestroyReason aWhy) override;
private:
GPUProcessHost* mHost;
};
} // namespace gfx
} // namespace mozilla
#endif // _include_mozilla_gfx_ipc_GPUChild_h_

62
gfx/ipc/GPUParent.cpp Normal file
View File

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "GPUParent.h"
#include "gfxConfig.h"
#include "GPUProcessHost.h"
#include "mozilla/Assertions.h"
#include "mozilla/ipc/ProcessChild.h"
namespace mozilla {
namespace gfx {
using namespace ipc;
GPUParent::GPUParent()
{
}
GPUParent::~GPUParent()
{
}
bool
GPUParent::Init(base::ProcessId aParentPid,
MessageLoop* aIOLoop,
IPC::Channel* aChannel)
{
if (NS_WARN_IF(!Open(aChannel, aParentPid, aIOLoop))) {
return false;
}
return true;
}
bool
GPUParent::RecvNothing()
{
return true;
}
void
GPUParent::ActorDestroy(ActorDestroyReason aWhy)
{
if (AbnormalShutdown == aWhy) {
NS_WARNING("Shutting down GPU process early due to a crash!");
ProcessChild::QuickExit();
}
#ifndef NS_FREE_PERMANENT_DATA
// No point in going through XPCOM shutdown because we don't keep persistent
// state. Currently we quick-exit in RecvBeginShutdown so this should be
// unreachable.
ProcessChild::QuickExit();
#else
XRE_ShutdownChildProcess();
#endif
}
} // namespace gfx
} // namespace mozilla

32
gfx/ipc/GPUParent.h Normal file
View File

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 _include_gfx_ipc_GPUParent_h__
#define _include_gfx_ipc_GPUParent_h__
#include "mozilla/gfx/PGPUParent.h"
namespace mozilla {
namespace gfx {
class GPUParent final : public PGPUParent
{
public:
GPUParent();
~GPUParent();
bool Init(base::ProcessId aParentPid,
MessageLoop* aIOLoop,
IPC::Channel* aChannel);
bool RecvNothing() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
};
} // namespace gfx
} // namespace mozilla
#endif // _include_gfx_ipc_GPUParent_h__

209
gfx/ipc/GPUProcessHost.cpp Normal file
View File

@ -0,0 +1,209 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sts=8 sw=2 ts=2 tw=99 et :
* 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 "GPUProcessHost.h"
#include "chrome/common/process_watcher.h"
#include "gfxPrefs.h"
#include "mozilla/gfx/Logging.h"
#include "nsITimer.h"
namespace mozilla {
namespace gfx {
GPUProcessHost::GPUProcessHost(Listener* aListener)
: GeckoChildProcessHost(GeckoProcessType_GPU),
mListener(aListener),
mTaskFactory(this),
mLaunchPhase(LaunchPhase::Unlaunched),
mShutdownRequested(false)
{
MOZ_COUNT_CTOR(GPUProcessHost);
}
GPUProcessHost::~GPUProcessHost()
{
MOZ_COUNT_DTOR(GPUProcessHost);
}
bool
GPUProcessHost::Launch()
{
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
MOZ_ASSERT(!mGPUChild);
mLaunchPhase = LaunchPhase::Waiting;
if (!GeckoChildProcessHost::AsyncLaunch()) {
mLaunchPhase = LaunchPhase::Complete;
return false;
}
return true;
}
bool
GPUProcessHost::WaitForLaunch()
{
if (mLaunchPhase == LaunchPhase::Complete) {
return !!mGPUChild;
}
int32_t timeoutMs = gfxPrefs::GPUProcessDevTimeoutMs();
// Our caller expects the connection to be finished after we return, so we
// immediately set up the IPDL actor and fire callbacks. The IO thread will
// still dispatch a notification to the main thread - we'll just ignore it.
bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
InitAfterConnect(result);
return result;
}
void
GPUProcessHost::OnChannelConnected(int32_t peer_pid)
{
MOZ_ASSERT(!NS_IsMainThread());
GeckoChildProcessHost::OnChannelConnected(peer_pid);
// Post a task to the main thread. Take the lock because mTaskFactory is not
// thread-safe.
RefPtr<Runnable> runnable;
{
MonitorAutoLock lock(mMonitor);
runnable = mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelConnectedTask);
}
NS_DispatchToMainThread(runnable);
}
void
GPUProcessHost::OnChannelError()
{
MOZ_ASSERT(!NS_IsMainThread());
GeckoChildProcessHost::OnChannelError();
// Post a task to the main thread. Take the lock because mTaskFactory is not
// thread-safe.
RefPtr<Runnable> runnable;
{
MonitorAutoLock lock(mMonitor);
runnable = mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelErrorTask);
}
NS_DispatchToMainThread(runnable);
}
void
GPUProcessHost::OnChannelConnectedTask()
{
if (mLaunchPhase == LaunchPhase::Waiting) {
InitAfterConnect(true);
}
}
void
GPUProcessHost::OnChannelErrorTask()
{
if (mLaunchPhase == LaunchPhase::Waiting) {
InitAfterConnect(false);
}
}
void
GPUProcessHost::InitAfterConnect(bool aSucceeded)
{
MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
MOZ_ASSERT(!mGPUChild);
mLaunchPhase = LaunchPhase::Complete;
if (aSucceeded) {
mGPUChild = MakeUnique<GPUChild>(this);
DebugOnly<bool> rv =
mGPUChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
MOZ_ASSERT(rv);
}
if (mListener) {
mListener->OnProcessLaunchComplete(this);
}
}
void
GPUProcessHost::Shutdown()
{
MOZ_ASSERT(!mShutdownRequested);
mListener = nullptr;
if (mGPUChild) {
// OnChannelClosed uses this to check if the shutdown was expected or
// unexpected.
mShutdownRequested = true;
#ifdef NS_FREE_PERMANENT_DATA
mGPUChild->Close();
#else
// No need to communicate shutdown, the GPU process doesn't need to
// communicate anything back.
KillHard("NormalShutdown");
#endif
// Wait for ActorDestroy.
return;
}
DestroyProcess();
}
void
GPUProcessHost::OnChannelClosed()
{
if (!mShutdownRequested) {
// This is an unclean shutdown. Notify our listener that we're going away.
if (mListener) {
mListener->OnProcessUnexpectedShutdown(this);
}
}
// Release the actor.
GPUChild::Destroy(Move(mGPUChild));
MOZ_ASSERT(!mGPUChild);
// If the owner of GPUProcessHost already requested shutdown, we can now
// schedule destruction. Otherwise we must wait for someone to call
// Shutdown. Note that GPUProcessManager calls Shutdown within
// OnProcessUnexpectedShutdown.
if (mShutdownRequested) {
DestroyProcess();
}
}
void
GPUProcessHost::KillHard(const char* aReason)
{
ProcessHandle handle = GetChildProcessHandle();
if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
NS_WARNING("failed to kill subprocess!");
}
SetAlreadyDead();
XRE_GetIOMessageLoop()->PostTask(
NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, handle, /*force=*/true));
}
void
GPUProcessHost::DestroyProcess()
{
// Cancel all tasks. We don't want anything triggering after our caller
// expects this to go away.
{
MonitorAutoLock lock(mMonitor);
mTaskFactory.RevokeAll();
}
DissociateActor();
}
} // namespace gfx
} // namespace mozilla

125
gfx/ipc/GPUProcessHost.h Normal file
View File

@ -0,0 +1,125 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sts=8 sw=2 ts=2 tw=99 et :
* 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 _include_mozilla_gfx_ipc_GPUProcessHost_h_
#define _include_mozilla_gfx_ipc_GPUProcessHost_h_
#include "mozilla/Function.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/ipc/TaskFactory.h"
class nsITimer;
namespace mozilla {
namespace gfx {
class GPUChild;
// GPUProcessHost is the "parent process" container for a subprocess handle and
// IPC connection. It owns the parent process IPDL actor, which in this case,
// is a GPUChild.
//
// GPUProcessHosts are allocated and managed by GPUProcessManager. For all
// intents and purposes it is a singleton, though more than one may be allocated
// at a time due to its shutdown being asynchronous.
class GPUProcessHost final : public ipc::GeckoChildProcessHost
{
friend class GPUChild;
public:
class Listener {
public:
virtual void OnProcessLaunchComplete(GPUProcessHost* aHost)
{}
// The GPUProcessHost has unexpectedly shutdown or had its connection
// severed. This is not called if an error occurs after calling
// Shutdown().
virtual void OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
{}
};
public:
explicit GPUProcessHost(Listener* listener);
~GPUProcessHost();
// Launch the subprocess asynchronously. On failure, false is returned.
// Otherwise, true is returned, and the OnLaunchComplete listener callback
// will be invoked either when a connection has been established, or if a
// connection could not be established due to an asynchronous error.
bool Launch();
// If the process is being launched, block until it has launched and
// connected. If a launch task is pending, it will fire immediately.
//
// Returns true if the process is successfully connected; false otherwise.
bool WaitForLaunch();
// Inform the process that it should clean up its resources and shut down.
// This initiates an asynchronous shutdown sequence. After this method returns,
// it is safe for the caller to forget its pointer to the GPUProcessHost.
//
// After this returns, the attached Listener is no longer used.
void Shutdown();
// Return the actor for the top-level actor of the process. If the process
// has not connected yet, this returns null.
GPUChild* GetActor() const {
return mGPUChild.get();
}
bool IsConnected() const {
return !!mGPUChild;
}
// Called on the IO thread.
void OnChannelConnected(int32_t peer_pid) override;
void OnChannelError() override;
void SetListener(Listener* aListener);
private:
// Called on the main thread.
void OnChannelConnectedTask();
void OnChannelErrorTask();
// Called on the main thread after a connection has been established.
void InitAfterConnect(bool aSucceeded);
// Called on the main thread when the mGPUChild actor is shutting down.
void OnChannelClosed();
// Kill the remote process, triggering IPC shutdown.
void KillHard(const char* aReason);
void DestroyProcess();
private:
DISALLOW_COPY_AND_ASSIGN(GPUProcessHost);
Listener* mListener;
ipc::TaskFactory<GPUProcessHost> mTaskFactory;
enum class LaunchPhase {
Unlaunched,
Waiting,
Complete
};
LaunchPhase mLaunchPhase;
UniquePtr<GPUChild> mGPUChild;
Listener* listener_;
bool mShutdownRequested;
};
} // namespace gfx
} // namespace mozilla
#endif // _include_mozilla_gfx_ipc_GPUProcessHost_h_

View File

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 "GPUProcessImpl.h"
#include "mozilla/ipc/IOThreadChild.h"
namespace mozilla {
namespace gfx {
using namespace ipc;
GPUProcessImpl::GPUProcessImpl(ProcessId aParentPid)
: ProcessChild(aParentPid)
{
}
GPUProcessImpl::~GPUProcessImpl()
{
}
bool
GPUProcessImpl::Init()
{
return mGPU.Init(ParentPid(),
IOThreadChild::message_loop(),
IOThreadChild::channel());
}
void
GPUProcessImpl::CleanUp()
{
}
} // namespace gfx
} // namespace mozilla

34
gfx/ipc/GPUProcessImpl.h Normal file
View File

@ -0,0 +1,34 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=99: */
/* 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 _include_gfx_ipc_GPUProcessImpl_h__
#define _include_gfx_ipc_GPUProcessImpl_h__
#include "mozilla/ipc/ProcessChild.h"
#include "GPUParent.h"
namespace mozilla {
namespace gfx {
// This class owns the subprocess instance of a PGPU - which in this case,
// is a GPUParent. It is instantiated as a singleton in XRE_InitChildProcess.
class GPUProcessImpl final : public ipc::ProcessChild
{
public:
explicit GPUProcessImpl(ProcessId aParentPid);
~GPUProcessImpl();
bool Init() override;
void CleanUp() override;
private:
DISALLOW_COPY_AND_ASSIGN(GPUProcessImpl);
GPUParent mGPU;
};
} // namespace gfx
} // namespace mozilla
#endif // _include_gfx_ipc_GPUProcessImpl_h__

View File

@ -4,8 +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/. */
#include "GPUProcessManager.h"
#include "GPUProcessHost.h"
#include "mozilla/layers/CompositorSession.h"
#include "mozilla/StaticPtr.h"
#include "nsContentUtils.h"
namespace mozilla {
namespace gfx {
@ -34,11 +36,117 @@ GPUProcessManager::Shutdown()
}
GPUProcessManager::GPUProcessManager()
: mProcess(nullptr),
mGPUChild(nullptr)
{
mObserver = new Observer(this);
nsContentUtils::RegisterShutdownObserver(mObserver);
}
GPUProcessManager::~GPUProcessManager()
{
// The GPU process should have already been shut down.
MOZ_ASSERT(!mProcess && !mGPUChild);
// We should have already removed observers.
MOZ_ASSERT(!mObserver);
}
NS_IMPL_ISUPPORTS(GPUProcessManager::Observer, nsIObserver);
GPUProcessManager::Observer::Observer(GPUProcessManager* aManager)
: mManager(aManager)
{
}
NS_IMETHODIMP
GPUProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
mManager->OnXPCOMShutdown();
}
return NS_OK;
}
void
GPUProcessManager::OnXPCOMShutdown()
{
if (mObserver) {
nsContentUtils::UnregisterShutdownObserver(mObserver);
mObserver = nullptr;
}
DestroyProcess();
}
void
GPUProcessManager::EnableGPUProcess()
{
if (mProcess) {
return;
}
// The subprocess is launched asynchronously, so we wait for a callback to
// acquire the IPDL actor.
mProcess = new GPUProcessHost(this);
if (!mProcess->Launch()) {
DisableGPUProcess("Failed to launch GPU process");
}
}
void
GPUProcessManager::DisableGPUProcess(const char* aMessage)
{
gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage);
gfxCriticalNote << aMessage;
DestroyProcess();
}
void
GPUProcessManager::EnsureGPUReady()
{
if (mProcess && mProcess->IsConnected()) {
if (!mProcess->WaitForLaunch()) {
// If this fails, we should have fired OnProcessLaunchComplete and
// removed the process.
MOZ_ASSERT(!mProcess && !mGPUChild);
return;
}
}
}
void
GPUProcessManager::OnProcessLaunchComplete(GPUProcessHost* aHost)
{
MOZ_ASSERT(mProcess && mProcess == aHost);
if (!mProcess->IsConnected()) {
DisableGPUProcess("Failed to launch GPU process");
return;
}
mGPUChild = mProcess->GetActor();
}
void
GPUProcessManager::OnProcessUnexpectedShutdown(GPUProcessHost* aHost)
{
MOZ_ASSERT(mProcess && mProcess == aHost);
DestroyProcess();
}
void
GPUProcessManager::DestroyProcess()
{
if (!mProcess) {
return;
}
mProcess->Shutdown();
mProcess = nullptr;
mGPUChild = nullptr;
}
already_AddRefed<CompositorSession>

View File

@ -10,7 +10,9 @@
#include "base/process.h"
#include "Units.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/gfx/GPUProcessHost.h"
#include "mozilla/ipc/Transport.h"
#include "nsIObserverService.h"
namespace mozilla {
namespace layers {
@ -32,10 +34,12 @@ class GeckoChildProcessHost;
} // namespace ipc
namespace gfx {
class GPUChild;
// The GPUProcessManager is a singleton responsible for creating GPU-bound
// objects that may live in another process. Currently, it provides access
// to the compositor via CompositorBridgeParent.
class GPUProcessManager final
class GPUProcessManager final : public GPUProcessHost::Listener
{
typedef layers::APZCTreeManager APZCTreeManager;
typedef layers::CompositorUpdateObserver CompositorUpdateObserver;
@ -47,6 +51,14 @@ public:
~GPUProcessManager();
// If not using a GPU process, launch a new GPU process asynchronously.
void EnableGPUProcess();
// Ensure that GPU-bound methods can be used. If no GPU process is being
// used, or one is launched and ready, this function returns immediately.
// Otherwise it blocks until the GPU process has finished launching.
void EnsureGPUReady();
already_AddRefed<layers::CompositorSession> CreateTopLevelCompositor(
widget::CompositorWidgetProxy* aProxy,
layers::ClientLayerManager* aLayerManager,
@ -92,10 +104,41 @@ public:
const dom::TabId& aTabId,
dom::TabParent* aBrowserParent);
void OnProcessLaunchComplete(GPUProcessHost* aHost) override;
void OnProcessUnexpectedShutdown(GPUProcessHost* aHost) override;
private:
// Called from our xpcom-shutdown observer.
void OnXPCOMShutdown();
private:
GPUProcessManager();
// Permanently disable the GPU process and record a message why.
void DisableGPUProcess(const char* aMessage);
// Shutdown the GPU process.
void DestroyProcess();
DISALLOW_COPY_AND_ASSIGN(GPUProcessManager);
class Observer final : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit Observer(GPUProcessManager* aManager);
protected:
~Observer() {}
GPUProcessManager* mManager;
};
friend class Observer;
private:
RefPtr<Observer> mObserver;
GPUProcessHost* mProcess;
GPUChild* mGPUChild;
};
} // namespace gfx

17
gfx/ipc/PGPU.ipdl Normal file
View File

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
namespace mozilla {
namespace gfx {
sync protocol PGPU
{
parent:
// Sent by the UI process to initiate shutdown.
async Nothing();
};
} // namespace gfx
} // namespace mozilla

View File

@ -10,6 +10,10 @@ EXPORTS.mozilla += [
]
EXPORTS.mozilla.gfx += [
'GPUChild.h',
'GPUParent.h',
'GPUProcessHost.h',
'GPUProcessImpl.h',
'GPUProcessManager.h',
'SharedDIB.h',
]
@ -31,12 +35,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
UNIFIED_SOURCES += [
'CompositorSession.cpp',
'D3DMessageUtils.cpp',
'GPUChild.cpp',
'GPUParent.cpp',
'GPUProcessHost.cpp',
'GPUProcessImpl.cpp',
'GPUProcessManager.cpp',
'SharedDIB.cpp',
]
IPDL_SOURCES = [
'GraphicsMessages.ipdlh',
'PGPU.ipdl',
]
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -1,90 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "PrintTargetThebes.h"
#include "gfxASurface.h"
#include "gfxPlatform.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace gfx {
/* static */ already_AddRefed<PrintTargetThebes>
PrintTargetThebes::CreateOrNull(gfxASurface* aSurface)
{
MOZ_ASSERT(aSurface);
if (!aSurface || aSurface->CairoStatus()) {
return nullptr;
}
RefPtr<PrintTargetThebes> target = new PrintTargetThebes(aSurface);
return target.forget();
}
PrintTargetThebes::PrintTargetThebes(gfxASurface* aSurface)
: PrintTarget(nullptr, aSurface->GetSize())
, mGfxSurface(aSurface)
{
}
already_AddRefed<DrawTarget>
PrintTargetThebes::MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder)
{
MOZ_ASSERT(!aRecorder,
"aRecorder should only be passed to an instance of "
"PrintTargetRecording");
RefPtr<gfx::DrawTarget> dt =
gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mGfxSurface, aSize);
if (!dt || !dt->IsValid()) {
return nullptr;
}
return dt.forget();
}
nsresult
PrintTargetThebes::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName)
{
return mGfxSurface->BeginPrinting(aTitle, aPrintToFileName);
}
nsresult
PrintTargetThebes::EndPrinting()
{
return mGfxSurface->EndPrinting();
}
nsresult
PrintTargetThebes::AbortPrinting()
{
return mGfxSurface->AbortPrinting();
}
nsresult
PrintTargetThebes::BeginPage()
{
return mGfxSurface->BeginPage();
}
nsresult
PrintTargetThebes::EndPage()
{
return mGfxSurface->EndPage();
}
void
PrintTargetThebes::Finish()
{
return mGfxSurface->Finish();
}
} // namespace gfx
} // namespace mozilla

View File

@ -1,52 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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_GFX_PRINTTARGETTHEBES_H
#define MOZILLA_GFX_PRINTTARGETTHEBES_H
#include "mozilla/gfx/PrintTarget.h"
class gfxASurface;
namespace mozilla {
namespace gfx {
/**
* XXX Remove this class.
*
* This class should go away once all the logic from the gfxASurface subclasses
* has been moved to new PrintTarget subclasses and we no longer need to
* wrap a gfxASurface.
*/
class PrintTargetThebes final : public PrintTarget {
public:
static already_AddRefed<PrintTargetThebes>
CreateOrNull(gfxASurface* aSurface);
virtual nsresult BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName) override;
virtual nsresult EndPrinting() override;
virtual nsresult AbortPrinting() override;
virtual nsresult BeginPage() override;
virtual nsresult EndPage() override;
virtual void Finish() override;
virtual already_AddRefed<DrawTarget>
MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder = nullptr) override;
private:
// Only created via CreateOrNull
explicit PrintTargetThebes(gfxASurface* aSurface);
RefPtr<gfxASurface> mGfxSurface;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETTHEBES_H */

View File

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "PrintTargetWindows.h"
#include "cairo-win32.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "nsCoord.h"
#include "nsString.h"
namespace mozilla {
namespace gfx {
PrintTargetWindows::PrintTargetWindows(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
HDC aDC)
: PrintTarget(aCairoSurface, aSize)
, mDC(aDC)
{
// TODO: At least add basic memory reporting.
// 4 * mSize.width * mSize.height + sizeof(PrintTargetWindows) ?
}
/* static */ already_AddRefed<PrintTargetWindows>
PrintTargetWindows::CreateOrNull(HDC aDC)
{
// Figure out the cairo surface size - Windows we need to use the printable
// area of the page. Note: we only scale the printing using the LOGPIXELSY,
// so we use that when calculating the surface width as well as the height.
int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY);
float width =
(::GetDeviceCaps(aDC, HORZRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
float height =
(::GetDeviceCaps(aDC, VERTRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
IntSize size(width, height);
if (!Factory::CheckSurfaceSize(size)) {
return nullptr;
}
cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC);
if (cairo_surface_status(surface)) {
return nullptr;
}
// The new object takes ownership of our surface reference.
RefPtr<PrintTargetWindows> target =
new PrintTargetWindows(surface, size, aDC);
return target.forget();
}
nsresult
PrintTargetWindows::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName)
{
const uint32_t DOC_TITLE_LENGTH = MAX_PATH - 1;
DOCINFOW docinfo;
nsString titleStr(aTitle);
if (titleStr.Length() > DOC_TITLE_LENGTH) {
titleStr.SetLength(DOC_TITLE_LENGTH - 3);
titleStr.AppendLiteral("...");
}
nsString docName(aPrintToFileName);
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = titleStr.Length() > 0 ? titleStr.get() : L"Mozilla Document";
docinfo.lpszOutput = docName.Length() > 0 ? docName.get() : nullptr;
docinfo.lpszDatatype = nullptr;
docinfo.fwType = 0;
::StartDocW(mDC, &docinfo);
return NS_OK;
}
nsresult
PrintTargetWindows::EndPrinting()
{
int result = ::EndDoc(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetWindows::AbortPrinting()
{
int result = ::AbortDoc(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetWindows::BeginPage()
{
int result = ::StartPage(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetWindows::EndPage()
{
cairo_surface_show_page(mCairoSurface);
int result = ::EndPage(mDC);
return (result <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
} // namespace gfx
} // namespace mozilla

View File

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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_GFX_PRINTTARGETWINDOWS_H
#define MOZILLA_GFX_PRINTTARGETWINDOWS_H
#include "PrintTarget.h"
/* include windows.h for the HDC definitions that we need. */
#include <windows.h>
namespace mozilla {
namespace gfx {
/**
* Windows printing target.
*/
class PrintTargetWindows final : public PrintTarget
{
public:
static already_AddRefed<PrintTargetWindows>
CreateOrNull(HDC aDC);
virtual nsresult BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName) override;
virtual nsresult EndPrinting() override;
virtual nsresult AbortPrinting() override;
virtual nsresult BeginPage() override;
virtual nsresult EndPage() override;
private:
PrintTargetWindows(cairo_surface_t* aCairoSurface,
const IntSize& aSize,
HDC aDC);
HDC mDC;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_PRINTTARGETWINDOWS_H */

View File

@ -353,36 +353,6 @@ gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
return cairo_format_stride_for_width(cformat, (int)width);
}
nsresult
gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
{
return NS_OK;
}
nsresult
gfxASurface::EndPrinting()
{
return NS_OK;
}
nsresult
gfxASurface::AbortPrinting()
{
return NS_OK;
}
nsresult
gfxASurface::BeginPage()
{
return NS_OK;
}
nsresult
gfxASurface::EndPage()
{
return NS_OK;
}
gfxContentType
gfxASurface::ContentFromFormat(gfxImageFormat format)
{

View File

@ -64,13 +64,6 @@ public:
void MarkDirty();
void MarkDirty(const gfxRect& r);
/* Printing backend functions */
virtual nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
virtual nsresult EndPrinting();
virtual nsresult AbortPrinting();
virtual nsresult BeginPage();
virtual nsresult EndPage();
void SetData(const cairo_user_data_key_t *key,
void *user_data,
thebes_destroy_func_t destroy);

View File

@ -142,6 +142,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted {
#include "mozilla/dom/ContentChild.h"
#include "gfxVR.h"
#include "VRManagerChild.h"
#include "mozilla/gfx/GPUParent.h"
namespace mozilla {
namespace layers {
@ -594,7 +595,9 @@ gfxPlatform::Init()
gfxConfig::Init();
GPUProcessManager::Initialize();
if (XRE_IsParentProcess()) {
GPUProcessManager::Initialize();
}
auto fwd = new CrashStatsLogForwarder("GraphicsCriticalError");
fwd->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
@ -848,7 +851,9 @@ gfxPlatform::Shutdown()
GLContextProviderEGL::Shutdown();
#endif
GPUProcessManager::Shutdown();
if (XRE_IsParentProcess()) {
GPUProcessManager::Shutdown();
}
// This is a bit iffy - we're assuming that we were the ones that set the
// log forwarder in the Factory, so that it's our responsibility to
@ -2115,6 +2120,23 @@ gfxPlatform::InitAcceleration()
"media.hardware-video-decoding.failed",
false);
if (XRE_IsParentProcess()) {
if (gfxPrefs::GPUProcessDevEnabled()) {
// We want to hide this from about:support, so only set a default if the
// pref is known to be true.
gfxConfig::SetDefaultFromPref(
Feature::GPU_PROCESS,
gfxPrefs::GetGPUProcessDevEnabledPrefName(),
true,
gfxPrefs::GetGPUProcessDevEnabledPrefDefault());
}
if (gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
GPUProcessManager* gpu = GPUProcessManager::Get();
gpu->EnableGPUProcess();
}
}
sLayersAccelerationPrefsInitialized = true;
}

View File

@ -71,7 +71,7 @@ class PreferenceAccessImpl;
class gfxPrefs;
class gfxPrefs final
{
private:
private:
/// See Logging.h. This lets Moz2D access preference values it owns.
PreferenceAccessImpl* mMoz2DPrefAccess;
@ -365,6 +365,8 @@ private:
DECL_GFX_PREF(Live, "layers.flash-borders", FlashLayerBorders, bool, false);
DECL_GFX_PREF(Once, "layers.force-shmem-tiles", ForceShmemTiles, bool, false);
DECL_GFX_PREF(Live, "layers.frame-counter", DrawFrameCounter, bool, false);
DECL_GFX_PREF(Once, "layers.gpu-process.dev.enabled", GPUProcessDevEnabled, bool, false);
DECL_GFX_PREF(Once, "layers.gpu-process.dev.timeout_ms", GPUProcessDevTimeoutMs, int32_t, 5000);
DECL_GFX_PREF(Once, "layers.gralloc.disable", DisableGralloc, bool, false);
DECL_GFX_PREF(Live, "layers.low-precision-buffer", UseLowPrecisionBuffer, bool, false);
DECL_GFX_PREF(Live, "layers.low-precision-opacity", LowPrecisionOpacity, float, 1.0f);

View File

@ -16,25 +16,13 @@
#include "nsString.h"
gfxWindowsSurface::gfxWindowsSurface(HDC dc, uint32_t flags) :
mOwnsDC(false), mForPrinting(false), mDC(dc), mWnd(nullptr)
mOwnsDC(false), mDC(dc), mWnd(nullptr)
{
if (flags & FLAG_TAKE_DC)
mOwnsDC = true;
#ifdef NS_PRINTING
if (flags & FLAG_FOR_PRINTING) {
Init(cairo_win32_printing_surface_create(mDC));
mForPrinting = true;
if (!mSurfaceValid) {
gfxCriticalNote << "Invalid printing surface";
}
} else
#endif
InitWithDC(flags);
}
gfxWindowsSurface::gfxWindowsSurface(IDirect3DSurface9 *surface, uint32_t flags) :
mOwnsDC(false), mForPrinting(false), mDC(0), mWnd(nullptr)
mOwnsDC(false), mDC(0), mWnd(nullptr)
{
cairo_surface_t *surf = cairo_win32_surface_create_with_d3dsurface9(surface);
Init(surf);
@ -48,7 +36,7 @@ gfxWindowsSurface::MakeInvalid(mozilla::gfx::IntSize& size)
}
gfxWindowsSurface::gfxWindowsSurface(const mozilla::gfx::IntSize& realSize, gfxImageFormat imageFormat) :
mOwnsDC(false), mForPrinting(false), mWnd(nullptr)
mOwnsDC(false), mWnd(nullptr)
{
mozilla::gfx::IntSize size(realSize);
if (!mozilla::gfx::Factory::CheckSurfaceSize(size))
@ -69,15 +57,14 @@ gfxWindowsSurface::gfxWindowsSurface(const mozilla::gfx::IntSize& realSize, gfxI
}
gfxWindowsSurface::gfxWindowsSurface(cairo_surface_t *csurf) :
mOwnsDC(false), mForPrinting(false), mWnd(nullptr)
mOwnsDC(false), mWnd(nullptr)
{
if (cairo_surface_status(csurf) == 0)
mDC = cairo_win32_surface_get_dc(csurf);
else
mDC = nullptr;
if (cairo_surface_get_type(csurf) == CAIRO_SURFACE_TYPE_WIN32_PRINTING)
mForPrinting = true;
MOZ_ASSERT(cairo_surface_get_type(csurf) != CAIRO_SURFACE_TYPE_WIN32_PRINTING);
Init(csurf, true);
}
@ -101,7 +88,7 @@ gfxWindowsSurface::CreateSimilarSurface(gfxContentType aContent,
}
cairo_surface_t *surface;
if (!mForPrinting && GetContentType() == gfxContentType::COLOR_ALPHA) {
if (GetContentType() == gfxContentType::COLOR_ALPHA) {
// When creating a similar surface to a transparent surface, ensure
// the new surface uses a DIB. cairo_surface_create_similar won't
// use a DIB for a gfxContentType::COLOR surface if this surface doesn't
@ -147,7 +134,6 @@ gfxWindowsSurface::GetDC()
return cairo_win32_surface_get_dc (CairoSurface());
}
already_AddRefed<gfxImageSurface>
gfxWindowsSurface::GetAsImageSurface()
{
@ -158,9 +144,6 @@ gfxWindowsSurface::GetAsImageSurface()
NS_ASSERTION(CairoSurface() != nullptr, "CairoSurface() shouldn't be nullptr when mSurfaceValid is TRUE!");
if (mForPrinting)
return nullptr;
cairo_surface_t *isurf = cairo_win32_surface_get_image(CairoSurface());
if (!isurf)
return nullptr;
@ -171,124 +154,9 @@ gfxWindowsSurface::GetAsImageSurface()
return result.forget();
}
nsresult
gfxWindowsSurface::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName)
{
#ifdef NS_PRINTING
#define DOC_TITLE_LENGTH (MAX_PATH-1)
if (!mForPrinting) {
return NS_OK;
}
DOCINFOW docinfo;
nsString titleStr(aTitle);
if (titleStr.Length() > DOC_TITLE_LENGTH) {
titleStr.SetLength(DOC_TITLE_LENGTH-3);
titleStr.AppendLiteral("...");
}
nsString docName(aPrintToFileName);
docinfo.cbSize = sizeof(docinfo);
docinfo.lpszDocName = titleStr.Length() > 0 ? titleStr.get() : L"Mozilla Document";
docinfo.lpszOutput = docName.Length() > 0 ? docName.get() : nullptr;
docinfo.lpszDatatype = nullptr;
docinfo.fwType = 0;
::StartDocW(mDC, &docinfo);
return NS_OK;
#else
return NS_ERROR_FAILURE;
#endif
}
nsresult
gfxWindowsSurface::EndPrinting()
{
#ifdef NS_PRINTING
if (!mForPrinting) {
return NS_OK;
}
int result = ::EndDoc(mDC);
if (result <= 0)
return NS_ERROR_FAILURE;
return NS_OK;
#else
return NS_ERROR_FAILURE;
#endif
}
nsresult
gfxWindowsSurface::AbortPrinting()
{
#ifdef NS_PRINTING
if (!mForPrinting) {
return NS_OK;
}
int result = ::AbortDoc(mDC);
if (result <= 0)
return NS_ERROR_FAILURE;
return NS_OK;
#else
return NS_ERROR_FAILURE;
#endif
}
nsresult
gfxWindowsSurface::BeginPage()
{
#ifdef NS_PRINTING
if (!mForPrinting) {
return NS_OK;
}
int result = ::StartPage(mDC);
if (result <= 0)
return NS_ERROR_FAILURE;
return NS_OK;
#else
return NS_ERROR_FAILURE;
#endif
}
nsresult
gfxWindowsSurface::EndPage()
{
#ifdef NS_PRINTING
if (!mForPrinting) {
return NS_OK;
}
cairo_surface_show_page(CairoSurface());
int result = ::EndPage(mDC);
if (result <= 0)
return NS_ERROR_FAILURE;
return NS_OK;
#else
return NS_ERROR_FAILURE;
#endif
}
const mozilla::gfx::IntSize
gfxWindowsSurface::GetSize() const
{
if (mForPrinting) {
// On Windows we need to use the printable area of the page.
// Note: we only scale the printing using the LOGPIXELSY, so we use that
// when calculating the surface width as well as the height.
int32_t heightDPI = ::GetDeviceCaps(mDC, LOGPIXELSY);
float width = (::GetDeviceCaps(mDC, HORZRES) * POINTS_PER_INCH_FLOAT)
/ heightDPI;
float height = (::GetDeviceCaps(mDC, VERTRES) * POINTS_PER_INCH_FLOAT)
/ heightDPI;
return mozilla::gfx::IntSize(width, height);
}
if (!mSurfaceValid) {
NS_WARNING ("GetImageSurface on an invalid (null) surface; who's calling this without checking for surface errors?");
return mozilla::gfx::IntSize(-1, -1);

View File

@ -22,8 +22,6 @@ class gfxContext;
class gfxWindowsSurface : public gfxASurface {
public:
enum {
FLAG_TAKE_DC = (1 << 0),
FLAG_FOR_PRINTING = (1 << 1),
FLAG_IS_TRANSPARENT = (1 << 2)
};
@ -49,19 +47,12 @@ public:
already_AddRefed<gfxImageSurface> GetAsImageSurface();
nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
nsresult EndPrinting();
nsresult AbortPrinting();
nsresult BeginPage();
nsresult EndPage();
const mozilla::gfx::IntSize GetSize() const;
private:
void MakeInvalid(mozilla::gfx::IntSize& size);
bool mOwnsDC;
bool mForPrinting;
HDC mDC;
HWND mWnd;

View File

@ -53,7 +53,6 @@ EXPORTS += [
EXPORTS.mozilla.gfx += [
'PrintTarget.h',
'PrintTargetRecording.h',
'PrintTargetThebes.h',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
@ -180,6 +179,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
]
EXPORTS.mozilla.gfx += [
'PrintTargetPDF.h',
'PrintTargetWindows.h',
]
SOURCES += [
'gfxGDIFont.cpp',
@ -188,6 +188,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'gfxWindowsPlatform.cpp',
'gfxWindowsSurface.cpp',
'PrintTargetPDF.cpp',
'PrintTargetWindows.cpp',
]
if CONFIG['MOZ_ENABLE_DWRITE_FONT']:
UNIFIED_SOURCES += [
@ -219,7 +220,6 @@ SOURCES += [
'gfxPrefs.cpp',
'PrintTarget.cpp',
'PrintTargetRecording.cpp',
'PrintTargetThebes.cpp',
]
UNIFIED_SOURCES += [

View File

@ -45,11 +45,8 @@ BlobSerial(ImageURL* aURI)
return Nothing();
}
ImageCacheKey::ImageCacheKey(nsIURI* aURI,
const PrincipalOriginAttributes& aAttrs,
nsIDocument* aDocument)
ImageCacheKey::ImageCacheKey(nsIURI* aURI, nsIDocument* aDocument)
: mURI(new ImageURL(aURI))
, mOriginAttributes(aAttrs)
, mControlledDocument(GetControlledDocumentToken(aDocument))
, mIsChrome(URISchemeIs(mURI, "chrome"))
{
@ -59,14 +56,11 @@ ImageCacheKey::ImageCacheKey(nsIURI* aURI,
mBlobSerial = BlobSerial(mURI);
}
mHash = ComputeHash(mURI, mBlobSerial, mOriginAttributes, mControlledDocument);
mHash = ComputeHash(mURI, mBlobSerial, mControlledDocument);
}
ImageCacheKey::ImageCacheKey(ImageURL* aURI,
const PrincipalOriginAttributes& aAttrs,
nsIDocument* aDocument)
ImageCacheKey::ImageCacheKey(ImageURL* aURI, nsIDocument* aDocument)
: mURI(aURI)
, mOriginAttributes(aAttrs)
, mControlledDocument(GetControlledDocumentToken(aDocument))
, mIsChrome(URISchemeIs(mURI, "chrome"))
{
@ -76,13 +70,12 @@ ImageCacheKey::ImageCacheKey(ImageURL* aURI,
mBlobSerial = BlobSerial(mURI);
}
mHash = ComputeHash(mURI, mBlobSerial, mOriginAttributes, mControlledDocument);
mHash = ComputeHash(mURI, mBlobSerial, mControlledDocument);
}
ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
: mURI(aOther.mURI)
, mBlobSerial(aOther.mBlobSerial)
, mOriginAttributes(aOther.mOriginAttributes)
, mControlledDocument(aOther.mControlledDocument)
, mHash(aOther.mHash)
, mIsChrome(aOther.mIsChrome)
@ -91,7 +84,6 @@ ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
: mURI(Move(aOther.mURI))
, mBlobSerial(Move(aOther.mBlobSerial))
, mOriginAttributes(aOther.mOriginAttributes)
, mControlledDocument(aOther.mControlledDocument)
, mHash(aOther.mHash)
, mIsChrome(aOther.mIsChrome)
@ -104,10 +96,6 @@ ImageCacheKey::operator==(const ImageCacheKey& aOther) const
if (mControlledDocument != aOther.mControlledDocument) {
return false;
}
// The origin attributes always have to match.
if (mOriginAttributes != aOther.mOriginAttributes) {
return false;
}
if (mBlobSerial || aOther.mBlobSerial) {
// If at least one of us has a blob serial, just compare the blob serial and
// the ref portion of the URIs.
@ -128,16 +116,12 @@ ImageCacheKey::Spec() const
/* static */ uint32_t
ImageCacheKey::ComputeHash(ImageURL* aURI,
const Maybe<uint64_t>& aBlobSerial,
const PrincipalOriginAttributes& aAttrs,
void* aControlledDocument)
{
// Since we frequently call Hash() several times in a row on the same
// ImageCacheKey, as an optimization we compute our hash once and store it.
nsPrintfCString ptr("%p", aControlledDocument);
nsAutoCString suffix;
aAttrs.CreateSuffix(suffix);
if (aBlobSerial) {
// For blob URIs, we hash the serial number of the underlying blob, so that
// different blob URIs which point to the same blob share a cache entry. We
@ -146,13 +130,13 @@ ImageCacheKey::ComputeHash(ImageURL* aURI,
// the same.
nsAutoCString ref;
aURI->GetRef(ref);
return HashGeneric(*aBlobSerial, HashString(ref + suffix + ptr));
return HashGeneric(*aBlobSerial, HashString(ref + ptr));
}
// For non-blob URIs, we hash the URI spec.
nsAutoCString spec;
aURI->GetSpec(spec);
return HashString(spec + suffix + ptr);
return HashString(spec + ptr);
}
/* static */ void*

View File

@ -10,7 +10,6 @@
#ifndef mozilla_image_src_ImageCacheKey_h
#define mozilla_image_src_ImageCacheKey_h
#include "mozilla/BasePrincipal.h"
#include "mozilla/Maybe.h"
class nsIDocument;
@ -32,10 +31,8 @@ class ImageURL;
class ImageCacheKey final
{
public:
ImageCacheKey(nsIURI* aURI, const PrincipalOriginAttributes& aAttrs,
nsIDocument* aDocument);
ImageCacheKey(ImageURL* aURI, const PrincipalOriginAttributes& aAttrs,
nsIDocument* aDocument);
ImageCacheKey(nsIURI* aURI, nsIDocument* aDocument);
ImageCacheKey(ImageURL* aURI, nsIDocument* aDocument);
ImageCacheKey(const ImageCacheKey& aOther);
ImageCacheKey(ImageCacheKey&& aOther);
@ -56,13 +53,11 @@ public:
private:
static uint32_t ComputeHash(ImageURL* aURI,
const Maybe<uint64_t>& aBlobSerial,
const PrincipalOriginAttributes& aAttrs,
void* aControlledDocument);
static void* GetControlledDocumentToken(nsIDocument* aDocument);
RefPtr<ImageURL> mURI;
Maybe<uint64_t> mBlobSerial;
PrincipalOriginAttributes mOriginAttributes;
void* mControlledDocument;
uint32_t mHash;
bool mIsChrome;

View File

@ -1368,20 +1368,7 @@ imgLoader::FindEntryProperties(nsIURI* uri,
*_retval = nullptr;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDoc);
nsCOMPtr<nsIPrincipal> principal;
if (doc) {
principal = doc->NodePrincipal();
} else {
nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(ssm, NS_ERROR_FAILURE);
ssm->GetSystemPrincipal(getter_AddRefs(principal));
}
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
const PrincipalOriginAttributes& attrs =
BasePrincipal::Cast(principal)->OriginAttributesRef();
ImageCacheKey key(uri, attrs, doc);
ImageCacheKey key(uri, doc);
imgCacheTable& cache = GetCache(key);
RefPtr<imgCacheEntry> entry;
@ -2138,11 +2125,7 @@ imgLoader::LoadImage(nsIURI* aURI,
// XXX For now ignore aCacheKey. We will need it in the future
// for correctly dealing with image load requests that are a result
// of post data.
NS_ENSURE_TRUE(aLoadingPrincipal, NS_ERROR_FAILURE);
const PrincipalOriginAttributes& attrs =
BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef();
ImageCacheKey key(aURI, attrs, aLoadingDocument);
ImageCacheKey key(aURI, aLoadingDocument);
imgCacheTable& cache = GetCache(key);
if (cache.Get(key, getter_AddRefs(entry)) && entry) {
@ -2346,15 +2329,7 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
PrincipalOriginAttributes attrs;
attrs.InheritFromNecko(loadInfo->GetOriginAttributes());
ImageCacheKey key(uri, attrs, doc);
ImageCacheKey key(uri, doc);
nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
channel->GetLoadFlags(&requestFlags);
@ -2456,7 +2431,7 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
// constructed above with the *current URI* and not the *original URI*. I'm
// pretty sure this is a bug, and it's preventing us from ever getting a
// cache hit in LoadImageWithChannel when redirects are involved.
ImageCacheKey originalURIKey(originalURI, attrs, doc);
ImageCacheKey originalURIKey(originalURI, doc);
// Default to doing a principal check because we don't know who
// started that load and whether their principal ended up being

View File

@ -13,9 +13,6 @@ var Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var ssm = Services.scriptSecurityManager;
var server = new HttpServer();
server.registerDirectory("/", do_get_file(''));
@ -100,9 +97,7 @@ function checkSecondLoad()
var listener = new ImageListener(checkClone, secondLoadDone);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var systemPrincipal = ssm.getSystemPrincipal();
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default",
systemPrincipal, null, outer, null, 0, null));
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null));
listener.synchronous = false;
}
@ -182,9 +177,7 @@ function startImageCallback(otherCb)
var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); });
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener2);
var systemPrincipal = ssm.getSystemPrincipal();
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default",
systemPrincipal, null, outer, null, 0, null));
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null));
listener2.synchronous = false;
// Now that we've started another load, chain to the callback.
@ -211,9 +204,7 @@ function run_test()
var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone);
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(listener);
var systemPrincipal = ssm.getSystemPrincipal();
var req = gCurrentLoader.loadImageXPCOM(uri, null, null, "default",
systemPrincipal, null, outer, null, 0, null);
var req = gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null);
requests.push(req);
// Ensure that we don't cause any mayhem when we lock an image.

View File

@ -7,8 +7,6 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://testing-common/httpd.js");
var ssm = Services.scriptSecurityManager;
var server = new HttpServer();
server.registerPathHandler('/image.png', imageHandler);
server.start(-1);
@ -86,9 +84,7 @@ function loadImage(isPrivate, callback) {
var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate);
var loader = isPrivate ? gPrivateLoader : gPublicLoader;
var systemPrincipal = ssm.getSystemPrincipal();
requests.push(loader.loadImageXPCOM(uri, null, null, "default",
systemPrincipal, loadGroup, outer, null, 0, null));
requests.push(loader.loadImageXPCOM(uri, null, null, "default", null, loadGroup, outer, null, 0, null));
listener.synchronous = false;
}
@ -108,9 +104,6 @@ function run_loadImage_tests() {
});
}
gPrivateLoader.QueryInterface(Ci.imgICache).clearCache(false);
gPublicLoader.QueryInterface(Ci.imgICache).clearCache(false);
Services.obs.addObserver(observer, "cacheservice:empty-cache", false);
let cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
.getService(Ci.nsICacheStorageService);

View File

@ -1072,6 +1072,8 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
shouldSandboxCurrentProcess = true;
}
break;
case GeckoProcessType_GPU:
break;
case GeckoProcessType_Default:
default:
MOZ_CRASH("Bad process type in GeckoChildProcessHost");

View File

@ -19,7 +19,7 @@
*/
namespace mozilla {
namespace plugins {
namespace ipc {
template<class T>
class TaskFactory : public RevocableStore
@ -103,7 +103,7 @@ private:
T* object_;
};
} // namespace plugins
} // namespace ipc
} // namespace mozilla
#endif // mozilla_plugins_TaskFactory_h

View File

@ -35,6 +35,7 @@ EXPORTS.mozilla.ipc += [
'SharedMemory.h',
'SharedMemoryBasic.h',
'Shmem.h',
'TaskFactory.h',
'Transport.h',
'URIUtils.h',
'WindowsMessageLoop.h',

View File

@ -2372,7 +2372,7 @@ IsSimdTuple(ModuleValidator& m, ParseNode* pn, SimdType* type)
}
static bool
IsNumericLiteral(ModuleValidator& m, ParseNode* pn);
IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd = nullptr);
static NumLit
ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn);
@ -2423,11 +2423,16 @@ IsSimdLiteral(ModuleValidator& m, ParseNode* pn)
}
static bool
IsNumericLiteral(ModuleValidator& m, ParseNode* pn)
IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd)
{
return IsNumericNonFloatLiteral(pn) ||
IsFloatLiteral(m, pn) ||
IsSimdLiteral(m, pn);
if (IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn))
return true;
if (IsSimdLiteral(m, pn)) {
if (isSimd)
*isSimd = true;
return true;
}
return false;
}
// The JS grammar treats -42 as -(42) (i.e., with separate grammar
@ -2756,9 +2761,13 @@ SimdToExpr(SimdType type, SimdOperation op)
}
#undef CASE
#undef I32CASE
#undef F32CASE
#undef B32CASE
#undef I8x16CASE
#undef I16x8CASE
#undef I32x4CASE
#undef F32x4CASE
#undef B8x16CASE
#undef B16x8CASE
#undef B32x4CASE
#undef ENUMERATE
typedef Vector<PropertyName*, 4, SystemAllocPolicy> NameVector;
@ -2832,6 +2841,13 @@ class MOZ_STACK_CLASS FunctionValidator
MOZ_ASSERT(continuableStack_.empty());
MOZ_ASSERT(breakLabels_.empty());
MOZ_ASSERT(continueLabels_.empty());
for (auto iter = locals_.all(); !iter.empty(); iter.popFront()) {
if (iter.front().value().type.isSimd()) {
setUsesSimd();
break;
}
}
return m_.mg().finishFuncDef(funcIndex, &fg_);
}
@ -2851,6 +2867,16 @@ class MOZ_STACK_CLASS FunctionValidator
return m_.failName(pn, fmt, name);
}
/***************************************************** Attributes */
void setUsesSimd() {
fg_.setUsesSimd();
}
void setUsesAtomics() {
fg_.setUsesAtomics();
}
/***************************************************** Local scope setup */
bool addLocal(ParseNode* pn, PropertyName* name, Type type) {
@ -3726,9 +3752,13 @@ IsLiteralOrConst(FunctionValidator& f, ParseNode* pn, NumLit* lit)
return true;
}
if (!IsNumericLiteral(f.m(), pn))
bool isSimd = false;
if (!IsNumericLiteral(f.m(), pn, &isSimd))
return false;
if (isSimd)
f.setUsesSimd();
*lit = ExtractNumericLiteral(f.m(), pn);
return true;
}
@ -4528,6 +4558,8 @@ static bool
CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func,
Type* type)
{
f.setUsesAtomics();
switch (func) {
case AsmJSAtomicsBuiltin_compareExchange:
return CheckAtomicsCompareExchange(f, callNode, type);
@ -5450,6 +5482,8 @@ static bool
CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
Type* type)
{
f.setUsesSimd();
MOZ_ASSERT(global->isSimdOperation());
SimdType opType = global->simdOperationType();
@ -5542,6 +5576,8 @@ static bool
CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
Type* type)
{
f.setUsesSimd();
MOZ_ASSERT(call->isKind(PNK_CALL));
SimdType simdType = global->simdCtorType();
@ -5674,7 +5710,10 @@ CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
if (IsNumericLiteral(f.m(), call)) {
bool isSimd = false;
if (IsNumericLiteral(f.m(), call, &isSimd)) {
if (isSimd)
f.setUsesSimd();
NumLit lit = ExtractNumericLiteral(f.m(), call);
if (!f.writeConstExpr(lit))
return false;
@ -6213,8 +6252,12 @@ CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
{
JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
if (IsNumericLiteral(f.m(), expr))
bool isSimd = false;
if (IsNumericLiteral(f.m(), expr, &isSimd)) {
if (isSimd)
f.setUsesSimd();
return CheckNumericLiteral(f, expr, type);
}
switch (expr->getKind()) {
case PNK_NAME: return CheckVarRef(f, expr, type);

View File

@ -126,9 +126,8 @@ CheckValType(JSContext* cx, Decoder& d, ValType type)
case ValType::F64:
return true;
case ValType::I64:
#ifndef JS_CPU_X64
return Fail(cx, d, "i64 NYI on this platform");
#endif
if (!IsI64Implemented())
return Fail(cx, d, "i64 NYI on this platform");
return true;
default:
// Note: it's important not to remove this default since readValType()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2016 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef asmjs_wasm_baseline_compile_h
#define asmjs_wasm_baseline_compile_h
#include "asmjs/WasmBinary.h"
#include "asmjs/WasmIonCompile.h"
namespace js {
namespace wasm {
class FunctionGenerator;
// Return true if BaselineCompileFunction can generate code for the
// function held in the FunctionGenerator. If false is returned a
// different compilation strategy must be chosen.
//
// This allows the baseline compiler to have different capabilities on
// different platforms and defer to the full Ion compiler if
// capabilities are missing. The FunctionGenerator and other data
// structures contain information about the capabilities that are
// required to compile the function.
bool
BaselineCanCompile(const FunctionGenerator* fg);
// Generate adequate code quickly.
bool
BaselineCompileFunction(IonCompileTask* task);
} // namespace wasm
} // namespace js
#endif // asmjs_wasm_baseline_compile_h

View File

@ -20,6 +20,7 @@
#include "mozilla/EnumeratedRange.h"
#include "asmjs/WasmBaselineCompile.h"
#include "asmjs/WasmIonCompile.h"
#include "asmjs/WasmStubs.h"
@ -802,14 +803,18 @@ ModuleGenerator::finishFuncDef(uint32_t funcIndex, FunctionGenerator* fg)
if (!func)
return false;
fg->task_->init(Move(func));
JSRuntime* rt = cx_->compartment()->runtimeFromAnyThread();
bool baselineCompile = rt->options().wasmAlwaysBaseline() && BaselineCanCompile(fg);
fg->task_->init(Move(func), baselineCompile ? IonCompileTask::CompileMode::Baseline
: IonCompileTask::CompileMode::Ion);
if (parallel_) {
if (!StartOffThreadWasmCompile(cx_, fg->task_))
return false;
outstanding_++;
} else {
if (!IonCompileFunction(fg->task_))
if (!CompileFunction(fg->task_))
return false;
if (!finishTask(fg->task_))
return false;

View File

@ -212,6 +212,8 @@ class MOZ_STACK_CLASS FunctionGenerator
ModuleGenerator* m_;
IonCompileTask* task_;
bool usesSimd_;
bool usesAtomics_;
// Data created during function generation, then handed over to the
// FuncBytes in ModuleGenerator::finishFunc().
@ -222,9 +224,27 @@ class MOZ_STACK_CLASS FunctionGenerator
public:
FunctionGenerator()
: m_(nullptr), task_(nullptr), lineOrBytecode_(0)
: m_(nullptr), task_(nullptr), usesSimd_(false), usesAtomics_(false), lineOrBytecode_(0)
{}
bool usesSimd() const {
return usesSimd_;
}
void setUsesSimd() {
usesSimd_ = true;
}
bool usesAtomics() const {
return usesAtomics_;
}
void setUsesAtomics() {
usesAtomics_ = true;
}
bool usesSignalsForInterrupts() const {
return m_->args().useSignalHandlersForInterrupt;
}
Bytes& bytes() {
return bytes_;
}

View File

@ -18,6 +18,7 @@
#include "asmjs/WasmIonCompile.h"
#include "asmjs/WasmBaselineCompile.h"
#include "asmjs/WasmBinaryIterator.h"
#include "asmjs/WasmGenerator.h"
@ -3393,6 +3394,8 @@ EmitExpr(FunctionCompiler& f)
bool
wasm::IonCompileFunction(IonCompileTask* task)
{
MOZ_ASSERT(task->mode() == IonCompileTask::CompileMode::Ion);
const FuncBytes& func = task->func();
FuncCompileResults& results = task->results();
@ -3463,3 +3466,18 @@ wasm::IonCompileFunction(IonCompileTask* task)
return true;
}
bool
wasm::CompileFunction(IonCompileTask* task)
{
switch (task->mode()) {
case wasm::IonCompileTask::CompileMode::Ion:
return wasm::IonCompileFunction(task);
case wasm::IonCompileTask::CompileMode::Baseline:
return wasm::BaselineCompileFunction(task);
case wasm::IonCompileTask::CompileMode::None:
MOZ_CRASH("Uninitialized task");
}
// Silence gcc 5.2.1 warning.
return false;
}

Some files were not shown because too many files have changed in this diff Show More