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;
}
if (aRole == roles::HEADING) {
if (aRole == roles::HEADING && aAttributes) {
// The heading level is an attribute, so we need that.
nsString level;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("level"), level);
if (NS_SUCCEEDED(rv)) {
@ -411,7 +412,7 @@ bool AccessibleWrap::WrapperRangeInfo(double* aCurVal, double* aMinVal,
return false;
}
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle() {
mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(bool aSmall) {
nsAutoString name;
Name(name);
nsAutoString textValue;
@ -419,6 +420,10 @@ mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle() {
nsAutoString nodeID;
WrapperDOMNodeID(nodeID);
if (aSmall) {
return ToBundle(State(), Bounds(), ActionCount(), name, textValue, nodeID);
}
double curValue = UnspecifiedNaN<double>();
double minValue = UnspecifiedNaN<double>();
double maxValue = UnspecifiedNaN<double>();
@ -513,63 +518,66 @@ mozilla::java::GeckoBundle::LocalRef AccessibleWrap::ToBundle(
GECKOBUNDLE_PUT(nodeInfo, "rangeInfo", rangeInfo);
}
nsString inputTypeAttr;
nsAccUtils::GetAccAttr(aAttributes, nsGkAtoms::textInputType, inputTypeAttr);
int32_t inputType = GetInputType(inputTypeAttr);
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);
if (aAttributes) {
nsString inputTypeAttr;
nsAccUtils::GetAccAttr(aAttributes, nsGkAtoms::textInputType,
inputTypeAttr);
int32_t inputType = GetInputType(inputTypeAttr);
if (inputType) {
GECKOBUNDLE_PUT(nodeInfo, "inputType",
java::sdk::Integer::ValueOf(inputType));
}
}
nsString colSize;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("child-item-count"),
colSize);
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 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);
nsString unused;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("hierarchical"),
unused);
if (NS_SUCCEEDED(rv)) {
GECKOBUNDLE_PUT(collectionInfo, "isHierarchical",
java::sdk::Boolean::TRUE());
GECKOBUNDLE_PUT(nodeInfo, "collectionItemInfo", collectionItemInfo);
}
}
if (IsSelect()) {
int32_t selectionMode = (aState & states::MULTISELECTABLE) ? 2 : 1;
GECKOBUNDLE_PUT(collectionInfo, "selectionMode",
java::sdk::Integer::ValueOf(selectionMode));
nsString colSize;
rv = aAttributes->GetStringProperty(NS_LITERAL_CSTRING("child-item-count"),
colSize);
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;
}
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);
mozilla::java::GeckoBundle::LocalRef ToBundle();
mozilla::java::GeckoBundle::LocalRef ToBundle(bool aSmall = false);
mozilla::java::GeckoBundle::LocalRef ToBundle(
const uint64_t aState, const nsIntRect& aBounds,
const uint8_t aActionCount, const nsString& aName,
const nsString& aTextValue, const nsString& aDOMNodeID,
const double& aCurVal, const double& aMinVal, const double& aMaxVal,
const double& aStep, nsIPersistentProperties* aAttributes);
mozilla::java::GeckoBundle::LocalRef ToSmallBundle(
const uint64_t aState, const nsIntRect& aBounds,
const uint8_t aActionCount);
mozilla::java::GeckoBundle::LocalRef ToSmallBundle();
const double& aCurVal = UnspecifiedNaN<double>(),
const double& aMinVal = UnspecifiedNaN<double>(),
const double& aMaxVal = UnspecifiedNaN<double>(),
const double& aStep = UnspecifiedNaN<double>(),
nsIPersistentProperties* aAttributes = nullptr);
virtual void WrapperDOMNodeID(nsString& aDOMNodeID);

View file

@ -133,10 +133,18 @@ void DocAccessibleWrap::CacheViewportCallback(nsITimer* aTimer,
auto uid = accessible->IsDoc() && accessible->AsDoc()->IPCDoc()
? 0
: 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(
BatchData(accessible->Document()->IPCDoc(), uid, accessible->State(),
accessible->Bounds(), accessible->ActionCount(), nsString(),
nsString(), nsString(), UnspecifiedNaN<double>(),
accessible->Bounds(), accessible->ActionCount(), name,
textValue, nodeID, UnspecifiedNaN<double>(),
UnspecifiedNaN<double>(), UnspecifiedNaN<double>(),
UnspecifiedNaN<double>(), nsTArray<Attribute>()));
}

View file

@ -333,10 +333,11 @@ void SessionAccessibility::ReplaceViewportCache(
if (aData.Length() == aAccessibles.Length()) {
const BatchData& data = aData.ElementAt(i);
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);
} else {
infos->SetElement(i, acc->ToSmallBundle());
infos->SetElement(i, acc->ToBundle(true));
}
}
@ -380,10 +381,11 @@ void SessionAccessibility::UpdateCachedBounds(
if (aData.Length() == aAccessibles.Length()) {
const BatchData& data = aData.ElementAt(i);
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);
} 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)
ProxyScrollingEvent(target, aType, aScrollX, aScrollY, aMaxScrollX,
aMaxScrollY);
#else
ProxyEvent(target, aType);
#endif
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.formfill.enable", 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.pinned", true);
pref("services.sync.prefs.sync.browser.offline-apps.notify", true);

View file

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

View file

@ -10,6 +10,9 @@ prefs =
browser.migration.version=9999999
browser.startup.record=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 =
head.js
[browser_appmenu.js]

View file

@ -1,8 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* This test records which services, JS components, process scripts, and JS
* modules are loaded when creating a new content process.
/* This test records which services, JS components, frame scripts, 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
* are loading more JS code during content process startup.
@ -68,6 +68,23 @@ const whitelist = {
"resource://gre/modules/ExtensionUtils.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([
"chrome://global/content/process-content.js",
"resource:///modules/ContentObservers.js",
@ -89,6 +106,7 @@ const intermittently_loaded_whitelist = {
modules: new Set([
"resource://gre/modules/sessionstore/Utils.jsm",
]),
frameScripts: new Set([]),
processScripts: new Set([]),
};
@ -140,6 +158,12 @@ add_task(async function() {
let loadedInfo = await promise;
// Gather loaded frame scripts.
loadedInfo.frameScripts = {};
for (let [uri] of Services.mm.getDelayedFrameScripts()) {
loadedInfo.frameScripts[uri] = "";
}
// Gather loaded process scripts.
loadedInfo.processScripts = {};
for (let [uri] of Services.ppmm.getDelayedProcessScripts()) {

View file

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

View file

@ -324,28 +324,30 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
RefPtr<Session> mSession;
};
// Fire start event and set mimeType, run in main thread task.
class DispatchStartEventRunnable : public Runnable {
// Fire a named event, run in main thread task.
class DispatchEventRunnable : public Runnable {
public:
explicit DispatchStartEventRunnable(Session* aSession)
: Runnable("dom::MediaRecorder::Session::DispatchStartEventRunnable"),
mSession(aSession) {}
explicit DispatchEventRunnable(Session* aSession,
const nsAString& aEventName)
: Runnable("dom::MediaRecorder::Session::DispatchEventRunnable"),
mSession(aSession),
mEventName(aEventName) {}
NS_IMETHOD Run() override {
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());
NS_ENSURE_TRUE(mSession->mRecorder, NS_OK);
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
mSession->mRecorder->DispatchSimpleEvent(mEventName);
return NS_OK;
}
private:
RefPtr<Session> mSession;
nsString mEventName;
};
// Main thread task.
@ -591,6 +593,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
}
mEncoder->Suspend(TimeStamp::Now());
NS_DispatchToMainThread(
new DispatchEventRunnable(this, NS_LITERAL_STRING("pause")));
return NS_OK;
}
@ -603,6 +607,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
}
mEncoder->Resume(TimeStamp::Now());
NS_DispatchToMainThread(
new DispatchEventRunnable(this, NS_LITERAL_STRING("resume")));
return NS_OK;
}
@ -983,7 +989,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
if (mRunningState.isOk() &&
(mRunningState.unwrap() == RunningState::Idling ||
mRunningState.unwrap() == RunningState::Starting)) {
NS_DispatchToMainThread(new DispatchStartEventRunnable(this));
NS_DispatchToMainThread(
new DispatchEventRunnable(this, NS_LITERAL_STRING("start")));
}
if (rv == NS_OK) {
@ -1050,7 +1057,8 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
}
self->mMimeType = mime;
self->mRecorder->SetMimeType(self->mMimeType);
auto startEvent = MakeRefPtr<DispatchStartEventRunnable>(self);
auto startEvent = MakeRefPtr<DispatchEventRunnable>(
self, NS_LITERAL_STRING("start"));
startEvent->Run();
}
}
@ -1323,7 +1331,6 @@ void MediaRecorder::Stop(ErrorResult& aResult) {
LOG(LogLevel::Debug, ("MediaRecorder.Stop %p", this));
MediaRecorderReporter::RemoveMediaRecorder(this);
if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mState = RecordingState::Inactive;
@ -1332,12 +1339,16 @@ void MediaRecorder::Stop(ErrorResult& aResult) {
}
void MediaRecorder::Pause(ErrorResult& aResult) {
LOG(LogLevel::Debug, ("MediaRecorder.Pause"));
LOG(LogLevel::Debug, ("MediaRecorder.Pause %p", this));
if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (mState == RecordingState::Paused) {
return;
}
MOZ_ASSERT(mSessions.Length() > 0);
nsresult rv = mSessions.LastElement()->Pause();
if (NS_FAILED(rv)) {
@ -1346,16 +1357,19 @@ void MediaRecorder::Pause(ErrorResult& aResult) {
}
mState = RecordingState::Paused;
DispatchSimpleEvent(NS_LITERAL_STRING("pause"));
}
void MediaRecorder::Resume(ErrorResult& aResult) {
LOG(LogLevel::Debug, ("MediaRecorder.Resume"));
LOG(LogLevel::Debug, ("MediaRecorder.Resume %p", this));
if (mState == RecordingState::Inactive) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (mState == RecordingState::Recording) {
return;
}
MOZ_ASSERT(mSessions.Length() > 0);
nsresult rv = mSessions.LastElement()->Resume();
if (NS_FAILED(rv)) {
@ -1364,7 +1378,6 @@ void MediaRecorder::Resume(ErrorResult& aResult) {
}
mState = RecordingState::Recording;
DispatchSimpleEvent(NS_LITERAL_STRING("resume"));
}
void MediaRecorder::RequestData(ErrorResult& aResult) {
@ -1576,15 +1589,12 @@ void MediaRecorder::DispatchSimpleEvent(const nsAString& aStr) {
return;
}
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
event->InitEvent(aStr, false, false);
event->SetTrusted(true);
IgnoredErrorResult res;
DispatchEvent(*event, res);
if (res.Failed()) {
rv = DOMEventTargetHelper::DispatchTrustedEvent(aStr);
if (NS_FAILED(rv)) {
LOG(LogLevel::Error,
("MediaRecorder.DispatchSimpleEvent: DispatchTrustedEvent failed %p",
this));
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>();
if (aBuffer.mEncryptionScheme() > GMPEncryptionScheme::kGMPEncryptionNone) {
// Cbcs is not yet supported, so we expect only cenc if the buffer us
// encrypted
MOZ_ASSERT(aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCenc);
GMPEncryptionScheme::kGMPEncryptionCenc ||
aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCbcs);
aInputBuffer.key_id = aBuffer.mKeyId().Elements();
aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
@ -595,6 +595,8 @@ static void InitInputBuffer(const CDMInputBuffer& aBuffer,
aInputBuffer.encryption_scheme =
ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme());
}
aInputBuffer.pattern.crypt_byte_block = aBuffer.mCryptByteBlock();
aInputBuffer.pattern.skip_byte_block = aBuffer.mSkipByteBlock();
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");
break;
}
const nsTArray<uint8_t>& iv =
encryptionScheme != GMPEncryptionScheme::kGMPEncryptionCbcs
? crypto.mIV
: crypto.mConstantIV;
aBuffer = gmp::CDMInputBuffer(
shmem, crypto.mKeyId, crypto.mIV, aSample->mTime.ToMicroseconds(),
shmem, crypto.mKeyId, iv, aSample->mTime.ToMicroseconds(),
aSample->mDuration.ToMicroseconds(), crypto.mPlainSizes,
crypto.mEncryptedSizes, encryptionScheme);
crypto.mEncryptedSizes, crypto.mCryptByteBlock, crypto.mSkipByteBlock,
encryptionScheme);
MOZ_ASSERT(
aBuffer.mEncryptionScheme() == GMPEncryptionScheme::kGMPEncryptionNone ||
aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCenc,
"aBuffer should use either no encryption or cenc, other kinds are not "
GMPEncryptionScheme::kGMPEncryptionCenc ||
aBuffer.mEncryptionScheme() ==
GMPEncryptionScheme::kGMPEncryptionCbcs,
"aBuffer should use no encryption, cenc, or cbcs, other kinds are not "
"yet supported");
return true;
}

View file

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

View file

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

View file

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

View file

@ -378,7 +378,7 @@ class gfxPrefs final {
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.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.quit.timeout", VRExternalQuitTimeout, int32_t, 10000);
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)
// The VR Service accesses all hardware from a separate process
// and replaces the other VRSystemManager when enabled.
if (!gfxPrefs::VRProcessEnabled()) {
if (!gfxPrefs::VRProcessEnabled() || !XRE_IsGPUProcess()) {
VRServiceManager::Get().CreateService();
}
if (VRServiceManager::Get().IsServiceValid()) {
@ -127,6 +127,7 @@ void VRManager::Destroy() {
VRServiceManager::Get().Shutdown();
}
#endif
Shutdown();
mInitialized = false;
}
@ -140,7 +141,9 @@ void VRManager::Shutdown() {
if (VRServiceManager::Get().IsServiceValid()) {
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(
"VRServiceManager::ShutdownVRProcess",
[]() -> void { VRServiceManager::Get().ShutdownVRProcess(); });
@ -435,13 +438,15 @@ void VRManager::EnumerateVRDisplays() {
* is in progress
*/
#if !defined(MOZ_WIDGET_ANDROID)
if (gfxPrefs::VRProcessEnabled() && !mVRServiceStarted) {
VRServiceManager::Get().CreateVRProcess();
mVRServiceStarted = true;
} else if (!gfxPrefs::VRProcessEnabled()) {
if (VRServiceManager::Get().IsServiceValid()) {
VRServiceManager::Get().Start();
if (!mVRServiceStarted) {
if (XRE_IsGPUProcess() && gfxPrefs::VRProcessEnabled()) {
VRServiceManager::Get().CreateVRProcess();
mVRServiceStarted = true;
} else {
if (VRServiceManager::Get().IsServiceValid()) {
VRServiceManager::Get().Start();
mVRServiceStarted = true;
}
}
}
#endif

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,6 +8,7 @@
#include "VRGPUParent.h"
#include "VRManager.h"
#include "gfxConfig.h"
#include "nsDebugImpl.h"
#include "mozilla/gfx/gfxVars.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!");
ProcessChild::QuickExit();
}
if (!mVRGPUParent->IsClosed()) {
mVRGPUParent->Close();
}
mVRGPUParent = nullptr;
#if defined(XP_WIN)
@ -123,6 +127,8 @@ bool VRParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
return false;
}
nsDebugImpl::SetMultiprocessMode("VR");
// This must be checked before any IPDL message, which may hit sentinel
// errors due to parent and content processes having different
// versions.

View file

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

View file

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

View file

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

View file

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

View file

@ -462,6 +462,21 @@ pub struct ClipChainInstance {
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 {
pub fn new() -> Self {
ClipStore {

View file

@ -70,6 +70,20 @@ pub struct FrameBuilder {
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 device_pixel_scale: DevicePixelScale,
pub scene_properties: &'a SceneProperties,
@ -198,6 +212,16 @@ impl FrameBuilder {
self.prim_store.destroy(
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
@ -295,6 +319,30 @@ impl FrameBuilder {
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 {
render_tasks,
profile_counters,
@ -336,7 +384,6 @@ impl FrameBuilder {
prim_list,
pic_context,
pic_state,
&mut 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")]
use api::{DebugFlags, DeviceVector2D};
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};
#[cfg(feature = "debug_renderer")]
use debug_colors;
@ -24,7 +24,7 @@ use gpu_types::{TransformPalette, TransformPaletteId, UvRectKind};
use plane_split::{Clipper, Polygon, Splitter};
use prim_store::{PictureIndex, PrimitiveInstance, SpaceMapper, VisibleFace, PrimitiveInstanceKind};
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 render_backend::FrameResources;
use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle, TileBlit};
@ -135,6 +135,9 @@ pub struct Tile {
/// The currently visible rect within this tile, updated per frame.
/// If None, this tile is not currently visible.
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
/// (reasonably) efficiently hashed and compared.
descriptor: TileDescriptor,
@ -155,6 +158,10 @@ pub struct Tile {
/// care about. Stored as a set here, and then collected, sorted
/// and converted to transform key values during post_update.
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 {
@ -166,12 +173,14 @@ impl Tile {
local_rect: LayoutRect::zero(),
world_rect: WorldRect::zero(),
visible_rect: None,
valid_rect: WorldRect::zero(),
handle: TextureCacheHandle::invalid(),
descriptor: TileDescriptor::new(),
is_same_content: false,
is_valid: false,
same_frames: 0,
transforms: FastHashSet::default(),
potential_clips: FastHashMap::default(),
id,
}
}
@ -180,6 +189,7 @@ impl Tile {
fn clear(&mut self) {
self.transforms.clear();
self.descriptor.clear();
self.potential_clips.clear();
}
/// Update state related to whether a tile has the same
@ -191,9 +201,9 @@ impl Tile {
// The tile is only valid if:
// - 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.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.
if !self.is_same_content {
@ -216,15 +226,6 @@ pub struct PrimitiveDescriptor {
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
/// (reasonably) efficiently hashed and compared.
#[derive(Debug)]
@ -250,12 +251,6 @@ pub struct TileDescriptor {
// TODO(gw): Ugh, get rid of all opacity binding support!
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
/// tracking for this tile.
transforms: ComparableVec<TransformKey>,
@ -269,8 +264,6 @@ impl TileDescriptor {
clip_vertices: ComparableVec::new(),
opacity_bindings: ComparableVec::new(),
image_keys: ComparableVec::new(),
needed_regions: Vec::new(),
current_regions: Vec::new(),
transforms: ComparableVec::new(),
}
}
@ -283,7 +276,6 @@ impl TileDescriptor {
self.clip_vertices.reset();
self.opacity_bindings.reset();
self.image_keys.reset();
self.needed_regions.clear();
self.transforms.reset();
}
@ -298,49 +290,6 @@ impl TileDescriptor {
self.prims.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.
@ -789,6 +738,14 @@ impl TileCache {
let mut current_clip_chain_id = prim_instance.clip_chain_id;
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)
let is_cacheable = prim_instance.is_cacheable(
&resources,
@ -885,17 +842,27 @@ impl TileCache {
size,
);
if let Some(clip_world_rect) = self.map_local_to_world.map(&local_rect) {
// Even if this ends up getting clipped out by the current clip
// stack, we want to ensure the primitive gets added to the tiles
// below, to ensure invalidation isn't tripped up by the wrong
// number of primitives that affect this tile.
world_clip_rect = world_clip_rect
.intersection(&clip_world_rect)
.unwrap_or(WorldRect::zero());
}
match self.map_local_to_world.map(&local_rect) {
Some(clip_world_rect) => {
// Even if this ends up getting clipped out by the current clip
// stack, we want to ensure the primitive gets added to the tiles
// below, to ensure invalidation isn't tripped up by the wrong
// number of primitives that affect this tile.
world_clip_rect = world_clip_rect
.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 {
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 {
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);
}
@ -939,17 +907,6 @@ impl TileCache {
// a partially clipped tile, which would be a significant
// 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.
tile.is_same_content &= is_cacheable;
@ -973,6 +930,9 @@ impl TileCache {
for spatial_node_index in &clip_spatial_nodes {
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.
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
let mut transform_spatial_nodes: Vec<SpatialNodeIndex> = tile.transforms.drain().collect();
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 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
// frame in order to update the cached texture tile.
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.
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 {
Some(PictureCompositeMode::Filter(ref filter)) => {
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(
ROOT_SPATIAL_NODE_INDEX,
surface_spatial_node_index,
@ -1902,16 +1870,9 @@ impl PicturePrimitive {
prim_list: PrimitiveList,
context: PictureContext,
state: PictureState,
frame_state: &mut FrameBuildingState,
) -> Option<ClipNodeCollector> {
) {
self.prim_list = prim_list;
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) {
@ -1926,6 +1887,7 @@ impl PicturePrimitive {
transforms: &TransformPalette,
prim_instance: &PrimitiveInstance,
original_local_rect: LayoutRect,
combined_local_clip_rect: &LayoutRect,
world_rect: WorldRect,
plane_split_anchor: usize,
) -> bool {
@ -1940,7 +1902,7 @@ impl PicturePrimitive {
// since we determine the UVs by doing a bilerp with a factor
// from the 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(),
None => return false,
@ -2298,7 +2260,6 @@ impl PicturePrimitive {
&mut self,
pic_index: PictureIndex,
prim_instance: &PrimitiveInstance,
prim_local_rect: &LayoutRect,
clipped_prim_bounding_rect: WorldRect,
surface_index: SurfaceIndex,
frame_context: &FrameBuildingContext,
@ -2334,7 +2295,7 @@ impl PicturePrimitive {
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(
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)
// [brush specific data]
// [segment_rect, segment data]
let shadow_rect = prim_local_rect.translate(&offset);
let shadow_rect = self.local_rect.translate(&offset);
// ImageBrush colors
request.push(color.premultiplied());
request.push(PremultipliedColorF::WHITE);
request.push([
prim_local_rect.size.width,
prim_local_rect.size.height,
self.local_rect.size.width,
self.local_rect.size.height,
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) {
RootedObject proto(cx);
if (isConstructor) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Array, &proto)) {
return false;
}
} else {

View file

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

View file

@ -137,7 +137,7 @@ bool DataViewObject::constructSameCompartment(JSContext* cx,
}
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_DataView, &proto)) {
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
// compartment.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_DataView, &proto)) {
return false;
}

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -120,15 +120,14 @@ class UnwinderTypeCache(object):
class_type = gdb.lookup_type('js::jit::' + SizeOfFramePrefix[name])
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():
# 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'
# 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*(.*)$")
@ -148,10 +147,10 @@ def parse_proc_maps():
mappings.append((long(start, 16), long(end, 16)))
return mappings
# A symbol/value pair as expected from gdb frame decorators.
class FrameSymbol(object):
"A symbol/value pair as expected from gdb frame decorators."
def __init__(self, sym, val):
self.sym = sym
self.val = val
@ -162,12 +161,12 @@ class FrameSymbol(object):
def value(self):
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):
"""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):
super(JitFrameDecorator, self).__init__(base)
self.info = info
@ -258,10 +257,10 @@ class JitFrameDecorator(FrameDecorator):
result.append(FrameSymbol(name, args_ptr[i]))
return result
# A frame filter for SpiderMonkey.
class SpiderMonkeyFrameFilter(object):
"A frame filter for SpiderMonkey."
# |state_holder| is either None, or an instance of
# SpiderMonkeyUnwinder. If the latter, then this class will
# reference the |unwinder_state| attribute to find the current
@ -285,31 +284,31 @@ class SpiderMonkeyFrameFilter(object):
def filter(self, frame_iter):
return imap(self.maybe_wrap_frame, frame_iter)
# A frame id class, as specified by the gdb unwinder API.
class SpiderMonkeyFrameId(object):
"A frame id class, as specified by the gdb unwinder API."
def __init__(self, sp, pc):
self.sp = sp
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):
"""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):
self.next_sp = None
self.next_type = None
@ -505,10 +504,10 @@ class UnwinderState(object):
# the time being.
return self.unwind_exit_frame(pc, pending_frame)
# The UnwinderState subclass for x86-64.
class x64UnwinderState(UnwinderState):
"The UnwinderState subclass for x86-64."
SP_REGISTER = 'rsp'
PC_REGISTER = 'rip'
@ -534,12 +533,12 @@ class x64UnwinderState(UnwinderState):
if reg is "rbp":
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):
"""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|.
UNWINDERS = [x64UnwinderState]
@ -601,11 +600,11 @@ class SpiderMonkeyUnwinder(Unwinder):
def invalidate_unwinder_state(self, *args, **kwargs):
self.unwinder_state = None
# Register the unwinder and frame filter with |objfile|. If |objfile|
# is None, register them globally.
def register_unwinder(objfile):
"""Register the unwinder and frame filter with |objfile|. If |objfile|
is None, register them globally."""
type_cache = UnwinderTypeCache()
unwinder = None
# 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());
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Date, &proto)) {
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) {
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
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
return false;
}
/* Compute the error message, if any. */
// Compute the error message, if any.
RootedString message(cx, nullptr);
if (args.hasDefined(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());
/* Set the 'fileName' property. */
RootedString fileName(cx);
if (args.length() > 1) {
fileName = ToString<CanGC>(cx, args[1]);
@ -422,7 +431,6 @@ bool Error(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
/* Set the 'lineNumber' property. */
uint32_t lineNumber, columnNumber = 0;
if (args.length() > 2) {
if (!ToUint32(cx, args[2], &lineNumber)) {
@ -438,15 +446,6 @@ bool Error(JSContext* cx, unsigned argc, Value* vp) {
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,
ErrorObject::create(cx, exnType, stack, fileName, lineNumber,
columnNumber, nullptr, message, proto));

View file

@ -563,7 +563,7 @@ static bool Number(JSContext* cx, unsigned argc, Value* vp) {
}
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Number, &proto)) {
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
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
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
skip script test262/built-ins/Proxy/apply/arguments-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/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/Function/internals/Construct/derived-return-val-realm.js
skip script test262/built-ins/Function/internals/Construct/derived-this-uninitialized-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1317416
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/Map/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
skip script test262/built-ins/Function/prototype/bind/proto-from-ctor-realm.js
# https://bugzilla.mozilla.org/show_bug.cgi?id=1317395
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.')
input_og.add_option('-x', '--exclude-file', action='append',
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=[],
help='Include the given test file or directory.')
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)
# 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,
requested_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).
// 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ArrayBuffer,
&proto)) {
return false;
}

View file

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

View file

@ -357,7 +357,7 @@ class MOZ_STACK_CLASS TryNoteIter {
} while (!(pcInRange() && tn_->kind == JSTRY_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;
}
JSProtoKey protoKey = JSProto_Null;
if (isAsync) {
if (isGenerator) {
if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf,
@ -2022,12 +2023,13 @@ static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
parameterListEnd)) {
return false;
}
protoKey = JSProto_Function;
}
}
// Steps 6, 29.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
return false;
}

View file

@ -1026,7 +1026,8 @@ bool js::NewObjectScriptedCall(JSContext* cx, MutableHandleObject pobj) {
JSObject* js::CreateThis(JSContext* cx, const Class* newclasp,
HandleObject callee) {
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, callee, &proto)) {
if (!GetPrototypeFromConstructor(
cx, callee, JSCLASS_CACHED_PROTO_KEY(newclasp), &proto)) {
return nullptr;
}
gc::AllocKind kind = NewObjectGCKind(newclasp);
@ -1158,12 +1159,45 @@ JSObject* js::CreateThisForFunctionWithProto(
}
bool js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget,
JSProtoKey intrinsicDefaultProto,
MutableHandleObject proto) {
RootedValue protov(cx);
if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) {
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;
}
@ -1171,7 +1205,7 @@ JSObject* js::CreateThisForFunction(JSContext* cx, HandleObject callee,
HandleObject newTarget,
NewObjectKind newKind) {
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) {
if (!GetPrototypeFromConstructor(cx, newTarget, JSProto_Null, &proto)) {
return nullptr;
}
@ -3679,6 +3713,10 @@ static void DumpProperty(const NativeObject* obj, Shape& shape,
out.printf(")\n");
}
bool JSObject::hasSameRealmAs(JSContext* cx) const {
return nonCCWRealm() == cx->realm();
}
bool JSObject::uninlinedIsProxy() const { return is<ProxyObject>(); }
bool JSObject::uninlinedNonProxyIsExtensible() const {

View file

@ -435,6 +435,7 @@ class JSObject : public js::gc::Cell {
MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this));
return group_->realm();
}
bool hasSameRealmAs(JSContext* cx) const;
// 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
@ -793,14 +794,35 @@ bool NewObjectWithTaggedProtoIsCachable(JSContext* cx,
// ES6 9.1.15 GetPrototypeFromConstructor.
extern bool GetPrototypeFromConstructor(JSContext* cx,
js::HandleObject newTarget,
JSProtoKey intrinsicDefaultProto,
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(
JSContext* cx, const CallArgs& args, js::MutableHandleObject proto) {
// When proto is set to nullptr, the caller is expected to select the
// correct default built-in prototype for this constructor.
JSContext* cx, const CallArgs& args, JSProtoKey intrinsicDefaultProto,
js::MutableHandleObject proto) {
// 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() ||
&args.newTarget().toObject() == &args.callee()) {
MOZ_ASSERT(args.callee().hasSameRealmAs(cx));
proto.set(nullptr);
return true;
}
@ -808,7 +830,8 @@ MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor(
// We're calling this constructor from a derived class, retrieve the
// actual prototype from newTarget.
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,

View file

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

View file

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

View file

@ -285,6 +285,10 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
static constexpr Scalar::Type ArrayTypeID() {
return TypeIDOfType<NativeType>::id;
}
static constexpr JSProtoKey protoKey() {
return TypeIDOfType<NativeType>::protoKey;
}
static constexpr bool ArrayTypeIsUnsigned() {
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.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
return nullptr;
}
@ -608,7 +612,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
// 22.2.4.{3,4,5}, step 4.
// 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto)) {
if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey(), &proto)) {
return nullptr;
}
@ -957,20 +961,9 @@ template <typename T>
// As an optimization, skip the "prototype" lookup for %ArrayBuffer%.
if (ctor != arrayBufferCtor) {
// 9.1.13 OrdinaryCreateFromConstructor, steps 1-2.
if (!GetPrototypeFromConstructor(cx, ctor, &proto)) {
if (!GetPrototypeFromConstructor(cx, ctor, JSProto_ArrayBuffer, &proto)) {
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.

View file

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

View file

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

View file

@ -6710,10 +6710,6 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
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 {
MOZ_ASSERT(mFrame->Combines3DTransformWithAncestors() ||
IsTransformSeparator());
@ -6794,15 +6790,13 @@ class nsDisplayTransform : public nsDisplayHitTestInfoItem {
mutable nsRect mBounds;
// True for mBounds is valid.
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
// 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;
// True if mTransformPreserves3D have been initialized.
bool mTransformPreserves3DInited;

View file

@ -63,6 +63,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.os.StrictMode;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
@ -71,6 +72,7 @@ import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
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";
private static final String GECKOVIEW_STATE_BUNDLE = "geckoViewState";
public static final String EXTRA_STATE_BUNDLE = "stateBundle";
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());
// 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) { }
@ -696,7 +709,7 @@ public abstract class GeckoApp extends GeckoActivity
rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed());
}
((GeckoApplication) getApplicationContext()).onDelayedStartup();
getGeckoApplication().onDelayedStartup();
// Reset the crash loop counter if we remain alive for at least half a minute.
ThreadUtils.postDelayedToBackgroundThread(new Runnable() {
@ -976,6 +989,11 @@ public abstract class GeckoApp extends GeckoActivity
**/
@Override
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).
if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) {
enableStrictMode();
@ -1106,6 +1124,9 @@ public abstract class GeckoApp extends GeckoActivity
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
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(
new GeckoSessionSettings.Builder()
@ -1119,6 +1140,9 @@ public abstract class GeckoApp extends GeckoActivity
}
mLayerView.setSession(session, GeckoApplication.getRuntime());
mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
if (mIsRestoringActivity && !receivedSavedInstanceState) {
restoreGeckoViewState(getGeckoApplication().getSavedState());
}
getAppEventDispatcher().registerGeckoThreadListener(this,
"Locale:Set",
@ -1313,6 +1337,26 @@ public abstract class GeckoApp extends GeckoActivity
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
protected void onStop() {
super.onStop();
@ -2534,6 +2578,10 @@ public abstract class GeckoApp extends GeckoActivity
return mLayerView;
}
protected GeckoApplication getGeckoApplication() {
return (GeckoApplication) getApplicationContext();
}
@Override
public boolean setRequestedOrientationForCurrentActivity(int requestedActivityInfoOrientation) {
// 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.os.Bundle;
import android.os.Environment;
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
import android.provider.MediaStore;
@ -26,6 +27,7 @@ import android.support.multidex.MultiDex;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.util.SparseArray;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
@ -81,6 +83,10 @@ public class GeckoApplication extends Application
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 final EventListener mListener = new EventListener();
@ -641,6 +647,14 @@ public class GeckoApplication extends Application
mLightweightTheme = new LightweightTheme(this);
}
/* package */ void setSavedState(SparseArray<Parcelable> savedState) {
mSavedState = savedState;
}
/* package */ SparseArray<Parcelable> getSavedState() {
return mSavedState;
}
public static void createShortcut() {
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
if (selectedTab != null) {

View file

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

View file

@ -93,6 +93,7 @@ class AccessibilityTest : BaseSessionTest() {
private interface EventDelegate {
fun onAccessibilityFocused(event: AccessibilityEvent) { }
fun onAccessibilityFocusCleared(event: AccessibilityEvent) { }
fun onClicked(event: AccessibilityEvent) { }
fun onFocused(event: AccessibilityEvent) { }
fun onSelected(event: AccessibilityEvent) { }
@ -125,6 +126,7 @@ class AccessibilityTest : BaseSessionTest() {
AccessibilityEvent.TYPE_VIEW_FOCUSED -> newDelegate.onFocused(event)
AccessibilityEvent.TYPE_VIEW_CLICKED -> newDelegate.onClicked(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_SCROLLED -> newDelegate.onScrolled(event)
AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED -> newDelegate.onTextSelectionChanged(event)
@ -192,6 +194,7 @@ class AccessibilityTest : BaseSessionTest() {
assertThat("Label accessibility focused", node.className.toString(),
equalTo("android.view.View"))
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))
}
})
@ -207,9 +210,22 @@ class AccessibilityTest : BaseSessionTest() {
assertThat("Editbox accessibility focused", node.className.toString(),
equalTo("android.widget.EditText"))
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))
}
})
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() {

View file

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

View file

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

View file

@ -7,6 +7,7 @@
</head>
<body>
<script>
SimpleTest.requestCompleteLog();
// All the requests are sent to test_accept_header.sjs which will return
// 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() {
let observer = new PerformanceObserver(function(list, obj) {
obj.disconnect();
list.getEntries().forEach(entry => info(entry.name));
list.getEntries().forEach(entry => {
if (entry.name.endsWith("test_accept_header.sjs?iframe")) {
obj.disconnect();
@ -51,8 +51,7 @@ function test_image() {
function test_style() {
let observer = new PerformanceObserver(function(list, obj) {
obj.disconnect();
list.getEntries().forEach(entry => info(entry.name));
list.getEntries().forEach(entry => {
if (entry.name.endsWith("test_accept_header.sjs?style")) {
obj.disconnect();

View file

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

View file

@ -44,20 +44,40 @@ following are valid:
Auto Completion
---------------
A `bash completion`_ script is bundled with mach. To enable it with ``bash``,
add the following to your ``~/.bashrc``, ``~/.bash_profile`` or equivalent:
A `bash completion`_ script is bundled with mach, it can be used with either ``bash`` or ``zsh``.
Bash
~~~~
Add the following to your ``~/.bashrc``, ``~/.bash_profile`` or equivalent:
.. code-block:: shell
source <srcdir>/python/mach/bash-completion.sh
This script can also be used with ``zsh``. Add this to your ``~/.zshrc`` or
equivalent:
.. tip::
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
autoload bashcompinit
bashcompinit
autoload -U 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
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:]
break
# If no command is typed yet, just offer the commands.
if not command:
print("\n".join(all_commands))
return
handler = self.context.commands.command_handlers[command]
# If a subcommand was typed, update the handler.
for arg in args:
if arg in handler.subcommand_handlers:
handler = handler.subcommand_handlers[arg]
break
parser = handler.parser
targets = sorted(handler.subcommand_handlers.keys())
if not is_help:
targets.append('help')
targets.extend(chain(*[action.option_strings for action in parser._actions]))
if is_help:
print("\n".join(targets))
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))

View file

@ -299,6 +299,30 @@ raptor-tp6-8-chrome:
- --test=raptor-tp6-8
- --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:
description: "Raptor tp6-10 on Firefox"
try-name: raptor-tp6-10-firefox

View file

@ -88,6 +88,7 @@ raptor-firefox:
- raptor-tp6-6-firefox
- raptor-tp6-7-firefox
- raptor-tp6-8-firefox
- raptor-tp6-9-firefox
- raptor-tp6-10-firefox
- raptor-speedometer-firefox
- raptor-stylebench-firefox
@ -121,6 +122,7 @@ raptor-chrome:
- raptor-tp6-6-chrome
- raptor-tp6-7-chrome
- raptor-tp6-8-chrome
- raptor-tp6-9-firefox
- raptor-tp6-10-chrome
- raptor-speedometer-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
# 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:
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-7.ini]
[include:tests/raptor-tp6-8.ini]
[include:tests/raptor-tp6-9.ini]
[include:tests/raptor-tp6-10.ini]
# 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/*",
"*://*.microsoft.com/*",
"*://*.paypal.com/*",
"*://*.pinterest.com/*",
"*://*.reddit.com/*",
"*://*.twitter.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]

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]
expected: TIMEOUT
[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]
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();
}
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 recorder = new MediaRecorder(video);
recorder.onstop = t.step_func(errorEvent => {
assert_equals(errorEvent.type, 'stop', 'the error type should be stop');
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();
});
let events = recordEvents(recorder,
["start", "stop", "dataavailable", "pause", "resume", "error"]);
assert_equals(video.getVideoTracks().length, 1, "video mediastream starts with one track");
recorder.start();
assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
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");
async_test(t => {
promise_test(async t => {
let video = createVideoStream();
let recorder = new MediaRecorder(video);
recorder.onstop = t.step_func(errorEvent => {
assert_equals(errorEvent.type, 'stop', 'the error type should be stop');
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();
});
let events = recordEvents(recorder,
["start", "stop", "dataavailable", "pause", "resume", "error"]);
recorder.start();
assert_equals(recorder.state, "recording", "MediaRecorder has been started successfully");
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");
</script>
</body>
</html>
</html>

View file

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

View file

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

View file

@ -24,6 +24,7 @@ codespell:
- mobile/locales/en-US/
- netwerk/locales/en-US/
- python/docs/
- python/mach/docs/
- python/mozlint/
- python/safety/
- 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 NSMouseMoved = 5;
var gLeftWindow, gRightWindow, gIFrame;
var gLeftWindow, gRightWindow, gBrowserElement;
var gExpectedEvents = [];
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');
SimpleTest.waitForFocus(function () {
gRightWindow = open('empty_window.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200');
SimpleTest.waitForFocus(attachIFrameToRightWindow, gRightWindow);
SimpleTest.waitForFocus(attachBrowserToLeftWindow, gRightWindow);
}, gLeftWindow);
}
function attachIFrameToRightWindow() {
gIFrame = gLeftWindow.document.createElementNS(XUL_NS, "iframe");
gIFrame.setAttribute("type", "content");
gIFrame.setAttribute("clickthrough", "never");
gIFrame.setAttribute("src", "file_bug596600.html");
gIFrame.style.width = "100px";
gIFrame.style.height = "100px";
gIFrame.style.margin = "50px";
gLeftWindow.document.documentElement.appendChild(gIFrame);
gIFrame.addEventListener("load", function (e) {
gIFrame.removeEventListener("load", arguments.callee, true);
function attachBrowserToLeftWindow() {
gBrowserElement = gLeftWindow.document.createXULElement("browser");
gBrowserElement.setAttribute("type", "content");
gBrowserElement.setAttribute("src", "file_bug596600.html");
gBrowserElement.style.width = "100px";
gBrowserElement.style.height = "100px";
gBrowserElement.style.margin = "50px";
gLeftWindow.document.documentElement.appendChild(gBrowserElement);
gBrowserElement.addEventListener("load", function (e) {
test1();
}, true);
}, { capture: true, once: true });
}
function test1() {
@ -76,7 +73,7 @@ function test1() {
moveMouseTo(80, 80, function () {
ok(!expectMouseOver, "Should have got mouseover event");
// Move over the iframe, which has clickthrough="never".
// Move over the browser
expectMouseOut = true;
moveMouseTo(150, 150, function () {
ok (!expectMouseOut, "Should have got mouseout event");
@ -89,12 +86,12 @@ function test1() {
}
function test2() {
// Make the iframe cover the whole window.
gIFrame.style.margin = "0";
gIFrame.style.width = gIFrame.style.height = "200px";
// Make the browser cover the whole window.
gBrowserElement.style.margin = "0";
gBrowserElement.style.width = gBrowserElement.style.height = "200px";
// Add a box to the iframe at the left edge.
var doc = gIFrame.contentDocument;
// Add a box to the browser at the left edge.
var doc = gBrowserElement.contentDocument;
var box = doc.createElement("div");
box.setAttribute("id", "box");
box.style.position = "absolute";
@ -105,7 +102,7 @@ function test2() {
box.style.backgroundColor = "green";
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
// events to happen. Note that those happen off the refresh driver,
@ -134,22 +131,22 @@ function test2() {
// Move the mouse over the box.
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.
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");
// De-activate the window (by activating the right window).
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");
// Re-activate it.
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");
// 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 () {
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");
finalize();
});