mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
58167fd092
2
CLOBBER
2
CLOBBER
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -20,4 +20,3 @@ tags = openwindow
|
||||
[browser_broadcastchannel.js]
|
||||
[browser_blobUrl.js]
|
||||
[browser_middleClick.js]
|
||||
[browser_imageCache.js]
|
||||
|
@ -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");
|
||||
});
|
@ -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">
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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]
|
@ -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);
|
||||
});
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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"); }
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
2
dom/media/test/external/requirements.txt
vendored
2
dom/media/test/external/requirements.txt
vendored
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.");
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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]
|
||||
|
@ -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(' ... ')) : '')));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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>
|
@ -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");
|
||||
|
@ -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>
|
@ -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>
|
@ -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);
|
||||
})
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -43,7 +43,6 @@ EXPORTS.mozilla.plugins += [
|
||||
'PluginWidgetParent.h',
|
||||
'StreamNotifyChild.h',
|
||||
'StreamNotifyParent.h',
|
||||
'TaskFactory.h',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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 ();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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));
|
||||
|
@ -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
52
gfx/ipc/GPUChild.cpp
Normal 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
34
gfx/ipc/GPUChild.h
Normal 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
62
gfx/ipc/GPUParent.cpp
Normal 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
32
gfx/ipc/GPUParent.h
Normal 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
209
gfx/ipc/GPUProcessHost.cpp
Normal 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
125
gfx/ipc/GPUProcessHost.h
Normal 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_
|
37
gfx/ipc/GPUProcessImpl.cpp
Normal file
37
gfx/ipc/GPUProcessImpl.cpp
Normal 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
34
gfx/ipc/GPUProcessImpl.h
Normal 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__
|
@ -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>
|
||||
|
@ -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
17
gfx/ipc/PGPU.ipdl
Normal 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
|
@ -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')
|
||||
|
@ -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
|
@ -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 */
|
112
gfx/thebes/PrintTargetWindows.cpp
Normal file
112
gfx/thebes/PrintTargetWindows.cpp
Normal 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
|
43
gfx/thebes/PrintTargetWindows.h
Normal file
43
gfx/thebes/PrintTargetWindows.h
Normal 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 */
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 += [
|
||||
|
@ -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*
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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
|
@ -35,6 +35,7 @@ EXPORTS.mozilla.ipc += [
|
||||
'SharedMemory.h',
|
||||
'SharedMemoryBasic.h',
|
||||
'Shmem.h',
|
||||
'TaskFactory.h',
|
||||
'Transport.h',
|
||||
'URIUtils.h',
|
||||
'WindowsMessageLoop.h',
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
6488
js/src/asmjs/WasmBaselineCompile.cpp
Normal file
6488
js/src/asmjs/WasmBaselineCompile.cpp
Normal file
File diff suppressed because it is too large
Load Diff
49
js/src/asmjs/WasmBaselineCompile.h
Normal file
49
js/src/asmjs/WasmBaselineCompile.h
Normal 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
|
@ -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;
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user