Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Brindusan Cristian 2019-01-15 06:41:22 +02:00
commit 338aeb2777
95 changed files with 1790 additions and 1057 deletions

View file

@ -307,7 +307,8 @@ void AccessibleWrap::GetRoleDescription(role aRole,
return; return;
} }
if (aRole == roles::HEADING) { if (aRole == roles::HEADING && aAttributes) {
// The heading level is an attribute, so we need that.
nsString level; nsString level;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"), level); rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"), level);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -411,7 +412,7 @@ bool AccessibleWrap::WrapperRangeInfo(double* aCurVal, double* aMinVal,
return false; return false;
} }
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle() { mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(bool aSmall) {
nsAutoString name; nsAutoString name;
Name(name); Name(name);
nsAutoString textValue; nsAutoString textValue;
@ -419,6 +420,10 @@ mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle() {
nsAutoString nodeID; nsAutoString nodeID;
WrapperDOMNodeID(nodeID); WrapperDOMNodeID(nodeID);
if (aSmall) {
return ToBundle(State(), Bounds(), ActionCount(), name, textValue, nodeID);
}
double curValue = UnspecifiedNaN<double>(); double curValue = UnspecifiedNaN<double>();
double minValue = UnspecifiedNaN<double>(); double minValue = UnspecifiedNaN<double>();
double maxValue = UnspecifiedNaN<double>(); double maxValue = UnspecifiedNaN<double>();
@ -513,63 +518,66 @@ mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(
GECKOBUNDLE_PUT(nodeInfo, "rangeInfo", rangeInfo); GECKOBUNDLE_PUT(nodeInfo, "rangeInfo", rangeInfo);
} }
nsString inputTypeAttr; if (aAttributes) {
nsAccUtils::GetAccAttr(aAttributes, nsGkAtoms::textInputType, inputTypeAttr); nsString inputTypeAttr;
int32_t inputType = GetInputType(inputTypeAttr); nsAccUtils::GetAccAttr(aAttributes, nsGkAtoms::textInputType,
if (inputType) { inputTypeAttr);
GECKOBUNDLE_PUT(nodeInfo, "inputType", int32_t inputType = GetInputType(inputTypeAttr);
java::sdk::Integer::ValueOf(inputType)); if (inputType) {
} GECKOBUNDLE_PUT(nodeInfo, "inputType",
java::sdk::Integer::ValueOf(inputType));
nsString posinset;
nsresult rv =
aAttributes->GetStringProperty(NS_LITERAL_CSTRING("posinset"), posinset);
if (NS_SUCCEEDED(rv)) {
int32_t rowIndex;
if (sscanf(NS_ConvertUTF16toUTF8(posinset).get(), "%d", &rowIndex) > 0) {
GECKOBUNDLE_START(collectionItemInfo);
GECKOBUNDLE_PUT(collectionItemInfo, "rowIndex",
java::sdk::Integer::ValueOf(rowIndex));
GECKOBUNDLE_PUT(collectionItemInfo, "columnIndex",
java::sdk::Integer::ValueOf(0));
GECKOBUNDLE_PUT(collectionItemInfo, "rowSpan",
java::sdk::Integer::ValueOf(1));
GECKOBUNDLE_PUT(collectionItemInfo, "columnSpan",
java::sdk::Integer::ValueOf(1));
GECKOBUNDLE_FINISH(collectionItemInfo);
GECKOBUNDLE_PUT(nodeInfo, "collectionItemInfo", collectionItemInfo);
} }
}
nsString colSize; nsString posinset;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("child-item-count"), nsresult rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("posinset"),
colSize); posinset);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
int32_t rowCount; int32_t rowIndex;
if (sscanf(NS_ConvertUTF16toUTF8(colSize).get(), "%d", &rowCount) > 0) { if (sscanf(NS_ConvertUTF16toUTF8(posinset).get(), "%d", &rowIndex) > 0) {
GECKOBUNDLE_START(collectionInfo); GECKOBUNDLE_START(collectionItemInfo);
GECKOBUNDLE_PUT(collectionInfo, "rowCount", GECKOBUNDLE_PUT(collectionItemInfo, "rowIndex",
java::sdk::Integer::ValueOf(rowCount)); java::sdk::Integer::ValueOf(rowIndex));
GECKOBUNDLE_PUT(collectionInfo, "columnCount", GECKOBUNDLE_PUT(collectionItemInfo, "columnIndex",
java::sdk::Integer::ValueOf(1)); java::sdk::Integer::ValueOf(0));
GECKOBUNDLE_PUT(collectionItemInfo, "rowSpan",
java::sdk::Integer::ValueOf(1));
GECKOBUNDLE_PUT(collectionItemInfo, "columnSpan",
java::sdk::Integer::ValueOf(1));
GECKOBUNDLE_FINISH(collectionItemInfo);
nsString unused; GECKOBUNDLE_PUT(nodeInfo, "collectionItemInfo", collectionItemInfo);
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("hierarchical"),
unused);
if (NS_SUCCEEDED(rv)) {
GECKOBUNDLE_PUT(collectionInfo, "isHierarchical",
java::sdk::Boolean::TRUE());
} }
}
if (IsSelect()) { nsString colSize;
int32_t selectionMode = (aState & states::MULTISELECTABLE) ? 2 : 1; rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("child-item-count"),
GECKOBUNDLE_PUT(collectionInfo, "selectionMode", colSize);
java::sdk::Integer::ValueOf(selectionMode)); if (NS_SUCCEEDED(rv)) {
int32_t rowCount;
if (sscanf(NS_ConvertUTF16toUTF8(colSize).get(), "%d", &rowCount) > 0) {
GECKOBUNDLE_START(collectionInfo);
GECKOBUNDLE_PUT(collectionInfo, "rowCount",
java::sdk::Integer::ValueOf(rowCount));
GECKOBUNDLE_PUT(collectionInfo, "columnCount",
java::sdk::Integer::ValueOf(1));
nsString unused;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("hierarchical"),
unused);
if (NS_SUCCEEDED(rv)) {
GECKOBUNDLE_PUT(collectionInfo, "isHierarchical",
java::sdk::Boolean::TRUE());
}
if (IsSelect()) {
int32_t selectionMode = (aState & states::MULTISELECTABLE) ? 2 : 1;
GECKOBUNDLE_PUT(collectionInfo, "selectionMode",
java::sdk::Integer::ValueOf(selectionMode));
}
GECKOBUNDLE_FINISH(collectionInfo);
GECKOBUNDLE_PUT(nodeInfo, "collectionInfo", collectionInfo);
} }
GECKOBUNDLE_FINISH(collectionInfo);
GECKOBUNDLE_PUT(nodeInfo, "collectionInfo", collectionInfo);
} }
} }
@ -586,42 +594,3 @@ mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(
return nodeInfo; return nodeInfo;
} }
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToSmallBundle() {
return ToSmallBundle(State(), Bounds(), ActionCount());
}
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToSmallBundle(
const uint64_t aState, const nsIntRect& aBounds,
const uint8_t aActionCount) {
GECKOBUNDLE_START(nodeInfo);
GECKOBUNDLE_PUT(nodeInfo, "id", java::sdk::Integer::ValueOf(VirtualViewID()));
AccessibleWrap* parent = WrapperParent();
GECKOBUNDLE_PUT(
nodeInfo, "parentId",
java::sdk::Integer::ValueOf(parent ? parent->VirtualViewID() : 0));
uint32_t flags = GetFlags(WrapperRole(), aState, aActionCount);
GECKOBUNDLE_PUT(nodeInfo, "flags", java::sdk::Integer::ValueOf(flags));
GECKOBUNDLE_PUT(nodeInfo, "className",
java::sdk::Integer::ValueOf(AndroidClass()));
const int32_t data[4] = {aBounds.x, aBounds.y, aBounds.x + aBounds.width,
aBounds.y + aBounds.height};
GECKOBUNDLE_PUT(nodeInfo, "bounds", jni::IntArray::New(data, 4));
auto childCount = ChildCount();
nsTArray<int32_t> children(childCount);
for (uint32_t i = 0; i < childCount; ++i) {
auto child = static_cast<AccessibleWrap*>(GetChildAt(i));
children.AppendElement(child->VirtualViewID());
}
GECKOBUNDLE_PUT(nodeInfo, "children",
jni::IntArray::New(children.Elements(), children.Length()));
GECKOBUNDLE_FINISH(nodeInfo);
return nodeInfo;
}

View file

@ -33,20 +33,17 @@ class AccessibleWrap : public Accessible {
virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset); virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset);
mozilla::java::GeckoBundle::LocalRef ToBundle(); mozilla::java::GeckoBundle::LocalRef ToBundle(bool aSmall = false);
mozilla::java::GeckoBundle::LocalRef ToBundle( mozilla::java::GeckoBundle::LocalRef ToBundle(
const uint64_t aState, const nsIntRect& aBounds, const uint64_t aState, const nsIntRect& aBounds,
const uint8_t aActionCount, const nsString& aName, const uint8_t aActionCount, const nsString& aName,
const nsString& aTextValue, const nsString& aDOMNodeID, const nsString& aTextValue, const nsString& aDOMNodeID,
const double& aCurVal, const double& aMinVal, const double& aMaxVal, const double& aCurVal = UnspecifiedNaN<double>(),
const double& aStep, nsIPersistentProperties* aAttributes); const double& aMinVal = UnspecifiedNaN<double>(),
const double& aMaxVal = UnspecifiedNaN<double>(),
mozilla::java::GeckoBundle::LocalRef ToSmallBundle( const double& aStep = UnspecifiedNaN<double>(),
const uint64_t aState, const nsIntRect& aBounds, nsIPersistentProperties* aAttributes = nullptr);
const uint8_t aActionCount);
mozilla::java::GeckoBundle::LocalRef ToSmallBundle();
virtual void WrapperDOMNodeID(nsString& aDOMNodeID); virtual void WrapperDOMNodeID(nsString& aDOMNodeID);

View file

@ -133,10 +133,18 @@ void DocAccessibleWrap::CacheViewportCallback(nsITimer* aTimer,
auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc() auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
? 0 ? 0
: reinterpret_cast<uint64_t>(accessible->UniqueID()); : reinterpret_cast<uint64_t>(accessible->UniqueID());
nsAutoString name;
accessible->Name(name);
nsAutoString textValue;
accessible->Value(textValue);
nsAutoString nodeID;
static_cast<AccessibleWrap*>(accessible)->WrapperDOMNodeID(nodeID);
cacheData.AppendElement( cacheData.AppendElement(
BatchData(accessible->Document()->IPCDoc(), uid, accessible->State(), BatchData(accessible->Document()->IPCDoc(), uid, accessible->State(),
accessible->Bounds(), accessible->ActionCount(), nsString(), accessible->Bounds(), accessible->ActionCount(), name,
nsString(), nsString(), UnspecifiedNaN<double>(), textValue, nodeID, UnspecifiedNaN<double>(),
UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
UnspecifiedNaN<double>(), nsTArray<Attribute>())); UnspecifiedNaN<double>(), nsTArray<Attribute>()));
} }

View file

@ -333,10 +333,11 @@ void SessionAccessibility::ReplaceViewportCache(
if (aData.Length() == aAccessibles.Length()) { if (aData.Length() == aAccessibles.Length()) {
const BatchData& data = aData.ElementAt(i); const BatchData& data = aData.ElementAt(i);
auto bundle = auto bundle =
acc->ToSmallBundle(data.State(), data.Bounds(), data.ActionCount()); acc->ToBundle(data.State(), data.Bounds(), data.ActionCount(),
data.Name(), data.TextValue(), data.DOMNodeID());
infos->SetElement(i, bundle); infos->SetElement(i, bundle);
} else { } else {
infos->SetElement(i, acc->ToSmallBundle()); infos->SetElement(i, acc->ToBundle(true));
} }
} }
@ -380,10 +381,11 @@ void SessionAccessibility::UpdateCachedBounds(
if (aData.Length() == aAccessibles.Length()) { if (aData.Length() == aAccessibles.Length()) {
const BatchData& data = aData.ElementAt(i); const BatchData& data = aData.ElementAt(i);
auto bundle = auto bundle =
acc->ToSmallBundle(data.State(), data.Bounds(), data.ActionCount()); acc->ToBundle(data.State(), data.Bounds(), data.ActionCount(),
data.Name(), data.TextValue(), data.DOMNodeID());
infos->SetElement(i, bundle); infos->SetElement(i, bundle);
} else { } else {
infos->SetElement(i, acc->ToSmallBundle()); infos->SetElement(i, acc->ToBundle(true));
} }
} }

View file

@ -411,6 +411,8 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvScrollingEvent(
#if defined(ANDROID) #if defined(ANDROID)
ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX, ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
aMaxScrollY); aMaxScrollY);
#else
ProxyEvent(target, aType);
#endif #endif
if (!nsCoreUtils::AccEventObserversExist()) { if (!nsCoreUtils::AccEventObserversExist()) {

View file

@ -1174,6 +1174,18 @@ pref("services.sync.prefs.sync.browser.ctrlTab.recentlyUsedOrder", true);
pref("services.sync.prefs.sync.browser.download.useDownloadDir", true); pref("services.sync.prefs.sync.browser.download.useDownloadDir", true);
pref("services.sync.prefs.sync.browser.formfill.enable", true); pref("services.sync.prefs.sync.browser.formfill.enable", true);
pref("services.sync.prefs.sync.browser.link.open_newwindow", true); pref("services.sync.prefs.sync.browser.link.open_newwindow", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSearch", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.topsites", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.topSitesRows", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.snippets", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.section.topstories", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.topstories.rows", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.feeds.section.highlights", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeVisited", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeBookmarks", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includeDownloads", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.includePocket", true);
pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.section.highlights.rows", true);
pref("services.sync.prefs.sync.browser.newtabpage.enabled", true); pref("services.sync.prefs.sync.browser.newtabpage.enabled", true);
pref("services.sync.prefs.sync.browser.newtabpage.pinned", true); pref("services.sync.prefs.sync.browser.newtabpage.pinned", true);
pref("services.sync.prefs.sync.browser.offline-apps.notify", true); pref("services.sync.prefs.sync.browser.offline-apps.notify", true);

View file

@ -492,7 +492,7 @@ window._gBrowser = {
}, },
set userTypedValue(val) { set userTypedValue(val) {
return this.selectedBrowser.userTypedValue = val; this.selectedBrowser.userTypedValue = val;
}, },
get userTypedValue() { get userTypedValue() {

View file

@ -10,6 +10,9 @@ prefs =
browser.migration.version=9999999 browser.migration.version=9999999
browser.startup.record=true browser.startup.record=true
gfx.canvas.willReadFrequently.enable=true gfx.canvas.willReadFrequently.enable=true
# The form autofill framescript is only used in certain locales if this
# pref is set to 'detect', which is the default value on non-Nightly.
extensions.formautofill.available='on'
support-files = support-files =
head.js head.js
[browser_appmenu.js] [browser_appmenu.js]

View file

@ -1,8 +1,8 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
/* This test records which services, JS components, process scripts, and JS /* This test records which services, JS components, frame scripts, process
* modules are loaded when creating a new content process. * scripts, and JS modules are loaded when creating a new content process.
* *
* If you made changes that cause this test to fail, it's likely because you * If you made changes that cause this test to fail, it's likely because you
* are loading more JS code during content process startup. * are loading more JS code during content process startup.
@ -68,6 +68,23 @@ const whitelist = {
"resource://gre/modules/ExtensionUtils.jsm", "resource://gre/modules/ExtensionUtils.jsm",
"resource://gre/modules/MessageChannel.jsm", "resource://gre/modules/MessageChannel.jsm",
]), ]),
frameScripts: new Set([
// Test related
"resource://specialpowers/MozillaLogger.js",
"resource://specialpowers/specialpowersFrameScript.js",
"chrome://mochikit/content/shutdown-leaks-collector.js",
"chrome://mochikit/content/tests/SimpleTest/AsyncUtilsContent.js",
"chrome://mochikit/content/tests/BrowserTestUtils/content-utils.js",
// Browser front-end
"chrome://global/content/browser-content.js",
// Forms
"chrome://formautofill/content/FormAutofillFrameScript.js",
// Extensions
"resource://gre/modules/addons/Content.js",
]),
processScripts: new Set([ processScripts: new Set([
"chrome://global/content/process-content.js", "chrome://global/content/process-content.js",
"resource:///modules/ContentObservers.js", "resource:///modules/ContentObservers.js",
@ -89,6 +106,7 @@ const intermittently_loaded_whitelist = {
modules: new Set([ modules: new Set([
"resource://gre/modules/sessionstore/Utils.jsm", "resource://gre/modules/sessionstore/Utils.jsm",
]), ]),
frameScripts: new Set([]),
processScripts: new Set([]), processScripts: new Set([]),
}; };
@ -140,6 +158,12 @@ add_task(async function() {
let loadedInfo = await promise; let loadedInfo = await promise;
// Gather loaded frame scripts.
loadedInfo.frameScripts = {};
for (let [uri] of Services.mm.getDelayedFrameScripts()) {
loadedInfo.frameScripts[uri] = "";
}
// Gather loaded process scripts. // Gather loaded process scripts.
loadedInfo.processScripts = {}; loadedInfo.processScripts = {};
for (let [uri] of Services.ppmm.getDelayedProcessScripts()) { for (let [uri] of Services.ppmm.getDelayedProcessScripts()) {

View file

@ -2820,6 +2820,9 @@ void EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
#ifdef XP_MACOSX #ifdef XP_MACOSX
static bool NodeAllowsClickThrough(nsINode* aNode) { static bool NodeAllowsClickThrough(nsINode* aNode) {
while (aNode) { while (aNode) {
if (aNode->IsXULElement(nsGkAtoms::browser)) {
return false;
}
if (aNode->IsXULElement()) { if (aNode->IsXULElement()) {
mozilla::dom::Element* element = aNode->AsElement(); mozilla::dom::Element* element = aNode->AsElement();
static Element::AttrValuesArray strings[] = {nsGkAtoms::always, static Element::AttrValuesArray strings[] = {nsGkAtoms::always,

View file

@ -324,28 +324,30 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
RefPtr<Session> mSession; RefPtr<Session> mSession;
}; };
// Fire start event and set mimeType, run in main thread task. // Fire a named event, run in main thread task.
class DispatchStartEventRunnable : public Runnable { class DispatchEventRunnable : public Runnable {
public: public:
explicit DispatchStartEventRunnable(Session* aSession) explicit DispatchEventRunnable(Session* aSession,
: Runnable("dom::MediaRecorder::Session::DispatchStartEventRunnable"), const nsAString& aEventName)
mSession(aSession) {} : Runnable("dom::MediaRecorder::Session::DispatchEventRunnable"),
mSession(aSession),
mEventName(aEventName) {}
NS_IMETHOD Run() override { NS_IMETHOD Run() override {
LOG(LogLevel::Debug, LOG(LogLevel::Debug,
("Session.DispatchStartEventRunnable s=(%p)", mSession.get())); ("Session.DispatchEventRunnable s=(%p) e=(%s)", mSession.get(),
NS_ConvertUTF16toUTF8(mEventName).get()));
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(mSession->mRecorder, NS_OK); NS_ENSURE_TRUE(mSession->mRecorder, NS_OK);
RefPtr<MediaRecorder> recorder = mSession->mRecorder; mSession->mRecorder->DispatchSimpleEvent(mEventName);
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
return NS_OK; return NS_OK;
} }
private: private:
RefPtr<Session> mSession; RefPtr<Session> mSession;
nsString mEventName;
}; };
// Main thread task. // Main thread task.
@ -591,6 +593,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
} }
mEncoder->Suspend(TimeStamp::Now()); mEncoder->Suspend(TimeStamp::Now());
NS_DispatchToMainThread(
new DispatchEventRunnable(this, NS_LITERAL_STRING("pause")));
return NS_OK; return NS_OK;
} }
@ -603,6 +607,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
} }
mEncoder->Resume(TimeStamp::Now()); mEncoder->Resume(TimeStamp::Now());
NS_DispatchToMainThread(
new DispatchEventRunnable(this, NS_LITERAL_STRING("resume")));
return NS_OK; return NS_OK;
} }
@ -983,7 +989,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
if (mRunningState.isOk() && if (mRunningState.isOk() &&
(mRunningState.unwrap() == RunningState::Idling || (mRunningState.unwrap() == RunningState::Idling ||
mRunningState.unwrap() == RunningState::Starting)) { mRunningState.unwrap() == RunningState::Starting)) {
NS_DispatchToMainThread(new DispatchStartEventRunnable(this)); NS_DispatchToMainThread(
new DispatchEventRunnable(this, NS_LITERAL_STRING("start")));
} }
if (rv == NS_OK) { if (rv == NS_OK) {
@ -1050,7 +1057,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
} }
self->mMimeType = mime; self->mMimeType = mime;
self->mRecorder->SetMimeType(self->mMimeType); self->mRecorder->SetMimeType(self->mMimeType);
auto startEvent = MakeRefPtr<DispatchStartEventRunnable>(self); auto startEvent = MakeRefPtr<DispatchEventRunnable>(
self, NS_LITERAL_STRING("start"));
startEvent->Run(); startEvent->Run();
} }
} }
@ -1323,7 +1331,6 @@ void MediaRecorder::Stop(ErrorResult& aResult) {
LOG(LogLevel::Debug, ("MediaRecorder.Stop %p", this)); LOG(LogLevel::Debug, ("MediaRecorder.Stop %p", this));
MediaRecorderReporter::RemoveMediaRecorder(this); MediaRecorderReporter::RemoveMediaRecorder(this);
if (mState == RecordingState::Inactive) { if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
mState = RecordingState::Inactive; mState = RecordingState::Inactive;
@ -1332,12 +1339,16 @@ void MediaRecorder::Stop(ErrorResult& aResult) {
} }
void MediaRecorder::Pause(ErrorResult& aResult) { void MediaRecorder::Pause(ErrorResult& aResult) {
LOG(LogLevel::Debug, ("MediaRecorder.Pause")); LOG(LogLevel::Debug, ("MediaRecorder.Pause %p", this));
if (mState == RecordingState::Inactive) { if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
if (mState == RecordingState::Paused) {
return;
}
MOZ_ASSERT(mSessions.Length() > 0); MOZ_ASSERT(mSessions.Length() > 0);
nsresult rv = mSessions.LastElement()->Pause(); nsresult rv = mSessions.LastElement()->Pause();
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@ -1346,16 +1357,19 @@ void MediaRecorder::Pause(ErrorResult& aResult) {
} }
mState = RecordingState::Paused; mState = RecordingState::Paused;
DispatchSimpleEvent(NS_LITERAL_STRING("pause"));
} }
void MediaRecorder::Resume(ErrorResult& aResult) { void MediaRecorder::Resume(ErrorResult& aResult) {
LOG(LogLevel::Debug, ("MediaRecorder.Resume")); LOG(LogLevel::Debug, ("MediaRecorder.Resume %p", this));
if (mState == RecordingState::Inactive) { if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
if (mState == RecordingState::Recording) {
return;
}
MOZ_ASSERT(mSessions.Length() > 0); MOZ_ASSERT(mSessions.Length() > 0);
nsresult rv = mSessions.LastElement()->Resume(); nsresult rv = mSessions.LastElement()->Resume();
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@ -1364,7 +1378,6 @@ void MediaRecorder::Resume(ErrorResult& aResult) {
} }
mState = RecordingState::Recording; mState = RecordingState::Recording;
DispatchSimpleEvent(NS_LITERAL_STRING("resume"));
} }
void MediaRecorder::RequestData(ErrorResult& aResult) { void MediaRecorder::RequestData(ErrorResult& aResult) {
@ -1576,15 +1589,12 @@ void MediaRecorder::DispatchSimpleEvent(const nsAString& aStr) {
return; return;
} }
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); rv = DOMEventTargetHelper::DispatchTrustedEvent(aStr);
event->InitEvent(aStr, false, false); if (NS_FAILED(rv)) {
event->SetTrusted(true); LOG(LogLevel::Error,
("MediaRecorder.DispatchSimpleEvent: DispatchTrustedEvent failed %p",
IgnoredErrorResult res; this));
DispatchEvent(*event, res);
if (res.Failed()) {
NS_ERROR("Failed to dispatch the event!!!"); NS_ERROR("Failed to dispatch the event!!!");
return;
} }
} }

View file

@ -575,10 +575,10 @@ static void InitInputBuffer(const CDMInputBuffer& aBuffer,
aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>(); aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
if (aBuffer.mEncryptionScheme() > GMPEncryptionScheme::kGMPEncryptionNone) { if (aBuffer.mEncryptionScheme() > GMPEncryptionScheme::kGMPEncryptionNone) {
// Cbcs is not yet supported, so we expect only cenc if the buffer us
// encrypted
MOZ_ASSERT(aBuffer.mEncryptionScheme() == MOZ_ASSERT(aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCenc); GMPEncryptionScheme::kGMPEncryptionCenc ||
aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCbcs);
aInputBuffer.key_id = aBuffer.mKeyId().Elements(); aInputBuffer.key_id = aBuffer.mKeyId().Elements();
aInputBuffer.key_id_size = aBuffer.mKeyId().Length(); aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
@ -595,6 +595,8 @@ static void InitInputBuffer(const CDMInputBuffer& aBuffer,
aInputBuffer.encryption_scheme = aInputBuffer.encryption_scheme =
ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme()); ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme());
} }
aInputBuffer.pattern.crypt_byte_block = aBuffer.mCryptByteBlock();
aInputBuffer.pattern.skip_byte_block = aBuffer.mSkipByteBlock();
aInputBuffer.timestamp = aBuffer.mTimestamp(); aInputBuffer.timestamp = aBuffer.mTimestamp();
} }

View file

@ -264,15 +264,23 @@ bool ChromiumCDMParent::InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer,
MOZ_ASSERT_UNREACHABLE("Should not have unrecognized encryption type"); MOZ_ASSERT_UNREACHABLE("Should not have unrecognized encryption type");
break; break;
} }
const nsTArray<uint8_t>& iv =
encryptionScheme != GMPEncryptionScheme::kGMPEncryptionCbcs
? crypto.mIV
: crypto.mConstantIV;
aBuffer = gmp::CDMInputBuffer( aBuffer = gmp::CDMInputBuffer(
shmem, crypto.mKeyId, crypto.mIV, aSample->mTime.ToMicroseconds(), shmem, crypto.mKeyId, iv, aSample->mTime.ToMicroseconds(),
aSample->mDuration.ToMicroseconds(), crypto.mPlainSizes, aSample->mDuration.ToMicroseconds(), crypto.mPlainSizes,
crypto.mEncryptedSizes, encryptionScheme); crypto.mEncryptedSizes, crypto.mCryptByteBlock, crypto.mSkipByteBlock,
encryptionScheme);
MOZ_ASSERT( MOZ_ASSERT(
aBuffer.mEncryptionScheme() == GMPEncryptionScheme::kGMPEncryptionNone || aBuffer.mEncryptionScheme() == GMPEncryptionScheme::kGMPEncryptionNone ||
aBuffer.mEncryptionScheme() == aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCenc, GMPEncryptionScheme::kGMPEncryptionCenc ||
"aBuffer should use either no encryption or cenc, other kinds are not " aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCbcs,
"aBuffer should use no encryption, cenc, or cbcs, other kinds are not "
"yet supported"); "yet supported");
return true; return true;
} }

View file

@ -56,6 +56,8 @@ struct CDMInputBuffer {
int64_t mDuration; int64_t mDuration;
uint16_t[] mClearBytes; uint16_t[] mClearBytes;
uint32_t[] mCipherBytes; uint32_t[] mCipherBytes;
uint8_t mCryptByteBlock;
uint8_t mSkipByteBlock;
GMPEncryptionScheme mEncryptionScheme; GMPEncryptionScheme mEncryptionScheme;
}; };

View file

@ -16,7 +16,7 @@ var manager = new MediaTestManager;
var operationTests = [ var operationTests = [
{ {
operations: ['stop'], operations: ['stop'],
isValid: false isValid: true
}, },
{ {
operations: ['requestData'], operations: ['requestData'],
@ -70,7 +70,7 @@ var operationTests = [
}, },
{ {
operations: ['stop', 'start'], operations: ['stop', 'start'],
isValid: false isValid: true
}, },
{ {
operations: ['start', 'stop'], operations: ['start', 'stop'],
@ -156,7 +156,7 @@ var operationTests = [
}, },
{ {
operations: ['start', 'stop', 'stop'], operations: ['start', 'stop', 'stop'],
isValid: false isValid: true
}, },
{ {
operations: ['start', 'pause', 'resume', 'resume'], operations: ['start', 'pause', 'resume', 'resume'],

View file

@ -10,6 +10,7 @@
#include "mozilla/gfx/Logging.h" #include "mozilla/gfx/Logging.h"
#include "nsITimer.h" #include "nsITimer.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "VRGPUChild.h"
namespace mozilla { namespace mozilla {
namespace gfx { namespace gfx {
@ -150,6 +151,9 @@ void GPUProcessHost::Shutdown() {
// The channel might already be closed if we got here unexpectedly. // The channel might already be closed if we got here unexpectedly.
if (!mChannelClosed) { if (!mChannelClosed) {
if (VRGPUChild::IsCreated()) {
VRGPUChild::Get()->Close();
}
mGPUChild->SendShutdownVR(); mGPUChild->SendShutdownVR();
mGPUChild->Close(); mGPUChild->Close();
} }

View file

@ -378,7 +378,7 @@ class gfxPrefs final {
DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false); DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false);
DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled", VRAutoActivateEnabled, bool, false); DECL_GFX_PREF(Live, "dom.vr.autoactivate.enabled", VRAutoActivateEnabled, bool, false);
DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold", VRControllerTriggerThreshold, float, 0.1f); DECL_GFX_PREF(Live, "dom.vr.controller_trigger_threshold", VRControllerTriggerThreshold, float, 0.1f);
DECL_GFX_PREF(Once, "dom.vr.external.enabled", VRExternalEnabled, bool, true); DECL_GFX_PREF(Once, "dom.vr.external.enabled", VRExternalEnabled, bool, false);
DECL_GFX_PREF(Live, "dom.vr.external.notdetected.timeout", VRExternalNotDetectedTimeout, int32_t, 60000); DECL_GFX_PREF(Live, "dom.vr.external.notdetected.timeout", VRExternalNotDetectedTimeout, int32_t, 60000);
DECL_GFX_PREF(Live, "dom.vr.external.quit.timeout", VRExternalQuitTimeout, int32_t, 10000); DECL_GFX_PREF(Live, "dom.vr.external.quit.timeout", VRExternalQuitTimeout, int32_t, 10000);
DECL_GFX_PREF(Live, "dom.vr.navigation.timeout", VRNavigationTimeout, int32_t, 1000); DECL_GFX_PREF(Live, "dom.vr.navigation.timeout", VRNavigationTimeout, int32_t, 1000);

View file

@ -78,7 +78,7 @@ VRManager::VRManager()
#if !defined(MOZ_WIDGET_ANDROID) #if !defined(MOZ_WIDGET_ANDROID)
// The VR Service accesses all hardware from a separate process // The VR Service accesses all hardware from a separate process
// and replaces the other VRSystemManager when enabled. // and replaces the other VRSystemManager when enabled.
if (!gfxPrefs::VRProcessEnabled()) { if (!gfxPrefs::VRProcessEnabled() || !XRE_IsGPUProcess()) {
VRServiceManager::Get().CreateService(); VRServiceManager::Get().CreateService();
} }
if (VRServiceManager::Get().IsServiceValid()) { if (VRServiceManager::Get().IsServiceValid()) {
@ -127,6 +127,7 @@ void VRManager::Destroy() {
VRServiceManager::Get().Shutdown(); VRServiceManager::Get().Shutdown();
} }
#endif #endif
Shutdown();
mInitialized = false; mInitialized = false;
} }
@ -140,7 +141,9 @@ void VRManager::Shutdown() {
if (VRServiceManager::Get().IsServiceValid()) { if (VRServiceManager::Get().IsServiceValid()) {
VRServiceManager::Get().Stop(); VRServiceManager::Get().Stop();
} }
if (gfxPrefs::VRProcessEnabled() && mVRServiceStarted) { // XRE_IsGPUProcess() is helping us to check some platforms like
// Win 7 try which are not using GPU process but VR process is enabled.
if (XRE_IsGPUProcess() && gfxPrefs::VRProcessEnabled() && mVRServiceStarted) {
RefPtr<Runnable> task = NS_NewRunnableFunction( RefPtr<Runnable> task = NS_NewRunnableFunction(
"VRServiceManager::ShutdownVRProcess", "VRServiceManager::ShutdownVRProcess",
[]() -> void { VRServiceManager::Get().ShutdownVRProcess(); }); []() -> void { VRServiceManager::Get().ShutdownVRProcess(); });
@ -435,13 +438,15 @@ void VRManager::EnumerateVRDisplays() {
* is in progress * is in progress
*/ */
#if !defined(MOZ_WIDGET_ANDROID) #if !defined(MOZ_WIDGET_ANDROID)
if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) { if (!mVRServiceStarted) {
VRServiceManager::Get().CreateVRProcess(); if (XRE_IsGPUProcess() && gfxPrefs::VRProcessEnabled()) {
mVRServiceStarted = true; VRServiceManager::Get().CreateVRProcess();
} else if (!gfxPrefs::VRProcessEnabled()) {
if (VRServiceManager::Get().IsServiceValid()) {
VRServiceManager::Get().Start();
mVRServiceStarted = true; mVRServiceStarted = true;
} else {
if (VRServiceManager::Get().IsServiceValid()) {
VRServiceManager::Get().Start();
mVRServiceStarted = true;
}
} }
} }
#endif #endif

View file

@ -486,7 +486,8 @@ void VRSystemManagerExternal::OpenShmem() {
mShmemFile = mShmemFile =
CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
sizeof(VRExternalShmem), kShmemName); sizeof(VRExternalShmem), kShmemName);
MOZ_ASSERT(GetLastError() == 0); MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS);
MOZ_ASSERT(mShmemFile);
} else { } else {
mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName); mShmemFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, kShmemName);
} }

View file

@ -42,8 +42,19 @@ static StaticRefPtr<VRGPUChild> sVRGPUChildSingleton;
/*static*/ void VRGPUChild::Shutdown() { /*static*/ void VRGPUChild::Shutdown() {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (sVRGPUChildSingleton && !sVRGPUChildSingleton->IsClosed()) {
sVRGPUChildSingleton->Close();
}
sVRGPUChildSingleton = nullptr; sVRGPUChildSingleton = nullptr;
} }
void VRGPUChild::ActorDestroy(ActorDestroyReason aWhy) {
mClosed = true;
}
bool VRGPUChild::IsClosed() {
return mClosed;
}
} // namespace gfx } // namespace gfx
} // namespace mozilla } // namespace mozilla

View file

@ -21,12 +21,17 @@ class VRGPUChild final : public PVRGPUChild {
static bool IsCreated(); static bool IsCreated();
static void Shutdown(); static void Shutdown();
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
bool IsClosed();
protected: protected:
explicit VRGPUChild() {} explicit VRGPUChild() : mClosed(false) {}
~VRGPUChild() {} ~VRGPUChild() = default;
private: private:
DISALLOW_COPY_AND_ASSIGN(VRGPUChild); DISALLOW_COPY_AND_ASSIGN(VRGPUChild);
bool mClosed;
}; };
} // namespace gfx } // namespace gfx

View file

@ -13,13 +13,18 @@ namespace gfx {
using namespace ipc; using namespace ipc;
VRGPUParent::VRGPUParent(ProcessId aChildProcessId) { VRGPUParent::VRGPUParent(ProcessId aChildProcessId)
: mClosed(false) {
MOZ_COUNT_CTOR(VRGPUParent); MOZ_COUNT_CTOR(VRGPUParent);
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
SetOtherProcessId(aChildProcessId); SetOtherProcessId(aChildProcessId);
} }
VRGPUParent::~VRGPUParent() {
MOZ_COUNT_DTOR(VRGPUParent);
}
void VRGPUParent::ActorDestroy(ActorDestroyReason aWhy) { void VRGPUParent::ActorDestroy(ActorDestroyReason aWhy) {
#if !defined(MOZ_WIDGET_ANDROID) #if !defined(MOZ_WIDGET_ANDROID)
if (mVRService) { if (mVRService) {
@ -28,6 +33,7 @@ void VRGPUParent::ActorDestroy(ActorDestroyReason aWhy) {
} }
#endif #endif
mClosed = true;
MessageLoop::current()->PostTask( MessageLoop::current()->PostTask(
NewRunnableMethod("gfx::VRGPUParent::DeferredDestroy", this, NewRunnableMethod("gfx::VRGPUParent::DeferredDestroy", this,
&VRGPUParent::DeferredDestroy)); &VRGPUParent::DeferredDestroy));
@ -41,7 +47,7 @@ void VRGPUParent::DeferredDestroy() { mSelfRef = nullptr; }
MessageLoop::current()->PostTask(NewRunnableMethod<Endpoint<PVRGPUParent>&&>( MessageLoop::current()->PostTask(NewRunnableMethod<Endpoint<PVRGPUParent>&&>(
"gfx::VRGPUParent::Bind", vcp, &VRGPUParent::Bind, std::move(aEndpoint))); "gfx::VRGPUParent::Bind", vcp, &VRGPUParent::Bind, std::move(aEndpoint)));
return vcp; return vcp.forget();
} }
void VRGPUParent::Bind(Endpoint<PVRGPUParent>&& aEndpoint) { void VRGPUParent::Bind(Endpoint<PVRGPUParent>&& aEndpoint) {
@ -74,5 +80,9 @@ mozilla::ipc::IPCResult VRGPUParent::RecvStopVRService() {
return IPC_OK(); return IPC_OK();
} }
bool VRGPUParent::IsClosed() {
return mClosed;
}
} // namespace gfx } // namespace gfx
} // namespace mozilla } // namespace mozilla

View file

@ -17,25 +17,28 @@ class VRGPUParent final : public PVRGPUParent {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUParent) NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRGPUParent)
public: public:
explicit VRGPUParent(ProcessId aChildProcessId);
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
static RefPtr<VRGPUParent> CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint); static RefPtr<VRGPUParent> CreateForGPU(Endpoint<PVRGPUParent>&& aEndpoint);
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
bool IsClosed();
protected: protected:
~VRGPUParent() = default;
void Bind(Endpoint<PVRGPUParent>&& aEndpoint); void Bind(Endpoint<PVRGPUParent>&& aEndpoint);
virtual mozilla::ipc::IPCResult RecvStartVRService() override; virtual mozilla::ipc::IPCResult RecvStartVRService() override;
virtual mozilla::ipc::IPCResult RecvStopVRService() override; virtual mozilla::ipc::IPCResult RecvStopVRService() override;
private: private:
explicit VRGPUParent(ProcessId aChildProcessId);
~VRGPUParent();
void DeferredDestroy(); void DeferredDestroy();
RefPtr<VRGPUParent> mSelfRef; RefPtr<VRGPUParent> mSelfRef;
#if !defined(MOZ_WIDGET_ANDROID) #if !defined(MOZ_WIDGET_ANDROID)
RefPtr<VRService> mVRService; RefPtr<VRService> mVRService;
#endif #endif
bool mClosed;
DISALLOW_COPY_AND_ASSIGN(VRGPUParent); DISALLOW_COPY_AND_ASSIGN(VRGPUParent);
}; };

View file

@ -8,6 +8,7 @@
#include "VRGPUParent.h" #include "VRGPUParent.h"
#include "VRManager.h" #include "VRManager.h"
#include "gfxConfig.h" #include "gfxConfig.h"
#include "nsDebugImpl.h"
#include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/gfxVars.h"
#include "mozilla/ipc/ProcessChild.h" #include "mozilla/ipc/ProcessChild.h"
@ -96,6 +97,9 @@ void VRParent::ActorDestroy(ActorDestroyReason aWhy) {
NS_WARNING("Shutting down VR process early due to a crash!"); NS_WARNING("Shutting down VR process early due to a crash!");
ProcessChild::QuickExit(); ProcessChild::QuickExit();
} }
if (!mVRGPUParent->IsClosed()) {
mVRGPUParent->Close();
}
mVRGPUParent = nullptr; mVRGPUParent = nullptr;
#if defined(XP_WIN) #if defined(XP_WIN)
@ -123,6 +127,8 @@ bool VRParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
return false; return false;
} }
nsDebugImpl::SetMultiprocessMode("VR");
// This must be checked before any IPDL message, which may hit sentinel // This must be checked before any IPDL message, which may hit sentinel
// errors due to parent and content processes having different // errors due to parent and content processes having different
// versions. // versions.

View file

@ -26,8 +26,6 @@ VRProcessChild::~VRProcessChild() { sVRParent = nullptr; }
} }
bool VRProcessChild::Init(int aArgc, char* aArgv[]) { bool VRProcessChild::Init(int aArgc, char* aArgv[]) {
BackgroundHangMonitor::Startup();
char* parentBuildID = nullptr; char* parentBuildID = nullptr;
for (int i = 1; i < aArgc; i++) { for (int i = 1; i < aArgc; i++) {
if (!aArgv[i]) { if (!aArgv[i]) {

View file

@ -206,6 +206,10 @@ OculusSession::OculusSession()
OculusSession::~OculusSession() { Shutdown(); } OculusSession::~OculusSession() { Shutdown(); }
bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState) { bool OculusSession::Initialize(mozilla::gfx::VRSystemState& aSystemState) {
if (!gfxPrefs::VREnabled() || !gfxPrefs::VROculusEnabled()) {
return false;
}
if (!CreateD3DObjects()) { if (!CreateD3DObjects()) {
return false; return false;
} }
@ -530,14 +534,18 @@ bool OculusSession::LoadOvrLib() {
nsString libName; nsString libName;
nsString searchPath; nsString searchPath;
static const char dirSep = '\\'; for (;;) {
static const int pathLen = 260; UINT requiredLength = ::GetSystemDirectoryW(
searchPath.SetCapacity(pathLen); char16ptr_t(searchPath.BeginWriting()), searchPath.Length());
int realLen = if (!requiredLength) {
::GetSystemDirectoryW(char16ptr_t(searchPath.BeginWriting()), pathLen); break;
if (realLen != 0 && realLen < pathLen) { }
searchPath.SetLength(realLen); if (requiredLength < searchPath.Length()) {
libSearchPaths.AppendElement(searchPath); searchPath.Truncate(requiredLength);
libSearchPaths.AppendElement(searchPath);
break;
}
searchPath.SetLength(requiredLength);
} }
libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION); libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION);
@ -554,13 +562,17 @@ bool OculusSession::LoadOvrLib() {
libName = _wgetenv(L"OVR_LIB_NAME"); libName = _wgetenv(L"OVR_LIB_NAME");
} }
if (libName.IsEmpty()) {
return false;
}
for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) { for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
nsString& libPath = libSearchPaths[i]; nsString& libPath = libSearchPaths[i];
nsString fullName; nsString fullName;
if (libPath.Length() == 0) { if (libPath.Length() == 0) {
fullName.Assign(libName); fullName.Assign(libName);
} else { } else {
fullName.AppendPrintf("%s%c%s", libPath.get(), dirSep, libName.get()); fullName.Assign(libPath + NS_LITERAL_STRING(u"\\") + libName);
} }
mOvrLib = LoadLibraryWithFlags(fullName.get()); mOvrLib = LoadLibraryWithFlags(fullName.get());

View file

@ -40,7 +40,9 @@ void VRServiceManager::ShutdownVRProcess() {
if (VRGPUChild::IsCreated()) { if (VRGPUChild::IsCreated()) {
VRGPUChild* vrGPUChild = VRGPUChild::Get(); VRGPUChild* vrGPUChild = VRGPUChild::Get();
vrGPUChild->SendStopVRService(); vrGPUChild->SendStopVRService();
vrGPUChild->Close(); if (!vrGPUChild->IsClosed()) {
vrGPUChild->Close();
}
VRGPUChild::Shutdown(); VRGPUChild::Shutdown();
} }
if (gfxPrefs::VRProcessEnabled()) { if (gfxPrefs::VRProcessEnabled()) {

View file

@ -15,7 +15,7 @@ use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
use internal_types::{FastHashMap, SavedTargetIndex, TextureSource}; use internal_types::{FastHashMap, SavedTargetIndex, TextureSource};
use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface};
use prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind}; use prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex};
use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex}; use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex};
use prim_store::image::ImageSource; use prim_store::image::ImageSource;
@ -583,7 +583,7 @@ impl AlphaBatchBuilder {
root_spatial_node_index: SpatialNodeIndex, root_spatial_node_index: SpatialNodeIndex,
z_generator: &mut ZBufferIdGenerator, z_generator: &mut ZBufferIdGenerator,
) { ) {
if prim_instance.bounding_rect.is_none() { if prim_instance.visibility_info == PrimitiveVisibilityIndex::INVALID {
return; return;
} }
@ -601,15 +601,15 @@ impl AlphaBatchBuilder {
// wasteful. We should probably cache this in // wasteful. We should probably cache this in
// the scroll node... // the scroll node...
let transform_kind = transform_id.transform_kind(); let transform_kind = transform_id.transform_kind();
let bounding_rect = prim_instance.bounding_rect let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize];
.as_ref() let bounding_rect = &prim_info.clip_chain.pic_clip_rect;
.expect("bug");
let z_id = z_generator.next(); let z_id = z_generator.next();
// Get the clip task address for the global primitive, if one was set. // Get the clip task address for the global primitive, if one was set.
let clip_task_address = get_clip_task_address( let clip_task_address = get_clip_task_address(
&ctx.scratch.clip_mask_instances, &ctx.scratch.clip_mask_instances,
prim_instance.clip_task_index, prim_info.clip_task_index,
0, 0,
render_tasks, render_tasks,
).unwrap_or(OPAQUE_TASK_ADDRESS); ).unwrap_or(OPAQUE_TASK_ADDRESS);
@ -631,7 +631,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -691,7 +691,7 @@ impl AlphaBatchBuilder {
} }
let non_segmented_blend_mode = if !common_data.opacity.is_opaque || let non_segmented_blend_mode = if !common_data.opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -701,7 +701,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -737,7 +737,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} }
@ -754,7 +754,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -891,7 +891,7 @@ impl AlphaBatchBuilder {
// helper methods, as we port more primitives to make // helper methods, as we port more primitives to make
// use of interning. // use of interning.
let blend_mode = if !common_data.opacity.is_opaque || let blend_mode = if !common_data.opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
BlendMode::PremultipliedAlpha BlendMode::PremultipliedAlpha
@ -901,7 +901,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -943,7 +943,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: picture.local_rect, local_rect: picture.local_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -955,6 +955,8 @@ impl AlphaBatchBuilder {
Picture3DContext::In { root_data: Some(ref list), .. } => { Picture3DContext::In { root_data: Some(ref list), .. } => {
for child in list { for child in list {
let prim_instance = &picture.prim_list.prim_instances[child.anchor]; let prim_instance = &picture.prim_list.prim_instances[child.anchor];
let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize];
let pic_index = match prim_instance.kind { let pic_index = match prim_instance.kind {
PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index,
PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::LineDecoration { .. } |
@ -972,17 +974,18 @@ impl AlphaBatchBuilder {
}; };
let pic = &ctx.prim_store.pictures[pic_index.0]; let pic = &ctx.prim_store.pictures[pic_index.0];
// Get clip task, if set, for the picture primitive. // Get clip task, if set, for the picture primitive.
let clip_task_address = get_clip_task_address( let clip_task_address = get_clip_task_address(
&ctx.scratch.clip_mask_instances, &ctx.scratch.clip_mask_instances,
prim_instance.clip_task_index, prim_info.clip_task_index,
0, 0,
render_tasks, render_tasks,
).unwrap_or(OPAQUE_TASK_ADDRESS); ).unwrap_or(OPAQUE_TASK_ADDRESS);
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: pic.local_rect, local_rect: pic.local_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: GpuCacheAddress::invalid(), specific_prim_address: GpuCacheAddress::invalid(),
clip_task_address, clip_task_address,
@ -1025,7 +1028,7 @@ impl AlphaBatchBuilder {
self.current_batch_list().push_single_instance( self.current_batch_list().push_single_instance(
key, key,
&prim_instance.bounding_rect.as_ref().expect("bug"), &prim_info.clip_chain.pic_clip_rect,
z_id, z_id,
PrimitiveInstanceData::from(instance), PrimitiveInstanceData::from(instance),
); );
@ -1044,7 +1047,7 @@ impl AlphaBatchBuilder {
PictureCompositeMode::TileCache { .. } => { PictureCompositeMode::TileCache { .. } => {
// Construct a local clip rect that ensures we only draw pixels where // Construct a local clip rect that ensures we only draw pixels where
// the local bounds of the picture extend to within the edge tiles. // the local bounds of the picture extend to within the edge tiles.
let local_clip_rect = prim_instance let local_clip_rect = prim_info
.combined_local_clip_rect .combined_local_clip_rect
.intersection(&picture.local_rect) .intersection(&picture.local_rect)
.and_then(|rect| { .and_then(|rect| {
@ -1503,7 +1506,7 @@ impl AlphaBatchBuilder {
let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle); let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle);
let specified_blend_mode = BlendMode::PremultipliedAlpha; let specified_blend_mode = BlendMode::PremultipliedAlpha;
let non_segmented_blend_mode = if !common_data.opacity.is_opaque || let non_segmented_blend_mode = if !common_data.opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -1513,7 +1516,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -1549,7 +1552,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} }
@ -1562,7 +1565,7 @@ impl AlphaBatchBuilder {
let opacity = opacity.combine(prim_data.opacity); let opacity = opacity.combine(prim_data.opacity);
let non_segmented_blend_mode = if !opacity.is_opaque || let non_segmented_blend_mode = if !opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -1587,7 +1590,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -1612,7 +1615,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} }
@ -1676,7 +1679,7 @@ impl AlphaBatchBuilder {
let specified_blend_mode = BlendMode::PremultipliedAlpha; let specified_blend_mode = BlendMode::PremultipliedAlpha;
let non_segmented_blend_mode = if !prim_common_data.opacity.is_opaque || let non_segmented_blend_mode = if !prim_common_data.opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -1695,7 +1698,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -1720,7 +1723,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} }
@ -1769,7 +1772,7 @@ impl AlphaBatchBuilder {
let opacity = opacity.combine(common_data.opacity); let opacity = opacity.combine(common_data.opacity);
let non_segmented_blend_mode = if !opacity.is_opaque || let non_segmented_blend_mode = if !opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -1799,7 +1802,7 @@ impl AlphaBatchBuilder {
let prim_header = PrimitiveHeader { let prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: prim_cache_address, specific_prim_address: prim_cache_address,
clip_task_address, clip_task_address,
@ -1824,7 +1827,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} else { } else {
@ -1869,7 +1872,7 @@ impl AlphaBatchBuilder {
let mut prim_header = PrimitiveHeader { let mut prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: GpuCacheAddress::invalid(), specific_prim_address: GpuCacheAddress::invalid(),
clip_task_address, clip_task_address,
@ -1878,7 +1881,7 @@ impl AlphaBatchBuilder {
if visible_tiles_range.is_empty() { if visible_tiles_range.is_empty() {
let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || let non_segmented_blend_mode = if !prim_data.opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -1923,7 +1926,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} else { } else {
@ -1950,7 +1953,7 @@ impl AlphaBatchBuilder {
let mut prim_header = PrimitiveHeader { let mut prim_header = PrimitiveHeader {
local_rect: prim_rect, local_rect: prim_rect,
local_clip_rect: prim_instance.combined_local_clip_rect, local_clip_rect: prim_info.combined_local_clip_rect,
task_address, task_address,
specific_prim_address: GpuCacheAddress::invalid(), specific_prim_address: GpuCacheAddress::invalid(),
clip_task_address, clip_task_address,
@ -1959,7 +1962,7 @@ impl AlphaBatchBuilder {
if visible_tiles_range.is_empty() { if visible_tiles_range.is_empty() {
let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || let non_segmented_blend_mode = if !prim_data.opacity.is_opaque ||
prim_instance.clip_task_index != ClipTaskIndex::INVALID || prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex transform_kind == TransformedRectKind::Complex
{ {
specified_blend_mode specified_blend_mode
@ -2004,7 +2007,7 @@ impl AlphaBatchBuilder {
transform_kind, transform_kind,
render_tasks, render_tasks,
z_id, z_id,
prim_instance.clip_task_index, prim_info.clip_task_index,
ctx, ctx,
); );
} else { } else {

View file

@ -462,6 +462,21 @@ pub struct ClipChainInstance {
pub pic_clip_rect: PictureRect, pub pic_clip_rect: PictureRect,
} }
impl ClipChainInstance {
pub fn empty() -> Self {
ClipChainInstance {
clips_range: ClipNodeRange {
first: 0,
count: 0,
},
local_clip_rect: LayoutRect::zero(),
has_non_local_clips: false,
needs_mask: false,
pic_clip_rect: PictureRect::zero(),
}
}
}
impl ClipStore { impl ClipStore {
pub fn new() -> Self { pub fn new() -> Self {
ClipStore { ClipStore {

View file

@ -70,6 +70,20 @@ pub struct FrameBuilder {
pub config: FrameBuilderConfig, pub config: FrameBuilderConfig,
} }
pub struct FrameVisibilityContext<'a> {
pub clip_scroll_tree: &'a ClipScrollTree,
pub screen_world_rect: WorldRect,
pub device_pixel_scale: DevicePixelScale,
pub surfaces: &'a [SurfaceInfo],
}
pub struct FrameVisibilityState<'a> {
pub clip_store: &'a mut ClipStore,
pub resource_cache: &'a mut ResourceCache,
pub gpu_cache: &'a mut GpuCache,
pub scratch: &'a mut PrimitiveScratchBuffer,
}
pub struct FrameBuildingContext<'a> { pub struct FrameBuildingContext<'a> {
pub device_pixel_scale: DevicePixelScale, pub device_pixel_scale: DevicePixelScale,
pub scene_properties: &'a SceneProperties, pub scene_properties: &'a SceneProperties,
@ -198,6 +212,16 @@ impl FrameBuilder {
self.prim_store.destroy( self.prim_store.destroy(
retained_tiles, retained_tiles,
); );
// In general, the pending retained tiles are consumed by the frame
// builder the first time a frame is built after a new scene has
// arrived. However, if two scenes arrive in quick succession, the
// frame builder may not have had a chance to build a frame and
// consume the pending tiles. In this case, the pending tiles will
// be lost, causing a full invalidation of the entire screen. To
// avoid this, if there are still pending tiles, include them in
// the retained tiles passed to the next frame builder.
retained_tiles.tiles.extend(self.pending_retained_tiles.tiles);
} }
/// Compute the contribution (bounding rectangles, and resources) of layers and their /// Compute the contribution (bounding rectangles, and resources) of layers and their
@ -295,6 +319,30 @@ impl FrameBuilder {
scratch, scratch,
); );
{
let visibility_context = FrameVisibilityContext {
device_pixel_scale,
clip_scroll_tree,
screen_world_rect,
surfaces: pic_update_state.surfaces,
};
let mut visibility_state = FrameVisibilityState {
resource_cache,
gpu_cache,
clip_store: &mut self.clip_store,
scratch,
};
self.prim_store.update_visibility(
self.root_pic_index,
ROOT_SURFACE_INDEX,
&visibility_context,
&mut visibility_state,
resources,
);
}
let mut frame_state = FrameBuildingState { let mut frame_state = FrameBuildingState {
render_tasks, render_tasks,
profile_counters, profile_counters,
@ -336,7 +384,6 @@ impl FrameBuilder {
prim_list, prim_list,
pic_context, pic_context,
pic_state, pic_state,
&mut frame_state,
); );
let child_tasks = frame_state let child_tasks = frame_state

View file

@ -9,7 +9,7 @@ use api::{PicturePixel, RasterPixel, WorldPixel, WorldRect, ImageFormat, ImageDe
#[cfg(feature = "debug_renderer")] #[cfg(feature = "debug_renderer")]
use api::{DebugFlags, DeviceVector2D}; use api::{DebugFlags, DeviceVector2D};
use box_shadow::{BLUR_SAMPLE_SCALE}; use box_shadow::{BLUR_SAMPLE_SCALE};
use clip::{ClipNodeCollector, ClipStore, ClipChainId, ClipChainNode, ClipItem}; use clip::{ClipStore, ClipChainId, ClipChainNode, ClipItem};
use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, CoordinateSystemId}; use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex, CoordinateSystemId};
#[cfg(feature = "debug_renderer")] #[cfg(feature = "debug_renderer")]
use debug_colors; use debug_colors;
@ -24,7 +24,7 @@ use gpu_types::{TransformPalette, TransformPaletteId, UvRectKind};
use plane_split::{Clipper, Polygon, Splitter}; use plane_split::{Clipper, Polygon, Splitter};
use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind}; use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind};
use prim_store::{get_raster_rects, CoordinateSpaceMapping, PrimitiveScratchBuffer}; use prim_store::{get_raster_rects, CoordinateSpaceMapping, PrimitiveScratchBuffer};
use prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; use prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex, RectangleKey};
use print_tree::PrintTreePrinter; use print_tree::PrintTreePrinter;
use render_backend::FrameResources; use render_backend::FrameResources;
use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit}; use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit};
@ -135,6 +135,9 @@ pub struct Tile {
/// The currently visible rect within this tile, updated per frame. /// The currently visible rect within this tile, updated per frame.
/// If None, this tile is not currently visible. /// If None, this tile is not currently visible.
visible_rect: Option<WorldRect>, visible_rect: Option<WorldRect>,
/// The currently valid rect of the tile, used to invalidate
/// tiles that were only partially drawn.
valid_rect: WorldRect,
/// Uniquely describes the content of this tile, in a way that can be /// Uniquely describes the content of this tile, in a way that can be
/// (reasonably) efficiently hashed and compared. /// (reasonably) efficiently hashed and compared.
descriptor: TileDescriptor, descriptor: TileDescriptor,
@ -155,6 +158,10 @@ pub struct Tile {
/// care about. Stored as a set here, and then collected, sorted /// care about. Stored as a set here, and then collected, sorted
/// and converted to transform key values during post_update. /// and converted to transform key values during post_update.
transforms: FastHashSet<SpatialNodeIndex>, transforms: FastHashSet<SpatialNodeIndex>,
/// A list of potentially important clips. We can't know if
/// they were important or can be discarded until we know the
/// tile cache bounding rect.
potential_clips: FastHashMap<RectangleKey, SpatialNodeIndex>,
} }
impl Tile { impl Tile {
@ -166,12 +173,14 @@ impl Tile {
local_rect: LayoutRect::zero(), local_rect: LayoutRect::zero(),
world_rect: WorldRect::zero(), world_rect: WorldRect::zero(),
visible_rect: None, visible_rect: None,
valid_rect: WorldRect::zero(),
handle: TextureCacheHandle::invalid(), handle: TextureCacheHandle::invalid(),
descriptor: TileDescriptor::new(), descriptor: TileDescriptor::new(),
is_same_content: false, is_same_content: false,
is_valid: false, is_valid: false,
same_frames: 0, same_frames: 0,
transforms: FastHashSet::default(), transforms: FastHashSet::default(),
potential_clips: FastHashMap::default(),
id, id,
} }
} }
@ -180,6 +189,7 @@ impl Tile {
fn clear(&mut self) { fn clear(&mut self) {
self.transforms.clear(); self.transforms.clear();
self.descriptor.clear(); self.descriptor.clear();
self.potential_clips.clear();
} }
/// Update state related to whether a tile has the same /// Update state related to whether a tile has the same
@ -191,9 +201,9 @@ impl Tile {
// The tile is only valid if: // The tile is only valid if:
// - The content is the same *and* // - The content is the same *and*
// - The valid part of the tile is the same wrt to world clips. // - The valid part of the tile includes the needed part.
self.is_valid &= self.is_same_content; self.is_valid &= self.is_same_content;
self.is_valid &= self.descriptor.is_valid(&tile_bounding_rect); self.is_valid &= self.valid_rect.contains_rect(tile_bounding_rect);
// Update count of how many times this tile has had the same content. // Update count of how many times this tile has had the same content.
if !self.is_same_content { if !self.is_same_content {
@ -216,15 +226,6 @@ pub struct PrimitiveDescriptor {
clip_count: u16, clip_count: u16,
} }
/// Defines the region of a primitive that exists on a tile.
#[derive(Debug)]
pub struct PrimitiveRegion {
/// The (prim relative) portion of on this tile.
prim_region: WorldRect,
/// Location within the tile.
tile_offset: WorldPoint,
}
/// Uniquely describes the content of this tile, in a way that can be /// Uniquely describes the content of this tile, in a way that can be
/// (reasonably) efficiently hashed and compared. /// (reasonably) efficiently hashed and compared.
#[derive(Debug)] #[derive(Debug)]
@ -250,12 +251,6 @@ pub struct TileDescriptor {
// TODO(gw): Ugh, get rid of all opacity binding support! // TODO(gw): Ugh, get rid of all opacity binding support!
opacity_bindings: ComparableVec<OpacityBinding>, opacity_bindings: ComparableVec<OpacityBinding>,
/// List of the required valid rectangles for each primitive.
needed_regions: Vec<PrimitiveRegion>,
/// List of the currently valid rectangles for each primitive.
current_regions: Vec<PrimitiveRegion>,
/// List of the (quantized) transforms that we care about /// List of the (quantized) transforms that we care about
/// tracking for this tile. /// tracking for this tile.
transforms: ComparableVec<TransformKey>, transforms: ComparableVec<TransformKey>,
@ -269,8 +264,6 @@ impl TileDescriptor {
clip_vertices: ComparableVec::new(), clip_vertices: ComparableVec::new(),
opacity_bindings: ComparableVec::new(), opacity_bindings: ComparableVec::new(),
image_keys: ComparableVec::new(), image_keys: ComparableVec::new(),
needed_regions: Vec::new(),
current_regions: Vec::new(),
transforms: ComparableVec::new(), transforms: ComparableVec::new(),
} }
} }
@ -283,7 +276,6 @@ impl TileDescriptor {
self.clip_vertices.reset(); self.clip_vertices.reset();
self.opacity_bindings.reset(); self.opacity_bindings.reset();
self.image_keys.reset(); self.image_keys.reset();
self.needed_regions.clear();
self.transforms.reset(); self.transforms.reset();
} }
@ -298,49 +290,6 @@ impl TileDescriptor {
self.prims.is_valid() && self.prims.is_valid() &&
self.transforms.is_valid() self.transforms.is_valid()
} }
/// Check if the tile is valid, given that the rest of the content is the same.
fn is_valid(&self, tile_bounding_rect: &WorldRect) -> bool {
// For a tile to be valid, it needs to ensure that the currently valid
// rect of each primitive encloses the required valid rect.
// TODO(gw): This is only needed for tiles that are partially rendered
// (i.e. those clipped to edge of screen). We can make this much
// faster by skipping this step for tiles that are not clipped!
// TODO(gw): For partial tiles that *do* need this test, we can probably
// make it faster again by caching and checking the relative
// transforms of primitives on this tile.
if self.needed_regions.len() == self.current_regions.len() {
for (needed, current) in self.needed_regions.iter().zip(self.current_regions.iter()) {
let needed_region = needed
.prim_region
.translate(&needed.tile_offset.to_vector())
.intersection(tile_bounding_rect);
let needed_rect = match needed_region {
Some(rect) => rect,
None => continue,
};
let current_region = current
.prim_region
.translate(&current.tile_offset.to_vector())
.intersection(tile_bounding_rect);
let current_rect = match current_region {
Some(rect) => rect,
None => return false,
};
if needed_rect != current_rect {
return false;
}
}
true
} else {
false
}
}
} }
/// Represents the dirty region of a tile cache picture. /// Represents the dirty region of a tile cache picture.
@ -789,6 +738,14 @@ impl TileCache {
let mut current_clip_chain_id = prim_instance.clip_chain_id; let mut current_clip_chain_id = prim_instance.clip_chain_id;
let mut clip_spatial_nodes = FastHashSet::default(); let mut clip_spatial_nodes = FastHashSet::default();
// TODO(gw): We only care about world clip rects that don't have the main
// scroll root as an ancestor. It may be a worthwhile optimization
// to check for these and skip them.
// TODO(gw): We could also trivially track and exclude the root iframe / content
// clip chain id, since we know that will exist on every item but never
// actually be relevant.
let mut world_clips: FastHashMap<RectangleKey, SpatialNodeIndex> = FastHashMap::default();
// Some primitives can not be cached (e.g. external video images) // Some primitives can not be cached (e.g. external video images)
let is_cacheable = prim_instance.is_cacheable( let is_cacheable = prim_instance.is_cacheable(
&resources, &resources,
@ -885,17 +842,27 @@ impl TileCache {
size, size,
); );
if let Some(clip_world_rect) = self.map_local_to_world.map(&local_rect) { match self.map_local_to_world.map(&local_rect) {
// Even if this ends up getting clipped out by the current clip Some(clip_world_rect) => {
// stack, we want to ensure the primitive gets added to the tiles // Even if this ends up getting clipped out by the current clip
// below, to ensure invalidation isn't tripped up by the wrong // stack, we want to ensure the primitive gets added to the tiles
// number of primitives that affect this tile. // below, to ensure invalidation isn't tripped up by the wrong
world_clip_rect = world_clip_rect // number of primitives that affect this tile.
.intersection(&clip_world_rect) world_clip_rect = world_clip_rect
.unwrap_or(WorldRect::zero()); .intersection(&clip_world_rect)
} .unwrap_or(WorldRect::zero());
false world_clips.insert(
clip_world_rect.into(),
clip_chain_node.spatial_node_index,
);
false
}
None => {
true
}
}
} else { } else {
true true
} }
@ -908,9 +875,10 @@ impl TileCache {
} }
}; };
clip_vertices.push(clip_chain_node.local_pos);
clip_chain_uids.push(clip_chain_node.handle.uid());
if add_to_clip_deps { if add_to_clip_deps {
clip_vertices.push(clip_chain_node.local_pos);
clip_chain_uids.push(clip_chain_node.handle.uid());
clip_spatial_nodes.insert(clip_chain_node.spatial_node_index); clip_spatial_nodes.insert(clip_chain_node.spatial_node_index);
} }
@ -939,17 +907,6 @@ impl TileCache {
// a partially clipped tile, which would be a significant // a partially clipped tile, which would be a significant
// optimization for the common case (non-clipped tiles). // optimization for the common case (non-clipped tiles).
// Get the required tile-local rect that this primitive occupies.
// Ensure that even if it's currently clipped out of this tile,
// we still insert a rect of zero size, so that the tile descriptor's
// needed rects array matches.
let prim_region = world_clip_rect.translate(&-world_rect.origin.to_vector());
tile.descriptor.needed_regions.push(PrimitiveRegion {
prim_region,
tile_offset: world_rect.origin - tile.world_rect.origin.to_vector(),
});
// Mark if the tile is cacheable at all. // Mark if the tile is cacheable at all.
tile.is_same_content &= is_cacheable; tile.is_same_content &= is_cacheable;
@ -973,6 +930,9 @@ impl TileCache {
for spatial_node_index in &clip_spatial_nodes { for spatial_node_index in &clip_spatial_nodes {
tile.transforms.insert(*spatial_node_index); tile.transforms.insert(*spatial_node_index);
} }
for (world_rect, spatial_node_index) in &world_clips {
tile.potential_clips.insert(world_rect.clone(), *spatial_node_index);
}
} }
} }
} }
@ -1018,6 +978,17 @@ impl TileCache {
// Step through each tile and invalidate if the dependencies have changed. // Step through each tile and invalidate if the dependencies have changed.
for (i, tile) in self.tiles.iter_mut().enumerate() { for (i, tile) in self.tiles.iter_mut().enumerate() {
// Deal with any potential world clips. Check to see if they are
// outside the tile cache bounding rect. If they are, they're not
// relevant and we don't care if they move relative to the content
// itself. This avoids a lot of redundant invalidations.
for (clip_world_rect, spatial_node_index) in &tile.potential_clips {
let clip_world_rect = WorldRect::from(clip_world_rect.clone());
if !clip_world_rect.contains_rect(&self.world_bounding_rect) {
tile.transforms.insert(*spatial_node_index);
}
}
// Update tile transforms // Update tile transforms
let mut transform_spatial_nodes: Vec<SpatialNodeIndex> = tile.transforms.drain().collect(); let mut transform_spatial_nodes: Vec<SpatialNodeIndex> = tile.transforms.drain().collect();
transform_spatial_nodes.sort(); transform_spatial_nodes.sort();
@ -1117,6 +1088,11 @@ impl TileCache {
let src_origin = (visible_rect.origin * frame_context.device_pixel_scale).round().to_i32(); let src_origin = (visible_rect.origin * frame_context.device_pixel_scale).round().to_i32();
let valid_rect = visible_rect.translate(&-tile.world_rect.origin.to_vector()); let valid_rect = visible_rect.translate(&-tile.world_rect.origin.to_vector());
tile.valid_rect = visible_rect
.intersection(&self.world_bounding_rect)
.map(|rect| rect.translate(&-tile.world_rect.origin.to_vector()))
.unwrap_or(WorldRect::zero());
// Store a blit operation to be done after drawing the // Store a blit operation to be done after drawing the
// frame in order to update the cached texture tile. // frame in order to update the cached texture tile.
let dest_rect = (valid_rect * frame_context.device_pixel_scale).round().to_i32(); let dest_rect = (valid_rect * frame_context.device_pixel_scale).round().to_i32();
@ -1129,10 +1105,6 @@ impl TileCache {
// We can consider this tile valid now. // We can consider this tile valid now.
tile.is_valid = true; tile.is_valid = true;
tile.descriptor.current_regions = mem::replace(
&mut tile.descriptor.needed_regions,
Vec::new(),
);
} }
} }
} }
@ -1677,7 +1649,7 @@ impl PicturePrimitive {
} }
} }
fn is_visible(&self) -> bool { pub fn is_visible(&self) -> bool {
match self.requested_composite_mode { match self.requested_composite_mode {
Some(PictureCompositeMode::Filter(ref filter)) => { Some(PictureCompositeMode::Filter(ref filter)) => {
filter.is_visible() filter.is_visible()
@ -1814,10 +1786,6 @@ impl PicturePrimitive {
} }
}; };
if self.raster_config.as_ref().map_or(false, |c| c.establishes_raster_root) {
frame_state.clip_store.push_raster_root(surface_spatial_node_index);
}
let map_pic_to_world = SpaceMapper::new_with_target( let map_pic_to_world = SpaceMapper::new_with_target(
ROOT_SPATIAL_NODE_INDEX, ROOT_SPATIAL_NODE_INDEX,
surface_spatial_node_index, surface_spatial_node_index,
@ -1902,16 +1870,9 @@ impl PicturePrimitive {
prim_list: PrimitiveList, prim_list: PrimitiveList,
context: PictureContext, context: PictureContext,
state: PictureState, state: PictureState,
frame_state: &mut FrameBuildingState, ) {
) -> Option<ClipNodeCollector> {
self.prim_list = prim_list; self.prim_list = prim_list;
self.state = Some((state, context)); self.state = Some((state, context));
if self.raster_config.as_ref().map_or(false, |c| c.establishes_raster_root) {
Some(frame_state.clip_store.pop_raster_root())
} else {
None
}
} }
pub fn take_state_and_context(&mut self) -> (PictureState, PictureContext) { pub fn take_state_and_context(&mut self) -> (PictureState, PictureContext) {
@ -1926,6 +1887,7 @@ impl PicturePrimitive {
transforms: &TransformPalette, transforms: &TransformPalette,
prim_instance: &PrimitiveInstance, prim_instance: &PrimitiveInstance,
original_local_rect: LayoutRect, original_local_rect: LayoutRect,
combined_local_clip_rect: &LayoutRect,
world_rect: WorldRect, world_rect: WorldRect,
plane_split_anchor: usize, plane_split_anchor: usize,
) -> bool { ) -> bool {
@ -1940,7 +1902,7 @@ impl PicturePrimitive {
// since we determine the UVs by doing a bilerp with a factor // since we determine the UVs by doing a bilerp with a factor
// from the original local rect. // from the original local rect.
let local_rect = match original_local_rect let local_rect = match original_local_rect
.intersection(&prim_instance.combined_local_clip_rect) .intersection(combined_local_clip_rect)
{ {
Some(rect) => rect.cast(), Some(rect) => rect.cast(),
None => return false, None => return false,
@ -2298,7 +2260,6 @@ impl PicturePrimitive {
&mut self, &mut self,
pic_index: PictureIndex, pic_index: PictureIndex,
prim_instance: &PrimitiveInstance, prim_instance: &PrimitiveInstance,
prim_local_rect: &LayoutRect,
clipped_prim_bounding_rect: WorldRect, clipped_prim_bounding_rect: WorldRect,
surface_index: SurfaceIndex, surface_index: SurfaceIndex,
frame_context: &FrameBuildingContext, frame_context: &FrameBuildingContext,
@ -2334,7 +2295,7 @@ impl PicturePrimitive {
frame_context.clip_scroll_tree, frame_context.clip_scroll_tree,
); );
let pic_rect = PictureRect::from_untyped(&prim_local_rect.to_untyped()); let pic_rect = PictureRect::from_untyped(&self.local_rect.to_untyped());
let (clipped, unclipped) = match get_raster_rects( let (clipped, unclipped) = match get_raster_rects(
pic_rect, pic_rect,
@ -2568,14 +2529,14 @@ impl PicturePrimitive {
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs) // Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
// [brush specific data] // [brush specific data]
// [segment_rect, segment data] // [segment_rect, segment data]
let shadow_rect = prim_local_rect.translate(&offset); let shadow_rect = self.local_rect.translate(&offset);
// ImageBrush colors // ImageBrush colors
request.push(color.premultiplied()); request.push(color.premultiplied());
request.push(PremultipliedColorF::WHITE); request.push(PremultipliedColorF::WHITE);
request.push([ request.push([
prim_local_rect.size.width, self.local_rect.size.width,
prim_local_rect.size.height, self.local_rect.size.height,
0.0, 0.0,
0.0, 0.0,
]); ]);

File diff suppressed because it is too large Load diff

View file

@ -3804,7 +3804,7 @@ static inline bool ArrayConstructorImpl(JSContext* cx, CallArgs& args,
bool isConstructor) { bool isConstructor) {
RootedObject proto(cx); RootedObject proto(cx);
if (isConstructor) { if (isConstructor) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Array, &proto)) {
return false; return false;
} }
} else { } else {

View file

@ -105,7 +105,8 @@ static bool Boolean(JSContext* cx, unsigned argc, Value* vp) {
if (args.isConstructing()) { if (args.isConstructing()) {
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Boolean,
&proto)) {
return false; return false;
} }

View file

@ -137,7 +137,7 @@ bool DataViewObject::constructSameCompartment(JSContext* cx,
} }
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_DataView, &proto)) {
return false; return false;
} }
@ -186,7 +186,7 @@ bool DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj,
// Make sure to get the [[Prototype]] for the created view from this // Make sure to get the [[Prototype]] for the created view from this
// compartment. // compartment.
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_DataView, &proto)) {
return false; return false;
} }

View file

@ -651,7 +651,7 @@ bool MapObject::construct(JSContext* cx, unsigned argc, Value* vp) {
} }
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Map, &proto)) {
return false; return false;
} }
@ -1270,7 +1270,7 @@ bool SetObject::construct(JSContext* cx, unsigned argc, Value* vp) {
} }
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Set, &proto)) {
return false; return false;
} }

View file

@ -2100,7 +2100,8 @@ static bool PromiseConstructor(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
} else { } else {
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Promise,
&proto)) {
return false; return false;
} }
} }

View file

@ -100,6 +100,9 @@ class PromiseObject : public NativeObject {
static JSObject* unforgeableReject(JSContext* cx, HandleValue value); static JSObject* unforgeableReject(JSContext* cx, HandleValue value);
int32_t flags() { return getFixedSlot(PromiseSlot_Flags).toInt32(); } int32_t flags() { return getFixedSlot(PromiseSlot_Flags).toInt32(); }
void setHandled() {
setFixedSlot(PromiseSlot_Flags, Int32Value(flags() | PROMISE_FLAG_HANDLED));
}
JS::PromiseState state() { JS::PromiseState state() {
int32_t flags = this->flags(); int32_t flags = this->flags();
if (!(flags & PROMISE_FLAG_RESOLVED)) { if (!(flags & PROMISE_FLAG_RESOLVED)) {

View file

@ -483,7 +483,7 @@ bool js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) {
// Step 7. // Step 7.
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_RegExp, &proto)) {
return false; return false;
} }
@ -559,7 +559,7 @@ bool js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) {
// Step 7. // Step 7.
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_RegExp, &proto)) {
return false; return false;
} }

File diff suppressed because it is too large Load diff

View file

@ -313,9 +313,8 @@ class ReadableStreamController : public StreamController {
underlyingSource().toPrivate()); underlyingSource().toPrivate());
} }
void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) { void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) {
MOZ_ASSERT(getFixedSlot(Slot_Flags).isUndefined());
setUnderlyingSource(JS::PrivateValue(underlyingSource)); setUnderlyingSource(JS::PrivateValue(underlyingSource));
setFlags(Flag_ExternalSource); addFlags(Flag_ExternalSource);
} }
double strategyHWM() const { double strategyHWM() const {
return getFixedSlot(Slot_StrategyHWM).toNumber(); return getFixedSlot(Slot_StrategyHWM).toNumber();

View file

@ -3426,7 +3426,7 @@ bool js::StringConstructor(JSContext* cx, unsigned argc, Value* vp) {
if (args.isConstructing()) { if (args.isConstructing()) {
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_String, &proto)) {
return false; return false;
} }

View file

@ -227,7 +227,7 @@ JS_PUBLIC_API bool JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj,
} }
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WeakMap, &proto)) {
return false; return false;
} }

View file

@ -172,7 +172,7 @@ bool WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp) {
} }
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WeakSet, &proto)) {
return false; return false;
} }

View file

@ -81,7 +81,7 @@ static bool Collator(JSContext* cx, const CallArgs& args) {
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor). // Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
return false; return false;
} }

View file

@ -92,7 +92,7 @@ static bool DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct,
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
return false; return false;
} }

View file

@ -91,7 +91,7 @@ static bool NumberFormat(JSContext* cx, const CallArgs& args, bool construct) {
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
return false; return false;
} }

View file

@ -77,7 +77,7 @@ static bool PluralRules(JSContext* cx, unsigned argc, Value* vp) {
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
return false; return false;
} }

View file

@ -83,7 +83,7 @@ static bool RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp) {
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor). // Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
return false; return false;
} }

View file

@ -120,15 +120,14 @@ class UnwinderTypeCache(object):
class_type = gdb.lookup_type('js::jit::' + SizeOfFramePrefix[name]) class_type = gdb.lookup_type('js::jit::' + SizeOfFramePrefix[name])
self.frame_class_types[enumval] = class_type.pointer() self.frame_class_types[enumval] = class_type.pointer()
# gdb doesn't have a direct way to tell us if a given address is
# claimed by some shared library or the executable. See
# https://sourceware.org/bugzilla/show_bug.cgi?id=19288
# In the interest of not requiring a patched gdb, instead we read
# /proc/.../maps. This only works locally, but maybe could work
# remotely using "remote get". FIXME.
def parse_proc_maps(): def parse_proc_maps():
# gdb doesn't have a direct way to tell us if a given address is
# claimed by some shared library or the executable. See
# https://sourceware.org/bugzilla/show_bug.cgi?id=19288
# In the interest of not requiring a patched gdb, instead we read
# /proc/.../maps. This only works locally, but maybe could work
# remotely using "remote get". FIXME.
mapfile = '/proc/' + str(gdb.selected_inferior().pid) + '/maps' mapfile = '/proc/' + str(gdb.selected_inferior().pid) + '/maps'
# Note we only examine executable mappings here. # Note we only examine executable mappings here.
matcher = re.compile("^([a-fA-F0-9]+)-([a-fA-F0-9]+)\s+..x.\s+\S+\s+\S+\s+\S*(.*)$") matcher = re.compile("^([a-fA-F0-9]+)-([a-fA-F0-9]+)\s+..x.\s+\S+\s+\S+\s+\S*(.*)$")
@ -148,10 +147,10 @@ def parse_proc_maps():
mappings.append((long(start, 16), long(end, 16))) mappings.append((long(start, 16), long(end, 16)))
return mappings return mappings
# A symbol/value pair as expected from gdb frame decorators.
class FrameSymbol(object): class FrameSymbol(object):
"A symbol/value pair as expected from gdb frame decorators."
def __init__(self, sym, val): def __init__(self, sym, val):
self.sym = sym self.sym = sym
self.val = val self.val = val
@ -162,12 +161,12 @@ class FrameSymbol(object):
def value(self): def value(self):
return self.val return self.val
# This represents a single JIT frame for the purposes of display.
# That is, the frame filter creates instances of this when it sees a
# JIT frame in the stack.
class JitFrameDecorator(FrameDecorator): class JitFrameDecorator(FrameDecorator):
"""This represents a single JIT frame for the purposes of display.
That is, the frame filter creates instances of this when it sees a
JIT frame in the stack."""
def __init__(self, base, info, cache): def __init__(self, base, info, cache):
super(JitFrameDecorator, self).__init__(base) super(JitFrameDecorator, self).__init__(base)
self.info = info self.info = info
@ -258,10 +257,10 @@ class JitFrameDecorator(FrameDecorator):
result.append(FrameSymbol(name, args_ptr[i])) result.append(FrameSymbol(name, args_ptr[i]))
return result return result
# A frame filter for SpiderMonkey.
class SpiderMonkeyFrameFilter(object): class SpiderMonkeyFrameFilter(object):
"A frame filter for SpiderMonkey."
# |state_holder| is either None, or an instance of # |state_holder| is either None, or an instance of
# SpiderMonkeyUnwinder. If the latter, then this class will # SpiderMonkeyUnwinder. If the latter, then this class will
# reference the |unwinder_state| attribute to find the current # reference the |unwinder_state| attribute to find the current
@ -285,31 +284,31 @@ class SpiderMonkeyFrameFilter(object):
def filter(self, frame_iter): def filter(self, frame_iter):
return imap(self.maybe_wrap_frame, frame_iter) return imap(self.maybe_wrap_frame, frame_iter)
# A frame id class, as specified by the gdb unwinder API.
class SpiderMonkeyFrameId(object): class SpiderMonkeyFrameId(object):
"A frame id class, as specified by the gdb unwinder API."
def __init__(self, sp, pc): def __init__(self, sp, pc):
self.sp = sp self.sp = sp
self.pc = pc self.pc = pc
# This holds all the state needed during a given unwind. Each time a
# new unwind is done, a new instance of this class is created. It
# keeps track of all the state needed to unwind JIT frames. Note that
# this class is not directly instantiated.
#
# This is a base class, and must be specialized for each target
# architecture, both because we need to use arch-specific register
# names, and because entry frame unwinding is arch-specific.
# See https://sourceware.org/bugzilla/show_bug.cgi?id=19286 for info
# about the register name issue.
#
# Each subclass must define SP_REGISTER, PC_REGISTER, and
# SENTINEL_REGISTER (see x64UnwinderState for info); and implement
# unwind_entry_frame_registers.
class UnwinderState(object): class UnwinderState(object):
"""This holds all the state needed during a given unwind. Each time a
new unwind is done, a new instance of this class is created. It
keeps track of all the state needed to unwind JIT frames. Note that
this class is not directly instantiated.
This is a base class, and must be specialized for each target
architecture, both because we need to use arch-specific register
names, and because entry frame unwinding is arch-specific.
See https://sourceware.org/bugzilla/show_bug.cgi?id=19286 for info
about the register name issue.
Each subclass must define SP_REGISTER, PC_REGISTER, and
SENTINEL_REGISTER (see x64UnwinderState for info); and implement
unwind_entry_frame_registers."""
def __init__(self, typecache): def __init__(self, typecache):
self.next_sp = None self.next_sp = None
self.next_type = None self.next_type = None
@ -505,10 +504,10 @@ class UnwinderState(object):
# the time being. # the time being.
return self.unwind_exit_frame(pc, pending_frame) return self.unwind_exit_frame(pc, pending_frame)
# The UnwinderState subclass for x86-64.
class x64UnwinderState(UnwinderState): class x64UnwinderState(UnwinderState):
"The UnwinderState subclass for x86-64."
SP_REGISTER = 'rsp' SP_REGISTER = 'rsp'
PC_REGISTER = 'rip' PC_REGISTER = 'rip'
@ -534,12 +533,12 @@ class x64UnwinderState(UnwinderState):
if reg is "rbp": if reg is "rbp":
unwind_info.add_saved_register(self.SP_REGISTER, sp) unwind_info.add_saved_register(self.SP_REGISTER, sp)
# The unwinder object. This provides the "user interface" to the JIT
# unwinder, and also handles constructing or destroying UnwinderState
# objects as needed.
class SpiderMonkeyUnwinder(Unwinder): class SpiderMonkeyUnwinder(Unwinder):
"""The unwinder object. This provides the "user interface" to the JIT
unwinder, and also handles constructing or destroying UnwinderState
objects as needed."""
# A list of all the possible unwinders. See |self.make_unwinder|. # A list of all the possible unwinders. See |self.make_unwinder|.
UNWINDERS = [x64UnwinderState] UNWINDERS = [x64UnwinderState]
@ -601,11 +600,11 @@ class SpiderMonkeyUnwinder(Unwinder):
def invalidate_unwinder_state(self, *args, **kwargs): def invalidate_unwinder_state(self, *args, **kwargs):
self.unwinder_state = None self.unwinder_state = None
# Register the unwinder and frame filter with |objfile|. If |objfile|
# is None, register them globally.
def register_unwinder(objfile): def register_unwinder(objfile):
"""Register the unwinder and frame filter with |objfile|. If |objfile|
is None, register them globally."""
type_cache = UnwinderTypeCache() type_cache = UnwinderTypeCache()
unwinder = None unwinder = None
# This currently only works on Linux, due to parse_proc_maps. # This currently only works on Linux, due to parse_proc_maps.

View file

@ -0,0 +1,33 @@
// Releasing a reader should not result in a promise being tracked as
// unhandled.
function test(readable) {
// Create an errored stream.
let controller;
let stream = new ReadableStream({
start(c) {
controller = c;
}
});
drainJobQueue();
// Track promises.
let status = new Map;
setPromiseRejectionTrackerCallback((p, x) => { status.set(p, x); });
// Per Streams spec 3.7.5 step 5, this creates a rejected promise
// (reader.closed) but marks it as handled.
let reader = stream.getReader();
if (!readable) {
controller.close();
}
reader.releaseLock();
// Check that the promise's status is not 0 (unhandled);
// it may be either 1 (handled) or undefined (never tracked).
let result = status.get(reader.closed);
assertEq(result === 1 || result === undefined, true);
}
test(true);
test(false);

View file

@ -0,0 +1,23 @@
// Creating a reader from an errored stream should not result in a promise
// being tracked as unhandled.
// Create an errored stream.
let stream = new ReadableStream({
start(controller) {
controller.error(new Error("splines insufficiently reticulated"));
}
});
drainJobQueue();
// Track promises.
let status = new Map;
setPromiseRejectionTrackerCallback((p, x) => { status.set(p, x); });
// Per Streams spec 3.7.4 step 5.c, this creates a rejected promise
// (reader.closed) but marks it as handled.
let reader = stream.getReader();
// Check that the promise's status is not 0 (unhandled);
// it may be either 1 (handled) or undefined (never tracked).
let result = status.get(reader.closed);
assertEq(result === 1 || result === undefined, true);

View file

@ -3136,7 +3136,7 @@ static bool NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t) {
MOZ_ASSERT(args.isConstructing()); MOZ_ASSERT(args.isConstructing());
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Date, &proto)) {
return false; return false;
} }

View file

@ -388,13 +388,23 @@ JS_PUBLIC_API uint64_t JS::ExceptionTimeWarpTarget(JS::HandleValue value) {
bool Error(JSContext* cx, unsigned argc, Value* vp) { bool Error(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
// ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
// called as functions, without operator new. But as we do not give
// each constructor a distinct JSClass, we must get the exception type
// ourselves.
JSExnType exnType =
JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
JSProtoKey protoKey =
JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
// ES6 19.5.1.1 mandates the .prototype lookup happens before the toString // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
return false; return false;
} }
/* Compute the error message, if any. */ // Compute the error message, if any.
RootedString message(cx, nullptr); RootedString message(cx, nullptr);
if (args.hasDefined(0)) { if (args.hasDefined(0)) {
message = ToString<CanGC>(cx, args[0]); message = ToString<CanGC>(cx, args[0]);
@ -403,10 +413,9 @@ bool Error(JSContext* cx, unsigned argc, Value* vp) {
} }
} }
/* Find the scripted caller, but only ones we're allowed to know about. */ // Find the scripted caller, but only ones we're allowed to know about.
NonBuiltinFrameIter iter(cx, cx->realm()->principals()); NonBuiltinFrameIter iter(cx, cx->realm()->principals());
/* Set the 'fileName' property. */
RootedString fileName(cx); RootedString fileName(cx);
if (args.length() > 1) { if (args.length() > 1) {
fileName = ToString<CanGC>(cx, args[1]); fileName = ToString<CanGC>(cx, args[1]);
@ -422,7 +431,6 @@ bool Error(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
/* Set the 'lineNumber' property. */
uint32_t lineNumber, columnNumber = 0; uint32_t lineNumber, columnNumber = 0;
if (args.length() > 2) { if (args.length() > 2) {
if (!ToUint32(cx, args[2], &lineNumber)) { if (!ToUint32(cx, args[2], &lineNumber)) {
@ -438,15 +446,6 @@ bool Error(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
/*
* ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
* called as functions, without operator new. But as we do not give
* each constructor a distinct JSClass, we must get the exception type
* ourselves.
*/
JSExnType exnType =
JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
RootedObject obj(cx, RootedObject obj(cx,
ErrorObject::create(cx, exnType, stack, fileName, lineNumber, ErrorObject::create(cx, exnType, stack, fileName, lineNumber,
columnNumber, nullptr, message, proto)); columnNumber, nullptr, message, proto));

View file

@ -563,7 +563,7 @@ static bool Number(JSContext* cx, unsigned argc, Value* vp) {
} }
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Number, &proto)) {
return false; return false;
} }

View file

@ -266,12 +266,12 @@ skip script test262/built-ins/Promise/resolve-function-name.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=944846 # https://bugzilla.mozilla.org/show_bug.cgi?id=944846
skip script test262/built-ins/Number/prototype/toExponential/return-values.js skip script test262/built-ins/Number/prototype/toExponential/return-values.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1288457
skip script test262/built-ins/Function/internals/Construct/base-ctor-revoked-proxy-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1225839 # https://bugzilla.mozilla.org/show_bug.cgi?id=1225839
skip script test262/built-ins/Function/internals/Call/class-ctor-realm.js skip script test262/built-ins/Function/internals/Call/class-ctor-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1288457
skip script test262/built-ins/Function/internals/Construct/base-ctor-revoked-proxy-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1297179 # https://bugzilla.mozilla.org/show_bug.cgi?id=1297179
skip script test262/built-ins/Proxy/apply/arguments-realm.js skip script test262/built-ins/Proxy/apply/arguments-realm.js
skip script test262/built-ins/Proxy/apply/trap-is-not-callable-realm.js skip script test262/built-ins/Proxy/apply/trap-is-not-callable-realm.js
@ -298,51 +298,15 @@ skip script test262/built-ins/Proxy/preventExtensions/trap-is-not-callable-realm
skip script test262/built-ins/Proxy/set/trap-is-not-callable-realm.js skip script test262/built-ins/Proxy/set/trap-is-not-callable-realm.js
skip script test262/built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js skip script test262/built-ins/Proxy/setPrototypeOf/trap-is-not-callable-realm.js
# Erros thrown from wrong realm, similar to 1225839, 1288457, and 1297179. # Errors thrown from wrong realm, similar to 1225839, 1288457, and 1297179.
skip script test262/built-ins/Array/length/define-own-prop-length-overflow-realm.js skip script test262/built-ins/Array/length/define-own-prop-length-overflow-realm.js
skip script test262/built-ins/Function/internals/Construct/derived-return-val-realm.js skip script test262/built-ins/Function/internals/Construct/derived-return-val-realm.js
skip script test262/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js skip script test262/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1317416 # https://bugzilla.mozilla.org/show_bug.cgi?id=1317416
skip script test262/language/expressions/super/realm.js skip script test262/language/expressions/super/realm.js
skip script test262/built-ins/Array/proto-from-ctor-realm.js
skip script test262/built-ins/ArrayBuffer/proto-from-ctor-realm.js
skip script test262/built-ins/Boolean/proto-from-ctor-realm.js
skip script test262/built-ins/DataView/proto-from-ctor-realm.js
skip script test262/built-ins/DataView/proto-from-ctor-realm-sab.js
skip script test262/built-ins/Date/proto-from-ctor-realm-one.js
skip script test262/built-ins/Date/proto-from-ctor-realm-two.js
skip script test262/built-ins/Date/proto-from-ctor-realm-zero.js
skip script test262/built-ins/Error/proto-from-ctor-realm.js
skip script test262/built-ins/Function/prototype/bind/proto-from-ctor-realm.js
skip script test262/built-ins/Function/proto-from-ctor-realm.js
skip script test262/built-ins/GeneratorFunction/proto-from-ctor-realm.js skip script test262/built-ins/GeneratorFunction/proto-from-ctor-realm.js
skip script test262/built-ins/Map/proto-from-ctor-realm.js skip script test262/built-ins/Function/prototype/bind/proto-from-ctor-realm.js
skip script test262/built-ins/Number/proto-from-ctor-realm.js
skip script test262/built-ins/Object/proto-from-ctor.js
skip script test262/built-ins/Promise/proto-from-ctor-realm.js
skip script test262/built-ins/RegExp/proto-from-ctor-realm.js
skip script test262/built-ins/Set/proto-from-ctor-realm.js
skip script test262/built-ins/SharedArrayBuffer/proto-from-ctor-realm.js
skip script test262/built-ins/String/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/buffer-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/buffer-arg/proto-from-ctor-realm-sab.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/length-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/no-args/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/object-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/typedarray-arg/other-ctor-buffer-ctor-custom-species-proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/typedarray-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors-bigint/typedarray-arg/same-ctor-buffer-ctor-species-custom-proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/buffer-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/buffer-arg/proto-from-ctor-realm-sab.js
skip script test262/built-ins/TypedArrayConstructors/ctors/length-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/no-args/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/object-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/typedarray-arg/other-ctor-buffer-ctor-custom-species-proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/typedarray-arg/proto-from-ctor-realm.js
skip script test262/built-ins/TypedArrayConstructors/ctors/typedarray-arg/same-ctor-buffer-ctor-species-custom-proto-from-ctor-realm.js
skip script test262/built-ins/WeakMap/proto-from-ctor-realm.js
skip script test262/built-ins/WeakSet/proto-from-ctor-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1317395 # https://bugzilla.mozilla.org/show_bug.cgi?id=1317395
skip script test262/built-ins/ArrayBuffer/prototype/byteLength/detached-buffer.js skip script test262/built-ins/ArrayBuffer/prototype/byteLength/detached-buffer.js

View file

@ -155,6 +155,12 @@ def parse_args():
help='Get tests from the given file.') help='Get tests from the given file.')
input_og.add_option('-x', '--exclude-file', action='append', input_og.add_option('-x', '--exclude-file', action='append',
help='Exclude tests from the given file.') help='Exclude tests from the given file.')
input_og.add_option('--wpt', dest='wpt',
type='choice',
choices=['enabled', 'disabled', 'if-running-everything'],
default='if-running-everything',
help="Enable or disable shell web-platform-tests "
"(default: enable if no test paths are specified).")
input_og.add_option('--include', action='append', dest='requested_paths', default=[], input_og.add_option('--include', action='append', dest='requested_paths', default=[],
help='Include the given test file or directory.') help='Include the given test file or directory.')
input_og.add_option('--exclude', action='append', dest='excluded_paths', default=[], input_og.add_option('--exclude', action='append', dest='excluded_paths', default=[],
@ -434,7 +440,11 @@ def load_tests(options, requested_paths, excluded_paths):
test_gen = manifest.load_reftests(test_dir, path_options, xul_tester) test_gen = manifest.load_reftests(test_dir, path_options, xul_tester)
# WPT tests are already run in the browser in their own harness. # WPT tests are already run in the browser in their own harness.
if not options.make_manifests: wpt_enabled = (options.wpt == 'enabled' or
(options.wpt == 'if-running-everything' and
len(requested_paths) == 0 and
not options.make_manifests))
if wpt_enabled:
wpt_tests = load_wpt_tests(xul_tester, wpt_tests = load_wpt_tests(xul_tester,
requested_paths, requested_paths,
excluded_paths) excluded_paths)

View file

@ -419,7 +419,8 @@ bool ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc,
// Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer). // Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer).
// 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor). // 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ArrayBuffer,
&proto)) {
return false; return false;
} }

View file

@ -121,12 +121,12 @@ class GlobalObject : public NativeObject {
"global object slot counts are inconsistent"); "global object slot counts are inconsistent");
static unsigned constructorSlot(JSProtoKey key) { static unsigned constructorSlot(JSProtoKey key) {
MOZ_ASSERT(key <= JSProto_LIMIT); MOZ_ASSERT(key < JSProto_LIMIT);
return APPLICATION_SLOTS + key; return APPLICATION_SLOTS + key;
} }
static unsigned prototypeSlot(JSProtoKey key) { static unsigned prototypeSlot(JSProtoKey key) {
MOZ_ASSERT(key <= JSProto_LIMIT); MOZ_ASSERT(key < JSProto_LIMIT);
return APPLICATION_SLOTS + JSProto_LIMIT + key; return APPLICATION_SLOTS + JSProto_LIMIT + key;
} }

View file

@ -357,7 +357,7 @@ class MOZ_STACK_CLASS TryNoteIter {
} while (!(pcInRange() && tn_->kind == JSTRY_FOR_OF)); } while (!(pcInRange() && tn_->kind == JSTRY_FOR_OF));
// Advance to trynote following the enclosing for-of. // Advance to trynote following the enclosing for-of.
++tn_; continue;
} }
/* /*

View file

@ -1999,6 +1999,7 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
return false; return false;
} }
JSProtoKey protoKey = JSProto_Null;
if (isAsync) { if (isAsync) {
if (isGenerator) { if (isGenerator) {
if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf,
@ -2022,12 +2023,13 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
parameterListEnd)) { parameterListEnd)) {
return false; return false;
} }
protoKey = JSProto_Function;
} }
} }
// Steps 6, 29. // Steps 6, 29.
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
return false; return false;
} }

View file

@ -1026,7 +1026,8 @@ bool js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) {
JSObject* js::CreateThis(JSContext* cx, const Class* newclasp, JSObject* js::CreateThis(JSContext* cx, const Class* newclasp,
HandleObject callee) { HandleObject callee) {
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, callee, &proto)) { if (!GetPrototypeFromConstructor(
cx, callee, JSCLASS_CACHED_PROTO_KEY(newclasp), &proto)) {
return nullptr; return nullptr;
} }
gc::AllocKind kind = NewObjectGCKind(newclasp); gc::AllocKind kind = NewObjectGCKind(newclasp);
@ -1158,12 +1159,45 @@ JSObject* js::CreateThisForFunctionWithProto(
} }
bool js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, bool js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget,
JSProtoKey intrinsicDefaultProto,
MutableHandleObject proto) { MutableHandleObject proto) {
RootedValue protov(cx); RootedValue protov(cx);
if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) { if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) {
return false; return false;
} }
proto.set(protov.isObject() ? &protov.toObject() : nullptr); if (protov.isObject()) {
proto.set(&protov.toObject());
} else if (newTarget->is<JSFunction>() &&
newTarget->as<JSFunction>().realm() == cx->realm()) {
// Steps 4.a-b fetch the builtin prototype of the current realm, which we
// represent as nullptr.
proto.set(nullptr);
} else if (intrinsicDefaultProto == JSProto_Null) {
// Bug 1317416. The caller did not pass a reasonable JSProtoKey, so let the
// caller select a prototype object. Most likely they will choose one from
// the wrong realm.
proto.set(nullptr);
} else {
// Step 4.a: Let realm be ? GetFunctionRealm(constructor);
JSObject* unwrappedConstructor = CheckedUnwrap(newTarget);
if (!unwrappedConstructor) {
ReportAccessDenied(cx);
return false;
}
// Step 4.b: Set proto to realm's intrinsic object named
// intrinsicDefaultProto.
{
AutoRealm ar(cx, unwrappedConstructor);
proto.set(GlobalObject::getOrCreatePrototype(cx, intrinsicDefaultProto));
}
if (!proto) {
return false;
}
if (!cx->compartment()->wrap(cx, proto)) {
return false;
}
}
return true; return true;
} }
@ -1171,7 +1205,7 @@ JSObject* js::CreateThisForFunction(JSContext* cx, HandleObject callee,
HandleObject newTarget, HandleObject newTarget,
NewObjectKind newKind) { NewObjectKind newKind) {
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) { if (!GetPrototypeFromConstructor(cx, newTarget, JSProto_Null, &proto)) {
return nullptr; return nullptr;
} }
@ -3679,6 +3713,10 @@ static void DumpProperty(const NativeObject* obj, Shape& shape,
out.printf(")\n"); out.printf(")\n");
} }
bool JSObject::hasSameRealmAs(JSContext* cx) const {
return nonCCWRealm() == cx->realm();
}
bool JSObject::uninlinedIsProxy() const { return is<ProxyObject>(); } bool JSObject::uninlinedIsProxy() const { return is<ProxyObject>(); }
bool JSObject::uninlinedNonProxyIsExtensible() const { bool JSObject::uninlinedNonProxyIsExtensible() const {

View file

@ -435,6 +435,7 @@ class JSObject : public js::gc::Cell {
MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this)); MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this));
return group_->realm(); return group_->realm();
} }
bool hasSameRealmAs(JSContext* cx) const;
// Returns the object's realm even if the object is a CCW (be careful, in // Returns the object's realm even if the object is a CCW (be careful, in
// this case the realm is not very meaningful because wrappers are shared by // this case the realm is not very meaningful because wrappers are shared by
@ -793,14 +794,35 @@ bool NewObjectWithTaggedProtoIsCachable(JSContext* cx,
// ES6 9.1.15 GetPrototypeFromConstructor. // ES6 9.1.15 GetPrototypeFromConstructor.
extern bool GetPrototypeFromConstructor(JSContext* cx, extern bool GetPrototypeFromConstructor(JSContext* cx,
js::HandleObject newTarget, js::HandleObject newTarget,
JSProtoKey intrinsicDefaultProto,
js::MutableHandleObject proto); js::MutableHandleObject proto);
// https://tc39.github.io/ecma262/#sec-getprototypefromconstructor
//
// Determine which [[Prototype]] to use when creating a new object using a
// builtin constructor.
//
// This sets `proto` to `nullptr` to mean "the builtin prototype object for
// this type in the current realm", the common case.
//
// We could set it to `cx->global()->getOrCreatePrototype(protoKey)`, but
// nullptr gets a fast path in e.g. js::NewObjectWithClassProtoCommon.
//
// intrinsicDefaultProto can be JSProto_Null if there's no appropriate
// JSProtoKey enum; but we then select the wrong prototype object in a
// multi-realm corner case (see bug 1515167).
MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor( MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor(
JSContext* cx, const CallArgs& args, js::MutableHandleObject proto) { JSContext* cx, const CallArgs& args, JSProtoKey intrinsicDefaultProto,
// When proto is set to nullptr, the caller is expected to select the js::MutableHandleObject proto) {
// correct default built-in prototype for this constructor. // We can skip the "prototype" lookup in the two common cases:
// 1. Builtin constructor called without `new`, as in `obj = Object();`.
// 2. Builtin constructor called with `new`, as in `obj = new Object();`.
//
// Cases that can't take the fast path include `new MySubclassOfObject()`,
// `new otherGlobal.Object()`, and `Reflect.construct(Object, [], Date)`.
if (!args.isConstructing() || if (!args.isConstructing() ||
&args.newTarget().toObject() == &args.callee()) { &args.newTarget().toObject() == &args.callee()) {
MOZ_ASSERT(args.callee().hasSameRealmAs(cx));
proto.set(nullptr); proto.set(nullptr);
return true; return true;
} }
@ -808,7 +830,8 @@ MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor(
// We're calling this constructor from a derived class, retrieve the // We're calling this constructor from a derived class, retrieve the
// actual prototype from newTarget. // actual prototype from newTarget.
RootedObject newTarget(cx, &args.newTarget().toObject()); RootedObject newTarget(cx, &args.newTarget().toObject());
return GetPrototypeFromConstructor(cx, newTarget, proto); return GetPrototypeFromConstructor(cx, newTarget, intrinsicDefaultProto,
proto);
} }
// Specialized call for constructing |this| with a known function callee, // Specialized call for constructing |this| with a known function callee,

View file

@ -198,7 +198,8 @@ bool SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc,
// Step 3 (Inlined 24.2.1.1 AllocateSharedArrayBuffer). // Step 3 (Inlined 24.2.1.1 AllocateSharedArrayBuffer).
// 24.2.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor). // 24.2.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_SharedArrayBuffer,
&proto)) {
return false; return false;
} }

View file

@ -121,38 +121,47 @@ struct TypeIDOfType;
template <> template <>
struct TypeIDOfType<int8_t> { struct TypeIDOfType<int8_t> {
static const Scalar::Type id = Scalar::Int8; static const Scalar::Type id = Scalar::Int8;
static const JSProtoKey protoKey = JSProto_Int8Array;
}; };
template <> template <>
struct TypeIDOfType<uint8_t> { struct TypeIDOfType<uint8_t> {
static const Scalar::Type id = Scalar::Uint8; static const Scalar::Type id = Scalar::Uint8;
static const JSProtoKey protoKey = JSProto_Uint8Array;
}; };
template <> template <>
struct TypeIDOfType<int16_t> { struct TypeIDOfType<int16_t> {
static const Scalar::Type id = Scalar::Int16; static const Scalar::Type id = Scalar::Int16;
static const JSProtoKey protoKey = JSProto_Int16Array;
}; };
template <> template <>
struct TypeIDOfType<uint16_t> { struct TypeIDOfType<uint16_t> {
static const Scalar::Type id = Scalar::Uint16; static const Scalar::Type id = Scalar::Uint16;
static const JSProtoKey protoKey = JSProto_Uint16Array;
}; };
template <> template <>
struct TypeIDOfType<int32_t> { struct TypeIDOfType<int32_t> {
static const Scalar::Type id = Scalar::Int32; static const Scalar::Type id = Scalar::Int32;
static const JSProtoKey protoKey = JSProto_Int32Array;
}; };
template <> template <>
struct TypeIDOfType<uint32_t> { struct TypeIDOfType<uint32_t> {
static const Scalar::Type id = Scalar::Uint32; static const Scalar::Type id = Scalar::Uint32;
static const JSProtoKey protoKey = JSProto_Uint32Array;
}; };
template <> template <>
struct TypeIDOfType<float> { struct TypeIDOfType<float> {
static const Scalar::Type id = Scalar::Float32; static const Scalar::Type id = Scalar::Float32;
static const JSProtoKey protoKey = JSProto_Float32Array;
}; };
template <> template <>
struct TypeIDOfType<double> { struct TypeIDOfType<double> {
static const Scalar::Type id = Scalar::Float64; static const Scalar::Type id = Scalar::Float64;
static const JSProtoKey protoKey = JSProto_Float64Array;
}; };
template <> template <>
struct TypeIDOfType<uint8_clamped> { struct TypeIDOfType<uint8_clamped> {
static const Scalar::Type id = Scalar::Uint8Clamped; static const Scalar::Type id = Scalar::Uint8Clamped;
static const JSProtoKey protoKey = JSProto_Uint8ClampedArray;
}; };
class SharedOps { class SharedOps {

View file

@ -285,6 +285,10 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
static constexpr Scalar::Type ArrayTypeID() { static constexpr Scalar::Type ArrayTypeID() {
return TypeIDOfType<NativeType>::id; return TypeIDOfType<NativeType>::id;
} }
static constexpr JSProtoKey protoKey() {
return TypeIDOfType<NativeType>::protoKey;
}
static constexpr bool ArrayTypeIsUnsigned() { static constexpr bool ArrayTypeIsUnsigned() {
return TypeIsUnsigned<NativeType>(); return TypeIsUnsigned<NativeType>();
} }
@ -596,7 +600,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
// 22.2.4.1, step 3 and 22.2.4.2, step 5. // 22.2.4.1, step 3 and 22.2.4.2, step 5.
// 22.2.4.2.1 AllocateTypedArray, step 1. // 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
return nullptr; return nullptr;
} }
@ -608,7 +612,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
// 22.2.4.{3,4,5}, step 4. // 22.2.4.{3,4,5}, step 4.
// 22.2.4.2.1 AllocateTypedArray, step 1. // 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx); RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) { if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
return nullptr; return nullptr;
} }
@ -957,20 +961,9 @@ template <typename T>
// As an optimization, skip the "prototype" lookup for %ArrayBuffer%. // As an optimization, skip the "prototype" lookup for %ArrayBuffer%.
if (ctor != arrayBufferCtor) { if (ctor != arrayBufferCtor) {
// 9.1.13 OrdinaryCreateFromConstructor, steps 1-2. // 9.1.13 OrdinaryCreateFromConstructor, steps 1-2.
if (!GetPrototypeFromConstructor(cx, ctor, &proto)) { if (!GetPrototypeFromConstructor(cx, ctor, JSProto_ArrayBuffer, &proto)) {
return false; return false;
} }
JSObject* arrayBufferProto =
GlobalObject::getOrCreateArrayBufferPrototype(cx, cx->global());
if (!arrayBufferProto) {
return false;
}
// Reset |proto| if it's the default %ArrayBufferPrototype%.
if (proto == arrayBufferProto) {
proto = nullptr;
}
} }
// 24.1.1.1 steps 1 (remaining part), 2-6. // 24.1.1.1 steps 1 (remaining part), 2-6.

View file

@ -2557,7 +2557,6 @@ static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
nsDisplayTransform* item = MakeDisplayItem<nsDisplayTransform>( nsDisplayTransform* item = MakeDisplayItem<nsDisplayTransform>(
aBuilder, aFrame, aNonParticipants, aBuilder->GetVisibleRect(), aBuilder, aFrame, aNonParticipants, aBuilder->GetVisibleRect(),
Matrix4x4(), aIndex); Matrix4x4(), aIndex);
item->SetNoExtendContext();
if (*aSeparator == nullptr) { if (*aSeparator == nullptr) {
*aSeparator = item; *aSeparator = item;

View file

@ -7184,7 +7184,6 @@ nsDisplayTransform::nsDisplayTransform(
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot), mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
mChildrenBuildingRect(aChildrenBuildingRect), mChildrenBuildingRect(aChildrenBuildingRect),
mIndex(aIndex), mIndex(aIndex),
mNoExtendContext(false),
mIsTransformSeparator(false), mIsTransformSeparator(false),
mTransformPreserves3DInited(false), mTransformPreserves3DInited(false),
mAllowAsyncAnimation(false) { mAllowAsyncAnimation(false) {
@ -7248,7 +7247,6 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot), mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
mChildrenBuildingRect(aChildrenBuildingRect), mChildrenBuildingRect(aChildrenBuildingRect),
mIndex(aIndex), mIndex(aIndex),
mNoExtendContext(false),
mIsTransformSeparator(false), mIsTransformSeparator(false),
mTransformPreserves3DInited(false), mTransformPreserves3DInited(false),
mAllowAsyncAnimation(aAllowAsyncAnimation) { mAllowAsyncAnimation(aAllowAsyncAnimation) {
@ -7272,7 +7270,6 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot), mAnimatedGeometryRootForScrollMetadata(mAnimatedGeometryRoot),
mChildrenBuildingRect(aChildrenBuildingRect), mChildrenBuildingRect(aChildrenBuildingRect),
mIndex(aIndex), mIndex(aIndex),
mNoExtendContext(false),
mIsTransformSeparator(true), mIsTransformSeparator(true),
mTransformPreserves3DInited(false), mTransformPreserves3DInited(false),
mAllowAsyncAnimation(false) { mAllowAsyncAnimation(false) {
@ -7873,13 +7870,15 @@ bool nsDisplayTransform::CreateWebRenderCommands(
bool animated = bool animated =
ActiveLayerTracker::IsStyleMaybeAnimated(Frame(), eCSSProperty_transform); ActiveLayerTracker::IsStyleMaybeAnimated(Frame(), eCSSProperty_transform);
bool preserve3D = mFrame->Extend3DContext() && !mIsTransformSeparator;
StackingContextHelper sc( StackingContextHelper sc(
aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, filters, aSc, GetActiveScrolledRoot(), mFrame, this, aBuilder, filters,
LayoutDeviceRect(position, LayoutDeviceSize()), &newTransformMatrix, LayoutDeviceRect(position, LayoutDeviceSize()), &newTransformMatrix,
animationsId ? &prop : nullptr, nullptr, transformForSC, nullptr, animationsId ? &prop : nullptr, nullptr, transformForSC, nullptr,
gfx::CompositionOp::OP_OVER, !BackfaceIsHidden(), gfx::CompositionOp::OP_OVER, !BackfaceIsHidden(),
mFrame->Extend3DContext() && !mNoExtendContext, deferredTransformItem, preserve3D, deferredTransformItem, wr::WrStackingContextClip::None(),
wr::WrStackingContextClip::None(), animated); animated);
return mStoredList.CreateWebRenderCommands(aBuilder, aResources, sc, aManager, return mStoredList.CreateWebRenderCommands(aBuilder, aResources, sc, aManager,
aDisplayListBuilder); aDisplayListBuilder);
@ -7932,8 +7931,8 @@ already_AddRefed<Layer> nsDisplayTransform::BuildLayer(
} }
// Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all
// flags, so we never need to explicitely unset this flag. // flags, so we never need to explicitly unset this flag.
if (mFrame->Extend3DContext() && !mNoExtendContext) { if (mFrame->Extend3DContext() && !mIsTransformSeparator) {
container->SetContentFlags(container->GetContentFlags() | container->SetContentFlags(container->GetContentFlags() |
Layer::CONTENT_EXTEND_3D_CONTEXT); Layer::CONTENT_EXTEND_3D_CONTEXT);
} else { } else {

View file

@ -6710,10 +6710,6 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
void WriteDebugInfo(std::stringstream& aStream) override; void WriteDebugInfo(std::stringstream& aStream) override;
// Force the layer created for this item not to extend 3D context.
// See nsIFrame::BuildDisplayListForStackingContext()
void SetNoExtendContext() { mNoExtendContext = true; }
void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override { void DoUpdateBoundsPreserves3D(nsDisplayListBuilder* aBuilder) override {
MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() || MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
IsTransformSeparator()); IsTransformSeparator());
@ -6794,15 +6790,13 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
mutable nsRect mBounds; mutable nsRect mBounds;
// True for mBounds is valid. // True for mBounds is valid.
mutable bool mHasBounds; mutable bool mHasBounds;
// Be forced not to extend 3D context. Since we don't create a
// transform item, a container layer, for every frames in a
// preserves3d context, the transform items of a child preserves3d
// context may extend the parent context not intented if the root of
// the child preserves3d context doesn't create a transform item.
// With this flags, we force the item not extending 3D context.
bool mNoExtendContext;
// This item is a separator between 3D rendering contexts, and // This item is a separator between 3D rendering contexts, and
// mTransform have been presetted by the constructor. // mTransform have been presetted by the constructor.
// This also forces us not to extend the 3D context. Since we don't create a
// transform item, a container layer, for every frame in a preserves3d
// context, the transform items of a child preserves3d context may extend the
// parent context unintendedly if the root of the child preserves3d context
// doesn't create a transform item.
bool mIsTransformSeparator; bool mIsTransformSeparator;
// True if mTransformPreserves3D have been initialized. // True if mTransformPreserves3D have been initialized.
bool mTransformPreserves3DInited; bool mTransformPreserves3DInited;

View file

@ -63,6 +63,7 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Parcelable;
import android.os.StrictMode; import android.os.StrictMode;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -71,6 +72,7 @@ import android.support.design.widget.Snackbar;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -136,6 +138,7 @@ public abstract class GeckoApp extends GeckoActivity
public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER"; public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER";
private static final String GECKOVIEW_STATE_BUNDLE = "geckoViewState";
public static final String EXTRA_STATE_BUNDLE = "stateBundle"; public static final String EXTRA_STATE_BUNDLE = "stateBundle";
public static final String PREFS_ALLOW_STATE_BUNDLE = "allowStateBundle"; public static final String PREFS_ALLOW_STATE_BUNDLE = "allowStateBundle";
@ -658,6 +661,16 @@ public abstract class GeckoApp extends GeckoActivity
} }
outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground()); outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground());
// There are situations where the saved instance state will be cleared (e.g. user swipes
// away activity in the task switcher), but Gecko will actually remain alive (because
// another activity or service of ours is still running in this process). The saved state is
// the only way we can reconnect to our previous GeckoView session and all the user's open
// tabs, so we need to keep a copy of the state ourselves.
SparseArray<Parcelable> geckoViewState = new SparseArray<>();
mLayerView.saveHierarchyState(geckoViewState);
outState.putSparseParcelableArray(GECKOVIEW_STATE_BUNDLE, geckoViewState);
getGeckoApplication().setSavedState(geckoViewState);
} }
public void addTab(int flags) { } public void addTab(int flags) { }
@ -696,7 +709,7 @@ public abstract class GeckoApp extends GeckoActivity
rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed()); rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed());
} }
((GeckoApplication) getApplicationContext()).onDelayedStartup(); getGeckoApplication().onDelayedStartup();
// Reset the crash loop counter if we remain alive for at least half a minute. // Reset the crash loop counter if we remain alive for at least half a minute.
ThreadUtils.postDelayedToBackgroundThread(new Runnable() { ThreadUtils.postDelayedToBackgroundThread(new Runnable() {
@ -976,6 +989,11 @@ public abstract class GeckoApp extends GeckoActivity
**/ **/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
// Within onCreate(), we might inject a different savedInstanceState for testing, but this
// won't influence what the OS will do with regards to calling onSaveInstanceState().
// Therefore, record whether we were passed some data or not.
final boolean receivedSavedInstanceState = (savedInstanceState != null);
// Enable Android Strict Mode for developers' local builds (the "default" channel). // Enable Android Strict Mode for developers' local builds (the "default" channel).
if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) { if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) {
enableStrictMode(); enableStrictMode();
@ -1106,6 +1124,9 @@ public abstract class GeckoApp extends GeckoActivity
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout); mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
mLayerView = (GeckoView) findViewById(R.id.layer_view); mLayerView = (GeckoView) findViewById(R.id.layer_view);
// Disable automatic state staving - we require some special handling that we need to do
// ourselves.
mLayerView.setSaveFromParentEnabled(false);
final GeckoSession session = new GeckoSession( final GeckoSession session = new GeckoSession(
new GeckoSessionSettings.Builder() new GeckoSessionSettings.Builder()
@ -1119,6 +1140,9 @@ public abstract class GeckoApp extends GeckoActivity
} }
mLayerView.setSession(session, GeckoApplication.getRuntime()); mLayerView.setSession(session, GeckoApplication.getRuntime());
mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER); mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
if (mIsRestoringActivity && !receivedSavedInstanceState) {
restoreGeckoViewState(getGeckoApplication().getSavedState());
}
getAppEventDispatcher().registerGeckoThreadListener(this, getAppEventDispatcher().registerGeckoThreadListener(this,
"Locale:Set", "Locale:Set",
@ -1313,6 +1337,26 @@ public abstract class GeckoApp extends GeckoActivity
mWasFirstTabShownAfterActivityUnhidden = false; // onStart indicates we were hidden. mWasFirstTabShownAfterActivityUnhidden = false; // onStart indicates we were hidden.
} }
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
final SparseArray<Parcelable> stateToRestore =
savedInstanceState.getSparseParcelableArray(GECKOVIEW_STATE_BUNDLE);
restoreGeckoViewState(stateToRestore);
}
/**
* Restores the given state into our GeckoView and clears any state we might have kept locally
* within our process, as it has now become obsolete.
*/
private void restoreGeckoViewState(final SparseArray<Parcelable> state) {
if (state != null) {
mLayerView.restoreHierarchyState(state);
}
getGeckoApplication().setSavedState(null);
}
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
@ -2534,6 +2578,10 @@ public abstract class GeckoApp extends GeckoActivity
return mLayerView; return mLayerView;
} }
protected GeckoApplication getGeckoApplication() {
return (GeckoApplication) getApplicationContext();
}
@Override @Override
public boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation) { public boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation) {
// We want to support the Screen Orientation API, and it always makes sense to lock the // We want to support the Screen Orientation API, and it always makes sense to lock the

View file

@ -16,6 +16,7 @@ import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Parcelable;
import android.os.Process; import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.MediaStore; import android.provider.MediaStore;
@ -26,6 +27,7 @@ import android.support.multidex.MultiDex;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher; import com.squareup.leakcanary.RefWatcher;
@ -81,6 +83,10 @@ public class GeckoApplication extends Application
private LightweightTheme mLightweightTheme; private LightweightTheme mLightweightTheme;
// GeckoApp *must* keep its GeckoView state around for as long as our app process (and
// therefore Gecko) keeps running, even if Android clears the normal savedInstanceState.
private SparseArray<Parcelable> mSavedState;
private RefWatcher mRefWatcher; private RefWatcher mRefWatcher;
private final EventListener mListener = new EventListener(); private final EventListener mListener = new EventListener();
@ -641,6 +647,14 @@ public class GeckoApplication extends Application
mLightweightTheme = new LightweightTheme(this); mLightweightTheme = new LightweightTheme(this);
} }
/* package */ void setSavedState(SparseArray<Parcelable> savedState) {
mSavedState = savedState;
}
/* package */ SparseArray<Parcelable> getSavedState() {
return mSavedState;
}
public static void createShortcut() { public static void createShortcut() {
final Tab selectedTab = Tabs.getInstance().getSelectedTab(); final Tab selectedTab = Tabs.getInstance().getSelectedTab();
if (selectedTab != null) { if (selectedTab != null) {

View file

@ -201,6 +201,8 @@ public class UpdateServiceHelper {
.replace("%DISTRIBUTION_VERSION%", "default") .replace("%DISTRIBUTION_VERSION%", "default")
.replace("%MOZ_VERSION%", AppConstants.MOZILLA_VERSION); .replace("%MOZ_VERSION%", AppConstants.MOZILLA_VERSION);
Log.i(LOGTAG, "AUS Url is: " + url);
try { try {
return new URI(url); return new URI(url);
} catch (java.net.URISyntaxException e) { } catch (java.net.URISyntaxException e) {

View file

@ -93,6 +93,7 @@ class AccessibilityTest : BaseSessionTest() {
private interface EventDelegate { private interface EventDelegate {
fun onAccessibilityFocused(event: AccessibilityEvent) { } fun onAccessibilityFocused(event: AccessibilityEvent) { }
fun onAccessibilityFocusCleared(event: AccessibilityEvent) { }
fun onClicked(event: AccessibilityEvent) { } fun onClicked(event: AccessibilityEvent) { }
fun onFocused(event: AccessibilityEvent) { } fun onFocused(event: AccessibilityEvent) { }
fun onSelected(event: AccessibilityEvent) { } fun onSelected(event: AccessibilityEvent) { }
@ -125,6 +126,7 @@ class AccessibilityTest : BaseSessionTest() {
AccessibilityEvent.TYPE_VIEW_FOCUSED -> newDelegate.onFocused(event) AccessibilityEvent.TYPE_VIEW_FOCUSED -> newDelegate.onFocused(event)
AccessibilityEvent.TYPE_VIEW_CLICKED -> newDelegate.onClicked(event) AccessibilityEvent.TYPE_VIEW_CLICKED -> newDelegate.onClicked(event)
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED -> newDelegate.onAccessibilityFocused(event) AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED -> newDelegate.onAccessibilityFocused(event)
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED -> newDelegate.onAccessibilityFocusCleared(event)
AccessibilityEvent.TYPE_VIEW_SELECTED -> newDelegate.onSelected(event) AccessibilityEvent.TYPE_VIEW_SELECTED -> newDelegate.onSelected(event)
AccessibilityEvent.TYPE_VIEW_SCROLLED -> newDelegate.onScrolled(event) AccessibilityEvent.TYPE_VIEW_SCROLLED -> newDelegate.onScrolled(event)
AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED -> newDelegate.onTextSelectionChanged(event) AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED -> newDelegate.onTextSelectionChanged(event)
@ -192,6 +194,7 @@ class AccessibilityTest : BaseSessionTest() {
assertThat("Label accessibility focused", node.className.toString(), assertThat("Label accessibility focused", node.className.toString(),
equalTo("android.view.View")) equalTo("android.view.View"))
assertThat("Text node should not be focusable", node.isFocusable, equalTo(false)) assertThat("Text node should not be focusable", node.isFocusable, equalTo(false))
assertThat("Text node should be a11y focused", node.isAccessibilityFocused, equalTo(true))
assertThat("Text node should not be clickable", node.isClickable, equalTo(false)) assertThat("Text node should not be clickable", node.isClickable, equalTo(false))
} }
}) })
@ -207,9 +210,22 @@ class AccessibilityTest : BaseSessionTest() {
assertThat("Editbox accessibility focused", node.className.toString(), assertThat("Editbox accessibility focused", node.className.toString(),
equalTo("android.widget.EditText")) equalTo("android.widget.EditText"))
assertThat("Entry node should be focusable", node.isFocusable, equalTo(true)) assertThat("Entry node should be focusable", node.isFocusable, equalTo(true))
assertThat("Entry node should be a11y focused", node.isAccessibilityFocused, equalTo(true))
assertThat("Entry node should be clickable", node.isClickable, equalTo(true)) assertThat("Entry node should be clickable", node.isClickable, equalTo(true))
} }
}) })
provider.performAction(nodeId,
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null)
sessionRule.waitUntilCalled(object : EventDelegate {
@AssertCalled(count = 1)
override fun onAccessibilityFocusCleared(event: AccessibilityEvent) {
assertThat("Accessibility focused node is now cleared", getSourceId(event), equalTo(nodeId))
val node = createNodeInfo(nodeId)
assertThat("Entry node should node be a11y focused", node.isAccessibilityFocused, equalTo(false))
}
})
} }
@Test fun testTextEntryNode() { @Test fun testTextEntryNode() {

View file

@ -118,10 +118,6 @@ public class SessionAccessibility {
if (mAttached) { if (mAttached) {
node = mSession.getSettings().getFullAccessibilityTree() ? node = mSession.getSettings().getFullAccessibilityTree() ?
getNodeFromGecko(virtualDescendantId) : getNodeFromCache(virtualDescendantId); getNodeFromGecko(virtualDescendantId) : getNodeFromCache(virtualDescendantId);
if (node != null) {
node.setAccessibilityFocused(mAccessibilityFocusedNode == virtualDescendantId);
node.setFocused(mFocusedNode == virtualDescendantId);
}
} }
if (node == null) { if (node == null) {
@ -143,6 +139,9 @@ public class SessionAccessibility {
final GeckoBundle data; final GeckoBundle data;
switch (action) { switch (action) {
case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
sendEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, virtualViewId, CLASSNAME_VIEW, null);
return true;
case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
if (virtualViewId == View.NO_ID) { if (virtualViewId == View.NO_ID) {
sendEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, View.NO_ID, CLASSNAME_WEBVIEW, null); sendEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, View.NO_ID, CLASSNAME_WEBVIEW, null);
@ -296,7 +295,8 @@ public class SessionAccessibility {
return; return;
} }
boolean isRoot = nodeInfo.getInt("id") == View.NO_ID; final int id = nodeInfo.getInt("id");
boolean isRoot = id == View.NO_ID;
if (isRoot) { if (isRoot) {
if (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null) { if (Build.VERSION.SDK_INT < 17 || mView.getDisplay() != null) {
// When running junit tests we don't have a display // When running junit tests we don't have a display
@ -321,8 +321,6 @@ public class SessionAccessibility {
// Add actions // Add actions
node.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT); node.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT); node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
node.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); node.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
node.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER | node.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |
@ -348,6 +346,14 @@ public class SessionAccessibility {
// Other boolean properties to consider later: // Other boolean properties to consider later:
// setHeading, setImportantForAccessibility, setScreenReaderFocusable, setShowingHintText, setDismissable // setHeading, setImportantForAccessibility, setScreenReaderFocusable, setShowingHintText, setDismissable
if (mAccessibilityFocusedNode == id) {
node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
node.setAccessibilityFocused(true);
} else {
node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
}
node.setFocused(mFocusedNode == id);
// Bounds // Bounds
int[] b = nodeInfo.getIntArray("bounds"); int[] b = nodeInfo.getIntArray("bounds");
if (b != null) { if (b != null) {
@ -714,6 +720,11 @@ public class SessionAccessibility {
} }
} }
break; break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
if (mAccessibilityFocusedNode == sourceId) {
mAccessibilityFocusedNode = 0;
}
break;
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
mAccessibilityFocusedNode = sourceId; mAccessibilityFocusedNode = sourceId;
break; break;

View file

@ -5334,7 +5334,11 @@ pref("dom.vr.autoactivate.enabled", false);
// The threshold value of trigger inputs for VR controllers // The threshold value of trigger inputs for VR controllers
pref("dom.vr.controller_trigger_threshold", "0.1"); pref("dom.vr.controller_trigger_threshold", "0.1");
// Enable external XR API integrations // Enable external XR API integrations
#if defined(XP_WIN) && defined(NIGHTLY_BUILD)
pref("dom.vr.external.enabled", true);
#else
pref("dom.vr.external.enabled", false); pref("dom.vr.external.enabled", false);
#endif
// Minimum number of milliseconds the browser will wait before attempting // Minimum number of milliseconds the browser will wait before attempting
// to re-start the VR service after an enumeration returned no devices. // to re-start the VR service after an enumeration returned no devices.
pref("dom.vr.external.notdetected.timeout", 60000); pref("dom.vr.external.notdetected.timeout", 60000);
@ -5423,8 +5427,8 @@ pref("dom.vr.poseprediction.enabled", true);
// tests or in a headless kiosk system. // tests or in a headless kiosk system.
pref("dom.vr.require-gesture", true); pref("dom.vr.require-gesture", true);
// Enable a separate process for VR module. // Enable a separate process for VR module.
#if defined(XP_WIN) #if defined(XP_WIN) && defined(NIGHTLY_BUILD)
pref("dom.vr.process.enabled", false); pref("dom.vr.process.enabled", true);
#endif #endif
// Puppet device, used for simulating VR hardware within tests and dev tools // Puppet device, used for simulating VR hardware within tests and dev tools
pref("dom.vr.puppet.enabled", false); pref("dom.vr.puppet.enabled", false);

View file

@ -7,6 +7,7 @@
</head> </head>
<body> <body>
<script> <script>
SimpleTest.requestCompleteLog();
// All the requests are sent to test_accept_header.sjs which will return // All the requests are sent to test_accept_header.sjs which will return
// different content based on the queryString. When the queryString is 'get', // different content based on the queryString. When the queryString is 'get',
@ -23,8 +24,7 @@ function test_last_request_and_continue(query, expected) {
function test_iframe() { function test_iframe() {
let observer = new PerformanceObserver(function(list, obj) { let observer = new PerformanceObserver(function(list, obj) {
obj.disconnect(); list.getEntries().forEach(entry => info(entry.name));
list.getEntries().forEach(entry => { list.getEntries().forEach(entry => {
if (entry.name.endsWith("test_accept_header.sjs?iframe")) { if (entry.name.endsWith("test_accept_header.sjs?iframe")) {
obj.disconnect(); obj.disconnect();
@ -51,8 +51,7 @@ function test_image() {
function test_style() { function test_style() {
let observer = new PerformanceObserver(function(list, obj) { let observer = new PerformanceObserver(function(list, obj) {
obj.disconnect(); list.getEntries().forEach(entry => info(entry.name));
list.getEntries().forEach(entry => { list.getEntries().forEach(entry => {
if (entry.name.endsWith("test_accept_header.sjs?style")) { if (entry.name.endsWith("test_accept_header.sjs?style")) {
obj.disconnect(); obj.disconnect();

View file

@ -1,8 +1,9 @@
function handleRequest(request, response) { function handleRequest(request, response) {
response.setStatusLine(request.httpVersion, "200", "OK"); response.setStatusLine(request.httpVersion, "200", "OK");
dump(`test_accept_header ${request.path}?${request.queryString}\n`);
if (request.queryString == "worker") { if (request.queryString == "worker") {
response.setHeader("Content-Type", "application/json", false); response.setHeader("Content-Type", "text/javascript", false);
response.write("postMessage(42)"); response.write("postMessage(42)");
setState("data", JSON.stringify({type: "worker", accept: request.getHeader("Accept") })); setState("data", JSON.stringify({type: "worker", accept: request.getHeader("Accept") }));
@ -39,7 +40,7 @@ function handleRequest(request, response) {
} }
if (request.queryString == "get") { if (request.queryString == "get") {
response.setHeader("Content-Type", "text/javascript", false); response.setHeader("Content-Type", "application/json", false);
response.write(getState("data")); response.write(getState("data"));
setState("data", ""); setState("data", "");

View file

@ -44,20 +44,40 @@ following are valid:
Auto Completion Auto Completion
--------------- ---------------
A `bash completion`_ script is bundled with mach. To enable it with ``bash``, A `bash completion`_ script is bundled with mach, it can be used with either ``bash`` or ``zsh``.
add the following to your ``~/.bashrc``, ``~/.bash_profile`` or equivalent:
Bash
~~~~
Add the following to your ``~/.bashrc``, ``~/.bash_profile`` or equivalent:
.. code-block:: shell .. code-block:: shell
source <srcdir>/python/mach/bash-completion.sh source <srcdir>/python/mach/bash-completion.sh
This script can also be used with ``zsh``. Add this to your ``~/.zshrc`` or .. tip::
equivalent:
Windows users using the default shell bundled with mozilla-build should source the completion
script from ``~/.bash_profile`` (it may need to be created first).
Zsh
~~~
Add this to your ``~/.zshrc`` or equivalent:
.. code-block:: shell .. code-block:: shell
autoload bashcompinit autoload -U bashcompinit && bashcompinit
bashcompinit source <srcdir>/python/mach/bash-completion.sh
The ``compinit`` function also needs to be loaded, but if using a framework (like ``oh-my-zsh``),
this will often be done for you. So if you see ``command not found: compdef``, you'll need to modify
the above instructions to:
.. code-block:: shell
autoload -U compinit && compinit
autoload -U bashcompinit && bashcompinit
source <srcdir>/python/mach/bash-completion.sh source <srcdir>/python/mach/bash-completion.sh
Don't forget to substitute ``<srcdir>`` with the path to your checkout. Don't forget to substitute ``<srcdir>`` with the path to your checkout.

View file

@ -72,19 +72,34 @@ class BuiltinCommands(object):
args = args[i+1:] args = args[i+1:]
break break
# If no command is typed yet, just offer the commands.
if not command: if not command:
print("\n".join(all_commands)) print("\n".join(all_commands))
return return
handler = self.context.commands.command_handlers[command] handler = self.context.commands.command_handlers[command]
# If a subcommand was typed, update the handler.
for arg in args: for arg in args:
if arg in handler.subcommand_handlers: if arg in handler.subcommand_handlers:
handler = handler.subcommand_handlers[arg] handler = handler.subcommand_handlers[arg]
break break
parser = handler.parser
targets = sorted(handler.subcommand_handlers.keys()) targets = sorted(handler.subcommand_handlers.keys())
if not is_help: if is_help:
targets.append('help') print("\n".join(targets))
targets.extend(chain(*[action.option_strings for action in parser._actions])) return
targets.append('help')
# The 'option_strings' are of the form [('-f', '--foo'), ('-b', '--bar'), ...].
option_strings = [item[0] for item in handler.arguments]
# Filter out positional arguments (we don't want to complete their metavar).
option_strings = [opt for opt in option_strings if opt[0].startswith('-')]
targets.extend(chain(*option_strings))
# If the command uses its own ArgumentParser, extract options from there as well.
if handler.parser:
targets.extend(chain(*[action.option_strings
for action in handler.parser._actions]))
print("\n".join(targets)) print("\n".join(targets))

View file

@ -299,6 +299,30 @@ raptor-tp6-8-chrome:
- --test=raptor-tp6-8 - --test=raptor-tp6-8
- --app=chrome - --app=chrome
raptor-tp6-9-firefox:
description: "Raptor tp6-9 on Firefox"
try-name: raptor-tp6-9-firefox
treeherder-symbol: Rap(tp6-9)
run-on-projects: ['try', 'mozilla-central']
tier: 2
mozharness:
extra-options:
- --test=raptor-tp6-9
raptor-tp6-9-chrome:
description: "Raptor tp6-9 on Chrome"
try-name: raptor-tp6-9-chrome
treeherder-symbol: Rap-C(tp6-9)
run-on-projects: ['try', 'mozilla-central']
tier:
by-test-platform:
linux64.*: 3
default: 2
mozharness:
extra-options:
- --test=raptor-tp6-9
- --app=chrome
raptor-tp6-10-firefox: raptor-tp6-10-firefox:
description: "Raptor tp6-10 on Firefox" description: "Raptor tp6-10 on Firefox"
try-name: raptor-tp6-10-firefox try-name: raptor-tp6-10-firefox

View file

@ -88,6 +88,7 @@ raptor-firefox:
- raptor-tp6-6-firefox - raptor-tp6-6-firefox
- raptor-tp6-7-firefox - raptor-tp6-7-firefox
- raptor-tp6-8-firefox - raptor-tp6-8-firefox
- raptor-tp6-9-firefox
- raptor-tp6-10-firefox - raptor-tp6-10-firefox
- raptor-speedometer-firefox - raptor-speedometer-firefox
- raptor-stylebench-firefox - raptor-stylebench-firefox
@ -121,6 +122,7 @@ raptor-chrome:
- raptor-tp6-6-chrome - raptor-tp6-6-chrome
- raptor-tp6-7-chrome - raptor-tp6-7-chrome
- raptor-tp6-8-chrome - raptor-tp6-8-chrome
- raptor-tp6-9-firefox
- raptor-tp6-10-chrome - raptor-tp6-10-chrome
- raptor-speedometer-chrome - raptor-speedometer-chrome
- raptor-stylebench-chrome - raptor-stylebench-chrome

View file

@ -164,7 +164,7 @@ def process_leak_log(leak_log_file, leak_thresholds=None,
# This list is based on kGeckoProcessTypeString. ipdlunittest processes likely # This list is based on kGeckoProcessTypeString. ipdlunittest processes likely
# are not going to produce leak logs we will ever see. # are not going to produce leak logs we will ever see.
knownProcessTypes = ["default", "plugin", "tab", "gmplugin", "gpu", "rdd"] knownProcessTypes = ["default", "plugin", "tab", "gmplugin", "gpu", "rdd", "vr"]
for processType in knownProcessTypes: for processType in knownProcessTypes:
log.info("TEST-INFO | leakcheck | %s process: leak threshold set at %d bytes" log.info("TEST-INFO | leakcheck | %s process: leak threshold set at %d bytes"

View file

@ -0,0 +1,10 @@
[
{
"size": 11627712,
"visibility": "public",
"digest": "a0dd4ccb99bac02a38b3c67baa08296c3e894837aba1c1b0750c592db6ff962ab0c61f557ffb45c2ea77e95345954a0786bf764448d090cc6cf76679d1a8a616",
"algorithm": "sha512",
"filename": "mitmproxy-tp6-pinterest.zip",
"unpack": true
}
]

View file

@ -7,6 +7,7 @@
[include:tests/raptor-tp6-6.ini] [include:tests/raptor-tp6-6.ini]
[include:tests/raptor-tp6-7.ini] [include:tests/raptor-tp6-7.ini]
[include:tests/raptor-tp6-8.ini] [include:tests/raptor-tp6-8.ini]
[include:tests/raptor-tp6-9.ini]
[include:tests/raptor-tp6-10.ini] [include:tests/raptor-tp6-10.ini]
# raptor benchmark tests # raptor benchmark tests

View file

@ -0,0 +1,34 @@
# 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/.
# raptor tp6-9
[DEFAULT]
type = pageload
playback = mitmproxy
playback_binary_manifest = mitmproxy-rel-bin-{platform}.manifest
python3_win_manifest = python3{x64}.manifest
page_cycles = 25
unit = ms
lower_is_better = true
alert_threshold = 2.0
# TTI/TTFI can take a while on some pages, and requires at least 5 seconds
# beyond typical pageload time
page_timeout = 30000
gecko_profile_interval = 1
gecko_profile_entries = 2000000
[raptor-tp6-pinterest-firefox]
apps = firefox
test_url = https://pinterest.com/
playback_pageset_manifest = mitmproxy-recordings-raptor-pinterest.manifest
playback_recordings = pinterest.mp
measure = fnbpaint, dcf, ttfi, loadtime
[raptor-tp6-pinterest-chrome]
apps = chrome
test_url = https://pinterest.com/
playback_pageset_manifest = mitmproxy-recordings-raptor-pinterest.manifest
playback_recordings = pinterest.mp
measure = fcp, loadtime

View file

@ -25,6 +25,7 @@
"*://*.instagram.com/*", "*://*.instagram.com/*",
"*://*.microsoft.com/*", "*://*.microsoft.com/*",
"*://*.paypal.com/*", "*://*.paypal.com/*",
"*://*.pinterest.com/*",
"*://*.reddit.com/*", "*://*.reddit.com/*",
"*://*.twitter.com/*", "*://*.twitter.com/*",
"*://*.vice.com/*", "*://*.vice.com/*",

View file

@ -1 +1,2 @@
lsan-allowed: [Alloc, AllocateProtoAndIfaceCache, EntrySlotOrCreate, Realloc, alloc_system::platform::_$LT$impl$u20$core..alloc..GlobalAlloc$u20$for$u20$alloc_system..System$GT$::alloc, alloc_system::platform::_$LT$impl$u20$core..alloc..GlobalAlloc$u20$for$u20$alloc_system..System$GT$::realloc, mozilla::dom::ChromeUtils::GenerateQI, mozilla::dom::Performance::CreateForMainThread, mozilla::dom::PerformanceMainThread::CreateNavigationTimingEntry, mozilla::net::nsStandardURL::TemplatedMutator]
leak-threshold: [tab:358400] leak-threshold: [tab:358400]

View file

@ -0,0 +1,4 @@
[MediaRecorder-pause-resume.html]
[MediaRecorder handles pause() and resume() calls appropriately in state and events]
expected: FAIL

View file

@ -1,7 +1,7 @@
[MediaRecorder-stop.html] [MediaRecorder-stop.html]
expected: TIMEOUT expected: TIMEOUT
[MediaRecorder will stop recording and fire a stop event when stop() is called] [MediaRecorder will stop recording and fire a stop event when stop() is called]
expected: FAIL expected: NOTRUN
[MediaRecorder will stop recording and fire a stop event when all tracks are ended] [MediaRecorder will stop recording and fire a stop event when all tracks are ended]
expected: TIMEOUT expected: TIMEOUT

View file

@ -0,0 +1,66 @@
<!doctype html>
<html>
<head>
<title>MediaRecorder Pause and Resume</title>
<link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<canvas id="canvas" width="200" height="200">
</canvas>
<script>
function createVideoStream() {
let canvas = document.getElementById("canvas");
canvas.getContext('2d');
return canvas.captureStream();
}
function recordEvents(target, events) {
let arr = [];
for (let ev of events) {
target.addEventListener(ev, _ => arr.push(ev));
}
return arr;
}
promise_test(async () => {
let video = createVideoStream();
let recorder = new MediaRecorder(video);
let events = recordEvents(recorder,
["start", "stop", "dataavailable", "pause", "resume", "error"]);
recorder.start();
assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
recorder.pause();
assert_equals(recorder.state, "paused", "MediaRecorder should be paused immediately following pause()");
// A second call to pause should be idempotent
recorder.pause();
assert_equals(recorder.state, "paused", "MediaRecorder should be paused immediately following pause()");
let event = await new Promise(r => recorder.onpause = r);
assert_equals(event.type, "pause", "the event type should be pause");
assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
recorder.resume();
assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following resume() call");
// A second call to resume should be idempotent
recorder.resume();
assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following resume() call");
event = await new Promise(r => recorder.onresume = r);
assert_equals(event.type, "resume", "the event type should be resume");
assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
recorder.stop();
await new Promise(r => recorder.onstop = r);
assert_array_equals(events, ["start", "pause", "resume", "dataavailable", "stop"],
"Should have gotten expected events");
}, "MediaRecorder handles pause() and resume() calls appropriately in state and events");
</script>
</body>
</html>

View file

@ -16,35 +16,68 @@
return canvas.captureStream(); return canvas.captureStream();
} }
async_test(t => { function recordEvents(target, events) {
let arr = [];
for (let ev of events) {
target.addEventListener(ev, _ => arr.push(ev));
}
return arr;
}
promise_test(async t => {
let video = createVideoStream(); let video = createVideoStream();
let recorder = new MediaRecorder(video); let recorder = new MediaRecorder(video);
recorder.onstop = t.step_func(errorEvent => { let events = recordEvents(recorder,
assert_equals(errorEvent.type, 'stop', 'the error type should be stop'); ["start", "stop", "dataavailable", "pause", "resume", "error"]);
assert_true(errorEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
assert_equals(recorder.state, "inactive", "MediaRecorder has been stopped when all tracks are ended");
t.done();
});
assert_equals(video.getVideoTracks().length, 1, "video mediastream starts with one track"); assert_equals(video.getVideoTracks().length, 1, "video mediastream starts with one track");
recorder.start(); recorder.start();
assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully"); assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
video.getVideoTracks()[0].stop(); video.getVideoTracks()[0].stop();
assert_equals(recorder.state, "recording", "MediaRecorder state should be recording immediately following last track ending");
let event = await new Promise(r => recorder.onstop = r);
assert_equals(event.type, "stop", "the event type should be stop");
assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event");
assert_array_equals(events, ["start", "dataavailable", "stop"],
"Should have gotten expected events");
recorder.stop();
await Promise.race([
new Promise((_, reject) => recorder.onstop =
_ => reject(new Error("stop() is idempotent"))),
new Promise(r => t.step_timeout(r, 0))
]);
}, "MediaRecorder will stop recording and fire a stop event when all tracks are ended"); }, "MediaRecorder will stop recording and fire a stop event when all tracks are ended");
async_test(t => { promise_test(async t => {
let video = createVideoStream(); let video = createVideoStream();
let recorder = new MediaRecorder(video); let recorder = new MediaRecorder(video);
recorder.onstop = t.step_func(errorEvent => { let events = recordEvents(recorder,
assert_equals(errorEvent.type, 'stop', 'the error type should be stop'); ["start", "stop", "dataavailable", "pause", "resume", "error"]);
assert_true(errorEvent.isTrusted, 'isTrusted should be true when the event is created by C++');
assert_equals(recorder.state, "inactive", "MediaRecorder has been stopped when stop() is called");
t.done();
});
recorder.start(); recorder.start();
assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully"); assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
recorder.stop(); recorder.stop();
assert_equals(recorder.state, "recording", "State should remain the same until stop event is fired"); assert_equals(recorder.state, "inactive", "MediaRecorder state should be inactive immediately following stop() call");
let event = await new Promise (r => recorder.onstop = r);
assert_equals(event.type, "stop", "the event type should be stop");
assert_true(event.isTrusted, "isTrusted should be true when the event is created by C++");
assert_equals(recorder.state, "inactive", "MediaRecorder is inactive after stop event");
assert_array_equals(events, ["start", "dataavailable", "stop"],
"Should have gotten expected events");
recorder.stop();
await Promise.race([
new Promise((_, reject) => recorder.onstop =
_ => reject(new Error("stop() is idempotent"))),
new Promise(r => t.step_timeout(r, 0))
]);
}, "MediaRecorder will stop recording and fire a stop event when stop() is called"); }, "MediaRecorder will stop recording and fire a stop event when stop() is called");
</script> </script>
</body> </body>
</html> </html>

View file

@ -48,6 +48,9 @@ with Files('gmp-sources/*'):
with Files('tests/browser/browser_audio*'): with Files('tests/browser/browser_audio*'):
BUG_COMPONENT = ('Core', 'Audio/Video: Playback') BUG_COMPONENT = ('Core', 'Audio/Video: Playback')
with Files('tests/browser/browser_autoplay*'):
BUG_COMPONENT = ('Core', 'Audio/Video: Playback')
with Files('tests/browser/*block*'): with Files('tests/browser/*block*'):
BUG_COMPONENT = ('Core', 'Audio/Video: Playback') BUG_COMPONENT = ('Core', 'Audio/Video: Playback')

View file

@ -377,17 +377,22 @@ class MozBrowser extends MozElementMixin(XULFrameElement) {
set docShellIsActive(val) { set docShellIsActive(val) {
if (this.isRemoteBrowser) { if (this.isRemoteBrowser) {
this.frameLoader.tabParent.docShellIsActive = val; let { frameLoader } = this;
return val; if (frameLoader && frameLoader.tabParent) {
frameLoader.tabParent.docShellIsActive = val;
}
} else if (this.docShell) {
this.docShell.isActive = val;
} }
if (this.docShell)
return this.docShell.isActive = val;
return false;
} }
get docShellIsActive() { get docShellIsActive() {
if (this.isRemoteBrowser) { if (this.isRemoteBrowser) {
return this.frameLoader.tabParent.docShellIsActive; let { frameLoader } = this;
if (frameLoader && frameLoader.tabParent) {
return frameLoader.tabParent.docShellIsActive;
}
return false;
} }
return this.docShell && this.docShell.isActive; return this.docShell && this.docShell.isActive;
} }
@ -396,11 +401,11 @@ class MozBrowser extends MozElementMixin(XULFrameElement) {
if (this.isRemoteBrowser) { if (this.isRemoteBrowser) {
let { frameLoader } = this; let { frameLoader } = this;
if (frameLoader && frameLoader.tabParent) { if (frameLoader && frameLoader.tabParent) {
return frameLoader.tabParent.renderLayers = val; frameLoader.tabParent.renderLayers = val;
} }
return false; } else {
this.docShellIsActive = val;
} }
return this.docShellIsActive = val;
} }
get renderLayers() { get renderLayers() {
@ -417,7 +422,7 @@ class MozBrowser extends MozElementMixin(XULFrameElement) {
get hasLayers() { get hasLayers() {
if (this.isRemoteBrowser) { if (this.isRemoteBrowser) {
let { frameLoader } = this; let { frameLoader } = this;
if (frameLoader.tabParent) { if (frameLoader && frameLoader.tabParent) {
return frameLoader.tabParent.hasLayers; return frameLoader.tabParent.hasLayers;
} }
return false; return false;
@ -727,7 +732,6 @@ class MozBrowser extends MozElementMixin(XULFrameElement) {
set userTypedValue(val) { set userTypedValue(val) {
this.urlbarChangeTracker.userTyped(); this.urlbarChangeTracker.userTyped();
this._userTypedValue = val; this._userTypedValue = val;
return val;
} }
get userTypedValue() { get userTypedValue() {

View file

@ -24,6 +24,7 @@ codespell:
- mobile/locales/en-US/ - mobile/locales/en-US/
- netwerk/locales/en-US/ - netwerk/locales/en-US/
- python/docs/ - python/docs/
- python/mach/docs/
- python/mozlint/ - python/mozlint/
- python/safety/ - python/safety/
- services/sync/locales/en-US/ - services/sync/locales/en-US/

View file

@ -23,7 +23,7 @@
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const NSMouseMoved = 5; const NSMouseMoved = 5;
var gLeftWindow, gRightWindow, gIFrame; var gLeftWindow, gRightWindow, gBrowserElement;
var gExpectedEvents = []; var gExpectedEvents = [];
function moveMouseTo(x, y, andThen) { function moveMouseTo(x, y, andThen) {
@ -36,24 +36,21 @@ function openWindows() {
gLeftWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200'); gLeftWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200');
SimpleTest.waitForFocus(function () { SimpleTest.waitForFocus(function () {
gRightWindow = open('empty_window.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200'); gRightWindow = open('empty_window.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200');
SimpleTest.waitForFocus(attachIFrameToRightWindow, gRightWindow); SimpleTest.waitForFocus(attachBrowserToLeftWindow, gRightWindow);
}, gLeftWindow); }, gLeftWindow);
} }
function attachIFrameToRightWindow() { function attachBrowserToLeftWindow() {
gIFrame = gLeftWindow.document.createElementNS(XUL_NS, "iframe"); gBrowserElement = gLeftWindow.document.createXULElement("browser");
gIFrame.setAttribute("type", "content"); gBrowserElement.setAttribute("type", "content");
gIFrame.setAttribute("clickthrough", "never"); gBrowserElement.setAttribute("src", "file_bug596600.html");
gIFrame.setAttribute("src", "file_bug596600.html"); gBrowserElement.style.width = "100px";
gIFrame.style.width = "100px"; gBrowserElement.style.height = "100px";
gIFrame.style.height = "100px"; gBrowserElement.style.margin = "50px";
gIFrame.style.margin = "50px"; gLeftWindow.document.documentElement.appendChild(gBrowserElement);
gLeftWindow.document.documentElement.appendChild(gIFrame); gBrowserElement.addEventListener("load", function (e) {
gIFrame.addEventListener("load", function (e) {
gIFrame.removeEventListener("load", arguments.callee, true);
test1(); test1();
}, true); }, { capture: true, once: true });
} }
function test1() { function test1() {
@ -76,7 +73,7 @@ function test1() {
moveMouseTo(80, 80, function () { moveMouseTo(80, 80, function () {
ok(!expectMouseOver, "Should have got mouseover event"); ok(!expectMouseOver, "Should have got mouseover event");
// Move over the iframe, which has clickthrough="never". // Move over the browser
expectMouseOut = true; expectMouseOut = true;
moveMouseTo(150, 150, function () { moveMouseTo(150, 150, function () {
ok (!expectMouseOut, "Should have got mouseout event"); ok (!expectMouseOut, "Should have got mouseout event");
@ -89,12 +86,12 @@ function test1() {
} }
function test2() { function test2() {
// Make the iframe cover the whole window. // Make the browser cover the whole window.
gIFrame.style.margin = "0"; gBrowserElement.style.margin = "0";
gIFrame.style.width = gIFrame.style.height = "200px"; gBrowserElement.style.width = gBrowserElement.style.height = "200px";
// Add a box to the iframe at the left edge. // Add a box to the browser at the left edge.
var doc = gIFrame.contentDocument; var doc = gBrowserElement.contentDocument;
var box = doc.createElement("div"); var box = doc.createElement("div");
box.setAttribute("id", "box"); box.setAttribute("id", "box");
box.style.position = "absolute"; box.style.position = "absolute";
@ -105,7 +102,7 @@ function test2() {
box.style.backgroundColor = "green"; box.style.backgroundColor = "green";
doc.body.appendChild(box); doc.body.appendChild(box);
ok(!box.matches(":hover"), "Box shouldn't be hovered (since the mouse isn't over it and since it's in a non-clickthrough iframe in a background window)"); ok(!box.matches(":hover"), "Box shouldn't be hovered (since the mouse isn't over it and since it's in a non-clickthrough browser in a background window)");
// A function to waitForFocus and then wait for synthetic mouse // A function to waitForFocus and then wait for synthetic mouse
// events to happen. Note that those happen off the refresh driver, // events to happen. Note that those happen off the refresh driver,
@ -134,22 +131,22 @@ function test2() {
// Move the mouse over the box. // Move the mouse over the box.
moveMouseTo(100, 150, function () { moveMouseTo(100, 150, function () {
ok(!box.matches(":hover"), "Box shouldn't be hovered (since it's in a non-clickthrough iframe in a background window)"); ok(!box.matches(":hover"), "Box shouldn't be hovered (since it's in a non-clickthrough browser in a background window)");
// Activate the left window. // Activate the left window.
changeFocusAndAwaitSyntheticMouse(function () { changeFocusAndAwaitSyntheticMouse(function () {
ok(gIFrame.matches(":hover"), "iframe should be hovered"); ok(gBrowserElement.matches(":hover"), "browser should be hovered");
ok(box.matches(":hover"), "Box should be hovered"); ok(box.matches(":hover"), "Box should be hovered");
// De-activate the window (by activating the right window). // De-activate the window (by activating the right window).
changeFocusAndAwaitSyntheticMouse(function () { changeFocusAndAwaitSyntheticMouse(function () {
ok(!gIFrame.matches(":hover"), "iframe shouldn't be hovered"); ok(!gBrowserElement.matches(":hover"), "browser shouldn't be hovered");
ok(!box.matches(":hover"), "Box shouldn't be hovered"); ok(!box.matches(":hover"), "Box shouldn't be hovered");
// Re-activate it. // Re-activate it.
changeFocusAndAwaitSyntheticMouse(function () { changeFocusAndAwaitSyntheticMouse(function () {
ok(gIFrame.matches(":hover"), "iframe should be hovered"); ok(gBrowserElement.matches(":hover"), "browser should be hovered");
ok(box.matches(":hover"), "Box should be hovered"); ok(box.matches(":hover"), "Box should be hovered");
// Unhover box and iframe by moving the mouse outside the window. // Unhover box and browser by moving the mouse outside the window.
moveMouseTo(0, 150, function () { moveMouseTo(0, 150, function () {
ok(!gIFrame.matches(":hover"), "iframe shouldn't be hovered"); ok(!gBrowserElement.matches(":hover"), "browser shouldn't be hovered");
ok(!box.matches(":hover"), "box shouldn't be hovered"); ok(!box.matches(":hover"), "box shouldn't be hovered");
finalize(); finalize();
}); });