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

This commit is contained in:
Tiberius Oros 2018-02-09 12:05:42 +02:00
Родитель f1b581daa2 677b0037d2
Коммит 628c59112e
51 изменённых файлов: 1023 добавлений и 414 удалений

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

@ -114,45 +114,64 @@ ContentAreaDropListener.prototype =
// uriString is a valid URI, so do the security check.
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
getService(Ci.nsIScriptSecurityManager);
let sourceNode = dataTransfer.mozSourceNode;
let flags = secMan.STANDARD;
if (disallowInherit)
flags |= secMan.DISALLOW_INHERIT_PRINCIPAL;
let principal;
if (sourceNode) {
principal = this._getTriggeringPrincipalFromSourceNode(sourceNode);
} else {
// Use file:/// as the default uri so that drops of file URIs are always
// allowed.
principal = secMan.createCodebasePrincipal(ioService.newURI("file:///"), {});
}
let principal = this._getTriggeringPrincipalFromDataTransfer(dataTransfer, false);
secMan.checkLoadURIStrWithPrincipal(principal, uriString, flags);
return uriString;
},
_getTriggeringPrincipalFromSourceNode: function(aSourceNode)
_getTriggeringPrincipalFromDataTransfer: function(aDataTransfer,
fallbackToSystemPrincipal)
{
if (aSourceNode.localName == "browser" &&
aSourceNode.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul") {
return aSourceNode.contentPrincipal;
let sourceNode = aDataTransfer.mozSourceNode;
if (sourceNode &&
(sourceNode.localName !== "browser" ||
sourceNode.namespaceURI !== "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")) {
// Use sourceNode's principal only if the sourceNode is not browser.
//
// If sourceNode is browser, the actual triggering principal may be
// differ than sourceNode's principal, since sourceNode's principal is
// top level document's one and the drag may be triggered from a frame
// with different principal.
if (sourceNode.nodePrincipal) {
return sourceNode.nodePrincipal;
}
}
return aSourceNode.nodePrincipal;
// First, fallback to mozTriggeringPrincipalURISpec that is set when the
// drop comes from another content process.
let principalURISpec = aDataTransfer.mozTriggeringPrincipalURISpec;
if (!principalURISpec) {
// Fallback to either system principal or file principal, supposing
// the drop comes from outside of the browser, so that drops of file
// URIs are always allowed.
//
// TODO: Investigate and describe the difference between them,
// or use only one principal. (Bug 1367038)
if (fallbackToSystemPrincipal) {
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
getService(Ci.nsIScriptSecurityManager);
return secMan.getSystemPrincipal();
} else {
principalURISpec = "file:///";
}
}
let ioService = Cc["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
getService(Ci.nsIScriptSecurityManager);
return secMan.createCodebasePrincipal(ioService.newURI(principalURISpec), {});
},
getTriggeringPrincipal: function(aEvent)
{
let dataTransfer = aEvent.dataTransfer;
let sourceNode = dataTransfer.mozSourceNode;
if (sourceNode) {
return this._getTriggeringPrincipalFromSourceNode(sourceNode, false);
}
// Bug 1367038: mozSourceNode is null if the drag event originated
// in an external application - needs better fallback!
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
getService(Ci.nsIScriptSecurityManager);
return secMan.getSystemPrincipal();
return this._getTriggeringPrincipalFromDataTransfer(dataTransfer, true);
},
canDropLink: function(aEvent, aAllowSameDocument)

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

@ -72,7 +72,8 @@ public:
nsresult Produce(DataTransfer* aDataTransfer,
bool* aCanDrag,
nsISelection** aSelection,
nsIContent** aDragNode);
nsIContent** aDragNode,
nsACString& aPrincipalURISpec);
private:
void AddString(DataTransfer* aDataTransfer,
@ -119,7 +120,8 @@ nsContentAreaDragDrop::GetDragData(nsPIDOMWindowOuter* aWindow,
DataTransfer* aDataTransfer,
bool* aCanDrag,
nsISelection** aSelection,
nsIContent** aDragNode)
nsIContent** aDragNode,
nsACString& aPrincipalURISpec)
{
NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
@ -127,7 +129,8 @@ nsContentAreaDragDrop::GetDragData(nsPIDOMWindowOuter* aWindow,
DragDataProducer
provider(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode);
return provider.Produce(aDataTransfer, aCanDrag, aSelection, aDragNode,
aPrincipalURISpec);
}
@ -365,7 +368,8 @@ nsresult
DragDataProducer::Produce(DataTransfer* aDataTransfer,
bool* aCanDrag,
nsISelection** aSelection,
nsIContent** aDragNode)
nsIContent** aDragNode,
nsACString& aPrincipalURISpec)
{
NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode,
"null pointer passed to Produce");
@ -430,7 +434,7 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer,
if (tp) {
// We have a TabParent, so it may have data for dnd in case the child
// process started a dnd session.
tp->AddInitialDnDDataTo(aDataTransfer);
tp->AddInitialDnDDataTo(aDataTransfer, aPrincipalURISpec);
}
}
}

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

@ -50,6 +50,9 @@ public:
* selection is being dragged.
* aDragNode - [out] the link, image or area being dragged, or null if the
* drag occurred on another element.
* aPrincipalURISpec - [out] set to the URI of the triggering principal of
* the drag, or empty string if it's from
* browser chrome or OS
*/
static nsresult GetDragData(nsPIDOMWindowOuter* aWindow,
nsIContent* aTarget,
@ -58,7 +61,8 @@ public:
mozilla::dom::DataTransfer* aDataTransfer,
bool* aCanDrag,
nsISelection** aSelection,
nsIContent** aDragNode);
nsIContent** aDragNode,
nsACString& aPrincipalURISpec);
};
// this is used to save images to disk lazily when the image data is asked for

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

@ -307,6 +307,20 @@ DataTransfer::GetEffectAllowedInt(uint32_t* aEffectAllowed)
return NS_OK;
}
void
DataTransfer::GetMozTriggeringPrincipalURISpec(nsAString& aPrincipalURISpec)
{
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (!dragSession) {
aPrincipalURISpec.Truncate(0);
return;
}
nsCString principalURISpec;
dragSession->GetTriggeringPrincipalURISpec(principalURISpec);
CopyUTF8toUTF16(principalURISpec, aPrincipalURISpec);
}
NS_IMETHODIMP
DataTransfer::SetEffectAllowedInt(uint32_t aEffectAllowed)
{

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

@ -211,6 +211,8 @@ public:
already_AddRefed<nsINode> GetMozSourceNode();
void GetMozTriggeringPrincipalURISpec(nsAString& aPrincipalURISpec);
mozilla::dom::Element* GetDragTarget() const
{
return mDragTarget;

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

@ -1335,9 +1335,11 @@ EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
nsCString principalURISpec;
if (dragSession) {
dragSession->DragEventDispatchedToChildProcess();
dragSession->GetDragAction(&action);
dragSession->GetTriggeringPrincipalURISpec(principalURISpec);
nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
if (initialDataTransfer) {
@ -1345,7 +1347,8 @@ EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
}
}
tabParent->SendRealDragEvent(*aEvent->AsDragEvent(), action, dropEffect);
tabParent->SendRealDragEvent(*aEvent->AsDragEvent(), action, dropEffect,
principalURISpec);
return;
}
case ePluginEventClass: {
@ -1863,11 +1866,13 @@ EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
nsCOMPtr<nsISelection> selection;
nsCOMPtr<nsIContent> eventContent, targetContent;
nsCString principalURISpec;
mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent));
if (eventContent)
DetermineDragTargetAndDefaultData(window, eventContent, dataTransfer,
getter_AddRefs(selection),
getter_AddRefs(targetContent));
getter_AddRefs(targetContent),
principalURISpec);
// Stop tracking the drag gesture now. This should stop us from
// reentering GenerateDragGesture inside DOM event processing.
@ -1929,7 +1934,8 @@ EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
if (status != nsEventStatus_eConsumeNoDefault) {
bool dragStarted = DoDefaultDragStart(aPresContext, event, dataTransfer,
targetContent, selection);
targetContent, selection,
principalURISpec);
if (dragStarted) {
sActiveESM = nullptr;
MaybeFirePointerCancel(aEvent);
@ -1952,7 +1958,8 @@ EventStateManager::DetermineDragTargetAndDefaultData(nsPIDOMWindowOuter* aWindow
nsIContent* aSelectionTarget,
DataTransfer* aDataTransfer,
nsISelection** aSelection,
nsIContent** aTargetNode)
nsIContent** aTargetNode,
nsACString& aPrincipalURISpec)
{
*aTargetNode = nullptr;
@ -1967,7 +1974,8 @@ EventStateManager::DetermineDragTargetAndDefaultData(nsPIDOMWindowOuter* aWindow
nsresult rv = nsContentAreaDragDrop::GetDragData(aWindow, mGestureDownContent,
aSelectionTarget, wasAlt,
aDataTransfer, &canDrag, aSelection,
getter_AddRefs(dragDataNode));
getter_AddRefs(dragDataNode),
aPrincipalURISpec);
if (NS_FAILED(rv) || !canDrag)
return;
@ -2031,7 +2039,8 @@ EventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
WidgetDragEvent* aDragEvent,
DataTransfer* aDataTransfer,
nsIContent* aDragTarget,
nsISelection* aSelection)
nsISelection* aSelection,
const nsACString& aPrincipalURISpec)
{
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
@ -2110,7 +2119,9 @@ EventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
// use InvokeDragSessionWithImage if a custom image was set or something
// other than a selection is being dragged.
if (!dragImage && aSelection) {
dragService->InvokeDragSessionWithSelection(aSelection, transArray,
dragService->InvokeDragSessionWithSelection(aSelection,
aPrincipalURISpec,
transArray,
action, event, dataTransfer);
}
else {
@ -2133,7 +2144,8 @@ EventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
}
#endif
dragService->InvokeDragSessionWithImage(dragTarget->AsDOMNode(), transArray,
dragService->InvokeDragSessionWithImage(dragTarget->AsDOMNode(),
aPrincipalURISpec, transArray,
region, action,
dragImage ? dragImage->AsDOMNode() :
nullptr,

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

@ -925,12 +925,16 @@ protected:
* aDataTransfer - data transfer object that will contain the data to drag
* aSelection - [out] set to the selection to be dragged
* aTargetNode - [out] the draggable node, or null if there isn't one
* aPrincipalURISpec - [out] set to the URI of the triggering principal of
* the drag, or an empty string if it's from
* browser chrome or OS
*/
void DetermineDragTargetAndDefaultData(nsPIDOMWindowOuter* aWindow,
nsIContent* aSelectionTarget,
dom::DataTransfer* aDataTransfer,
nsISelection** aSelection,
nsIContent** aTargetNode);
nsIContent** aTargetNode,
nsACString& aPrincipalURISpec);
/*
* Perform the default handling for the dragstart event and set up a
@ -941,12 +945,15 @@ protected:
* aDataTransfer - the data transfer that holds the data to be dragged
* aDragTarget - the target of the drag
* aSelection - the selection to be dragged
* aPrincipalURISpec - the URI of the triggering principal of the drag,
* or an empty string if it's from browser chrome or OS
*/
bool DoDefaultDragStart(nsPresContext* aPresContext,
WidgetDragEvent* aDragEvent,
dom::DataTransfer* aDataTransfer,
nsIContent* aDragTarget,
nsISelection* aSelection);
nsISelection* aSelection,
const nsACString& aPrincipalURISpec);
bool IsTrackingDragGesture ( ) const { return mGestureDownContent != nullptr; }
/**

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

@ -126,6 +126,7 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = {
"javascript.options.parallel_parsing",
"javascript.options.shared_memory",
"javascript.options.spectre.index_masking",
"javascript.options.spectre.string_mitigations",
"javascript.options.streams",
"javascript.options.strict",
"javascript.options.strict.debug",

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

@ -566,7 +566,8 @@ parent:
async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
OptionalShmem visualData,
uint32_t stride, uint8_t format,
LayoutDeviceIntRect dragRect);
LayoutDeviceIntRect dragRect,
nsCString principalURISpec);
// After a compositor reset, it is necessary to reconnect each layers ID to
// the compositor of the widget that will render those layers. Note that
@ -705,7 +706,7 @@ child:
* don't need support RealDragEvent with input priority.
*/
async RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction,
uint32_t aDropEffect);
uint32_t aDropEffect, nsCString aPrincipalURISpec);
async PluginEvent(WidgetPluginEvent aEvent);

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

@ -1948,7 +1948,8 @@ TabChild::RecvNormalPriorityRealTouchMoveEvent(
mozilla::ipc::IPCResult
TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent,
const uint32_t& aDragAction,
const uint32_t& aDropEffect)
const uint32_t& aDropEffect,
const nsCString& aPrincipalURISpec)
{
WidgetDragEvent localEvent(aEvent);
localEvent.mWidget = mPuppetWidget;
@ -1956,6 +1957,7 @@ TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent,
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (dragSession) {
dragSession->SetDragAction(aDragAction);
dragSession->SetTriggeringPrincipalURISpec(aPrincipalURISpec);
nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
if (initialDataTransfer) {

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

@ -391,7 +391,8 @@ public:
virtual mozilla::ipc::IPCResult RecvRealDragEvent(const WidgetDragEvent& aEvent,
const uint32_t& aDragAction,
const uint32_t& aDropEffect) override;
const uint32_t& aDropEffect,
const nsCString& aPrincipalURISpec) override;
virtual mozilla::ipc::IPCResult
RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;

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

@ -1281,7 +1281,8 @@ TabParent::QueryDropLinksForVerification()
void
TabParent::SendRealDragEvent(WidgetDragEvent& aEvent, uint32_t aDragAction,
uint32_t aDropEffect)
uint32_t aDropEffect,
const nsCString& aPrincipalURISpec)
{
if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
return;
@ -1294,7 +1295,8 @@ TabParent::SendRealDragEvent(WidgetDragEvent& aEvent, uint32_t aDragAction,
}
}
DebugOnly<bool> ret =
PBrowserParent::SendRealDragEvent(aEvent, aDragAction, aDropEffect);
PBrowserParent::SendRealDragEvent(aEvent, aDragAction, aDropEffect,
aPrincipalURISpec);
NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealDragEvent() failed");
MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
}
@ -3345,7 +3347,8 @@ TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
const uint32_t& aAction,
const OptionalShmem& aVisualDnDData,
const uint32_t& aStride, const uint8_t& aFormat,
const LayoutDeviceIntRect& aDragRect)
const LayoutDeviceIntRect& aDragRect,
const nsCString& aPrincipalURISpec)
{
mInitialDataTransferItems.Clear();
nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell();
@ -3387,6 +3390,7 @@ TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
mDragValid = true;
mDragRect = aDragRect;
mDragPrincipalURISpec = aPrincipalURISpec;
esm->BeginTrackingRemoteDragGesture(mFrameElement);
@ -3398,8 +3402,22 @@ TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
}
void
TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer,
nsACString& aPrincipalURISpec)
{
aPrincipalURISpec.Assign(mDragPrincipalURISpec);
nsCOMPtr<nsIPrincipal> principal;
if (!mDragPrincipalURISpec.IsEmpty()) {
// If principal is given, try using it first.
principal = BasePrincipal::CreateCodebasePrincipal(mDragPrincipalURISpec);
}
if (!principal) {
// Fallback to system principal, to handle like the data is from browser
// chrome or OS.
principal = nsContentUtils::GetSystemPrincipal();
}
for (uint32_t i = 0; i < mInitialDataTransferItems.Length(); ++i) {
nsTArray<IPCDataTransferItem>& itemArray = mInitialDataTransferItems[i];
for (auto& item : itemArray) {
@ -3435,19 +3453,17 @@ TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
mozilla::Unused << DeallocShmem(item.data().get_Shmem());
}
// Using system principal here, since once the data is on parent process
// side, it can be handled as being from browser chrome or OS.
// We set aHidden to false, as we don't need to worry about hiding data
// from content in the parent process where there is no content.
// XXX: Nested Content Processes may change this
aDataTransfer->SetDataWithPrincipalFromOtherProcess(NS_ConvertUTF8toUTF16(item.flavor()),
variant, i,
nsContentUtils::GetSystemPrincipal(),
principal,
/* aHidden = */ false);
}
}
mInitialDataTransferItems.Clear();
mDragPrincipalURISpec.Truncate(0);
}
bool

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

@ -442,7 +442,8 @@ public:
void SendRealDragEvent(WidgetDragEvent& aEvent,
uint32_t aDragAction,
uint32_t aDropEffect);
uint32_t aDropEffect,
const nsCString& aPrincipalURISpec);
void SendMouseWheelEvent(WidgetWheelEvent& aEvent);
@ -585,9 +586,11 @@ public:
const uint32_t& aAction,
const OptionalShmem& aVisualDnDData,
const uint32_t& aStride, const uint8_t& aFormat,
const LayoutDeviceIntRect& aDragRect) override;
const LayoutDeviceIntRect& aDragRect,
const nsCString& aPrincipalURISpec) override;
void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
void AddInitialDnDDataTo(DataTransfer* aDataTransfer,
nsACString& aPrincipalURISpec);
bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
LayoutDeviceIntRect* aDragRect);
@ -694,6 +697,7 @@ private:
RefPtr<gfx::DataSourceSurface> mDnDVisualization;
bool mDragValid;
LayoutDeviceIntRect mDragRect;
nsCString mDragPrincipalURISpec;
// When true, the TabParent is initialized without child side's request.
// When false, the TabParent is initialized by window.open() from child side.

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

@ -156,6 +156,14 @@ partial interface DataTransfer {
[UseCounter]
readonly attribute Node? mozSourceNode;
/**
* The URI spec of the triggering principal. This may be different than
* sourceNode's principal when sourceNode is xul:browser and the drag is
* triggered in a browsing context inside it.
*/
[ChromeOnly]
readonly attribute DOMString mozTriggeringPrincipalURISpec;
/**
* Copy the given DataTransfer for the given event. Used by testing code for
* creating emulated Drag and Drop events in the UI.

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

@ -313,6 +313,9 @@ ScaledFontFontconfig::GetWRFontInstanceOptions(Maybe<wr::FontInstanceOptions>* a
if (FcPatternGetBool(mPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) == FcResultMatch && bitmap) {
options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS;
}
// FIXME: Cairo-FT metrics are not compatible with subpixel positioning.
options.subpx_dir = wr::SubpixelDirection::None;
} else {
options.render_mode = wr::FontRenderMode::Mono;
options.subpx_dir = wr::SubpixelDirection::None;

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

@ -995,6 +995,10 @@ void qcms_transform_release(qcms_transform *t)
free(t->output_gamma_lut_g);
free(t->output_gamma_lut_b);
/* r_clut points to beginning of buffer allocated in qcms_transform_precacheLUT_float */
if (t->r_clut)
free(t->r_clut);
transform_free(t);
}
@ -1191,6 +1195,7 @@ qcms_transform* qcms_transform_precacheLUT_float(qcms_transform *transform, qcms
//XXX: qcms_modular_transform_data may return either the src or dest buffer. If so it must not be free-ed
// It will be stored in r_clut, which will be cleaned up in qcms_transform_release.
if (src && lut != src) {
free(src);
}

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

@ -1338,7 +1338,7 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
masm.branch32(Assembler::AboveOrEqual, lastIndex, temp2, &done);
// Check if input[lastIndex] is trail surrogate.
masm.loadStringChars(input, temp2);
masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
masm.computeEffectiveAddress(BaseIndex(temp2, lastIndex, TimesTwo), temp3);
masm.load16ZeroExtend(Address(temp3, 0), temp3);
@ -1374,20 +1374,22 @@ PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Re
// the input start/end pointers in the InputOutputData.
Register codePointer = temp1;
{
masm.loadStringChars(input, temp2);
masm.storePtr(temp2, inputStartAddress);
masm.loadStringLength(input, temp3);
Label isLatin1, done;
masm.branchLatin1String(input, &isLatin1);
{
masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
masm.storePtr(temp2, inputStartAddress);
masm.lshiftPtr(Imm32(1), temp3);
masm.loadPtr(Address(temp1, RegExpShared::offsetOfTwoByteJitCode(mode)),
codePointer);
masm.jump(&done);
}
masm.jump(&done);
masm.bind(&isLatin1);
{
masm.bind(&isLatin1);
masm.loadStringChars(input, temp2, CharEncoding::Latin1);
masm.storePtr(temp2, inputStartAddress);
masm.loadPtr(Address(temp1, RegExpShared::offsetOfLatin1JitCode(mode)),
codePointer);
}
@ -1580,15 +1582,16 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
newStartIndexAddress.offset += 2 * sizeof(void*);
// Load chars pointer for the new string.
masm.addPtr(ImmWord(JSInlineString::offsetOfInlineStorage()), string);
masm.loadInlineStringCharsForStore(string, string);
// Load the source characters pointer.
masm.loadStringChars(base, base);
masm.load32(newStartIndexAddress, temp2);
masm.loadStringChars(base, temp2,
latin1 ? CharEncoding::Latin1 : CharEncoding::TwoByte);
masm.load32(newStartIndexAddress, base);
if (latin1)
masm.addPtr(temp2, base);
else
masm.computeEffectiveAddress(BaseIndex(base, temp2, TimesTwo), base);
masm.computeEffectiveAddress(BaseIndex(temp2, base, TimesTwo), base);
CopyStringChars(masm, string, base, temp1, temp2, latin1 ? 1 : 2, latin1 ? 1 : 2);
@ -1614,14 +1617,15 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
masm.store32(Imm32(flags), Address(string, JSString::offsetOfFlags()));
masm.store32(temp1, Address(string, JSString::offsetOfLength()));
masm.loadPtr(Address(base, JSString::offsetOfNonInlineChars()), temp1);
masm.loadNonInlineStringChars(base, temp1,
latin1 ? CharEncoding::Latin1 : CharEncoding::TwoByte);
masm.load32(startIndexAddress, temp2);
if (latin1)
masm.addPtr(temp2, temp1);
else
masm.computeEffectiveAddress(BaseIndex(temp1, temp2, TimesTwo), temp1);
masm.storePtr(temp1, Address(string, JSString::offsetOfNonInlineChars()));
masm.storePtr(base, Address(string, JSDependentString::offsetOfBase()));
masm.storeNonInlineStringChars(temp1, string);
masm.storeDependentStringBase(base, string);
// Follow any base pointer if the input is itself a dependent string.
// Watch for undepended strings, which have a base pointer but don't
@ -1630,8 +1634,8 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
masm.load32(Address(base, JSString::offsetOfFlags()), temp1);
masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), temp1);
masm.branch32(Assembler::NotEqual, temp1, Imm32(JSString::DEPENDENT_FLAGS), &noBase);
masm.loadPtr(Address(base, JSDependentString::offsetOfBase()), temp1);
masm.storePtr(temp1, Address(string, JSDependentString::offsetOfBase()));
masm.loadDependentStringBase(base, temp1);
masm.storeDependentStringBase(temp1, string);
masm.bind(&noBase);
}
@ -2451,8 +2455,6 @@ static void
FindFirstDollarIndex(MacroAssembler& masm, Register str, Register len, Register chars,
Register temp, Register output, bool isLatin1)
{
masm.loadStringChars(str, chars);
masm.move32(Imm32(0), output);
Label start, done;
@ -2494,11 +2496,13 @@ CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins)
Label isLatin1, done;
masm.branchLatin1String(str, &isLatin1);
{
masm.loadStringChars(str, temp0, CharEncoding::TwoByte);
FindFirstDollarIndex(masm, str, len, temp0, temp1, output, /* isLatin1 = */ false);
masm.jump(&done);
}
masm.jump(&done);
masm.bind(&isLatin1);
{
masm.bind(&isLatin1);
masm.loadStringChars(str, temp0, CharEncoding::Latin1);
FindFirstDollarIndex(masm, str, len, temp0, temp1, output, /* isLatin1 = */ true);
}
masm.bind(&done);
@ -7774,13 +7778,15 @@ CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, Register destC
masm.loadStringLength(input, temp1);
masm.branchLatin1String(input, &isLatin1);
{
masm.loadStringChars(input, input);
masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
masm.movePtr(temp2, input);
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char16_t), sizeof(char16_t));
masm.jump(&done);
}
masm.bind(&isLatin1);
{
masm.loadStringChars(input, input);
masm.loadStringChars(input, temp2, CharEncoding::Latin1);
masm.movePtr(temp2, input);
CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char), sizeof(char16_t));
}
masm.bind(&done);
@ -7828,7 +7834,7 @@ ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register ou
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
// Load chars pointer in temp2.
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()), temp2);
masm.loadInlineStringCharsForStore(output, temp2);
{
// Copy lhs chars. Note that this advances temp2 to point to the next
@ -7837,7 +7843,8 @@ ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register ou
CopyStringCharsMaybeInflate(masm, lhs, temp2, temp1, temp3);
} else {
masm.loadStringLength(lhs, temp3);
masm.loadStringChars(lhs, lhs);
masm.loadStringChars(lhs, temp1, CharEncoding::Latin1);
masm.movePtr(temp1, lhs);
CopyStringChars(masm, temp2, lhs, temp3, temp1, sizeof(char), sizeof(char));
}
@ -7846,7 +7853,8 @@ ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, Register ou
CopyStringCharsMaybeInflate(masm, rhs, temp2, temp1, temp3);
} else {
masm.loadStringLength(rhs, temp3);
masm.loadStringChars(rhs, rhs);
masm.loadStringChars(rhs, temp1, CharEncoding::Latin1);
masm.movePtr(temp1, rhs);
CopyStringChars(masm, temp2, rhs, temp3, temp1, sizeof(char), sizeof(char));
}
@ -7907,19 +7915,17 @@ CodeGenerator::visitSubstr(LSubstr* lir)
masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::INLINE_CHARS_BIT), &notInline);
masm.newGCFatInlineString(output, temp, slowPath);
masm.store32(length, Address(output, JSString::offsetOfLength()));
Address stringStorage(string, JSInlineString::offsetOfInlineStorage());
Address outputStorage(output, JSInlineString::offsetOfInlineStorage());
masm.branchLatin1String(string, &isInlinedLatin1);
{
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
Address(output, JSString::offsetOfFlags()));
masm.computeEffectiveAddress(stringStorage, temp);
masm.loadInlineStringChars(string, temp, CharEncoding::TwoByte);
if (temp2 == string)
masm.push(string);
BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
masm.computeEffectiveAddress(chars, temp2);
masm.computeEffectiveAddress(outputStorage, temp);
masm.loadInlineStringCharsForStore(output, temp);
CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
masm.load32(Address(output, JSString::offsetOfLength()), length);
masm.store16(Imm32(0), Address(temp, 0));
@ -7931,12 +7937,16 @@ CodeGenerator::visitSubstr(LSubstr* lir)
{
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
Address(output, JSString::offsetOfFlags()));
if (temp2 == string)
if (temp2 == string) {
masm.push(string);
masm.computeEffectiveAddress(stringStorage, temp2);
masm.loadInlineStringChars(string, temp, CharEncoding::Latin1);
masm.movePtr(temp, temp2);
} else {
masm.loadInlineStringChars(string, temp2, CharEncoding::Latin1);
}
static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
masm.addPtr(begin, temp2);
masm.computeEffectiveAddress(outputStorage, temp);
masm.loadInlineStringCharsForStore(output, temp);
CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
masm.load32(Address(output, JSString::offsetOfLength()), length);
masm.store8(Imm32(0), Address(temp, 0));
@ -7949,25 +7959,25 @@ CodeGenerator::visitSubstr(LSubstr* lir)
masm.bind(&notInline);
masm.newGCString(output, temp, slowPath);
masm.store32(length, Address(output, JSString::offsetOfLength()));
masm.storePtr(string, Address(output, JSDependentString::offsetOfBase()));
masm.storeDependentStringBase(string, output);
masm.branchLatin1String(string, &isLatin1);
{
masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags()));
masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
masm.loadNonInlineStringChars(string, temp, CharEncoding::TwoByte);
BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
masm.computeEffectiveAddress(chars, temp);
masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
masm.storeNonInlineStringChars(temp, output);
masm.jump(done);
}
masm.bind(&isLatin1);
{
masm.store32(Imm32(JSString::DEPENDENT_FLAGS | JSString::LATIN1_CHARS_BIT),
Address(output, JSString::offsetOfFlags()));
masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
masm.loadNonInlineStringChars(string, temp, CharEncoding::Latin1);
static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
masm.addPtr(begin, temp);
masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
masm.storeNonInlineStringChars(temp, output);
masm.jump(done);
}
@ -8039,8 +8049,7 @@ JitCompartment::generateStringConcatStub(JSContext* cx)
masm.store32(temp2, Address(output, JSString::offsetOfLength()));
// Store left and right nodes.
masm.storePtr(lhs, Address(output, JSRope::offsetOfLeft()));
masm.storePtr(rhs, Address(output, JSRope::offsetOfRight()));
masm.storeRopeChildren(lhs, rhs, output);
masm.ret();
masm.bind(&leftEmpty);
@ -8337,8 +8346,7 @@ CodeGenerator::visitFromCodePoint(LFromCodePoint* lir)
masm.store32(Imm32(1), Address(output, JSString::offsetOfLength()));
// Load chars pointer in temp1.
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()),
temp1);
masm.loadInlineStringCharsForStore(output, temp1);
masm.store16(codePoint, Address(temp1, 0));
@ -8353,8 +8361,7 @@ CodeGenerator::visitFromCodePoint(LFromCodePoint* lir)
masm.store32(Imm32(2), Address(output, JSString::offsetOfLength()));
// Load chars pointer in temp1.
masm.computeEffectiveAddress(Address(output, JSInlineString::offsetOfInlineStorage()),
temp1);
masm.loadInlineStringCharsForStore(output, temp1);
// Inlined unicode::LeadSurrogate(uint32_t).
masm.move32(codePoint, temp2);

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

@ -233,6 +233,7 @@ DefaultJitOptions::DefaultJitOptions()
}
SET_DEFAULT(spectreIndexMasking, true);
SET_DEFAULT(spectreStringMitigations, false);
// Toggles whether unboxed plain objects can be created by the VM.
SET_DEFAULT(disableUnboxedObjects, false);

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

@ -95,6 +95,7 @@ struct DefaultJitOptions
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
bool spectreIndexMasking;
bool spectreStringMitigations;
// The options below affect the rest of the VM, and not just the JIT.
bool disableUnboxedObjects;

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

@ -1367,19 +1367,155 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register
}
void
MacroAssembler::loadStringChars(Register str, Register dest)
MacroAssembler::loadStringChars(Register str, Register dest, CharEncoding encoding)
{
Label isInline, done;
branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
Imm32(JSString::INLINE_CHARS_BIT), &isInline);
MOZ_ASSERT(str != dest);
loadPtr(Address(str, JSString::offsetOfNonInlineChars()), dest);
jump(&done);
if (JitOptions.spectreStringMitigations) {
if (encoding == CharEncoding::Latin1) {
// If the string is a rope, zero the |str| register. The code below
// depends on str->flags so this should block speculative execution.
movePtr(ImmWord(0), dest);
test32MovePtr(Assembler::Zero,
Address(str, JSString::offsetOfFlags()), Imm32(JSString::LINEAR_BIT),
dest, str);
} else {
// If we're loading TwoByte chars, there's an additional risk:
// if the string has Latin1 chars, we could read out-of-bounds. To
// prevent this, we check both the Linear and Latin1 bits. We don't
// have a scratch register, so we use these flags also to block
// speculative execution, similar to the use of 0 above.
MOZ_ASSERT(encoding == CharEncoding::TwoByte);
static constexpr uint32_t Mask = JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT;
static_assert(Mask < 1024,
"Mask should be a small, near-null value to ensure we "
"block speculative execution when it's used as string "
"pointer");
move32(Imm32(Mask), dest);
and32(Address(str, JSString::offsetOfFlags()), dest);
cmp32MovePtr(Assembler::NotEqual, dest, Imm32(JSString::LINEAR_BIT),
dest, str);
}
}
bind(&isInline);
// Load the inline chars.
computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()), dest);
bind(&done);
// If it's not an inline string, load the non-inline chars. Use a
// conditional move to prevent speculative execution.
test32LoadPtr(Assembler::Zero,
Address(str, JSString::offsetOfFlags()), Imm32(JSString::INLINE_CHARS_BIT),
Address(str, JSString::offsetOfNonInlineChars()), dest);
}
void
MacroAssembler::loadNonInlineStringChars(Register str, Register dest, CharEncoding encoding)
{
MOZ_ASSERT(str != dest);
if (JitOptions.spectreStringMitigations) {
// If the string is a rope, has inline chars, or has a different
// character encoding, set str to a near-null value to prevent
// speculative execution below (when reading str->nonInlineChars).
static constexpr uint32_t Mask =
JSString::LINEAR_BIT |
JSString::INLINE_CHARS_BIT |
JSString::LATIN1_CHARS_BIT;
static_assert(Mask < 1024,
"Mask should be a small, near-null value to ensure we "
"block speculative execution when it's used as string "
"pointer");
uint32_t expectedBits = JSString::LINEAR_BIT;
if (encoding == CharEncoding::Latin1)
expectedBits |= JSString::LATIN1_CHARS_BIT;
move32(Imm32(Mask), dest);
and32(Address(str, JSString::offsetOfFlags()), dest);
cmp32MovePtr(Assembler::NotEqual, dest, Imm32(expectedBits),
dest, str);
}
loadPtr(Address(str, JSString::offsetOfNonInlineChars()), dest);
}
void
MacroAssembler::storeNonInlineStringChars(Register chars, Register str)
{
MOZ_ASSERT(chars != str);
storePtr(chars, Address(str, JSString::offsetOfNonInlineChars()));
}
void
MacroAssembler::loadInlineStringCharsForStore(Register str, Register dest)
{
computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()), dest);
}
void
MacroAssembler::loadInlineStringChars(Register str, Register dest, CharEncoding encoding)
{
MOZ_ASSERT(str != dest);
if (JitOptions.spectreStringMitigations) {
// Making this Spectre-safe is a bit complicated: using
// computeEffectiveAddress and then zeroing the output register if
// non-inline is not sufficient: when the index is very large, it would
// allow reading |nullptr + index|. Just fall back to loadStringChars
// for now.
loadStringChars(str, dest, encoding);
} else {
computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()), dest);
}
}
void
MacroAssembler::loadRopeLeftChild(Register str, Register dest)
{
MOZ_ASSERT(str != dest);
if (JitOptions.spectreStringMitigations) {
// Zero the output register if the input was not a rope.
movePtr(ImmWord(0), dest);
test32LoadPtr(Assembler::Zero,
Address(str, JSString::offsetOfFlags()), Imm32(JSString::LINEAR_BIT),
Address(str, JSRope::offsetOfLeft()), dest);
} else {
loadPtr(Address(str, JSRope::offsetOfLeft()), dest);
}
}
void
MacroAssembler::storeRopeChildren(Register left, Register right, Register str)
{
storePtr(left, Address(str, JSRope::offsetOfLeft()));
storePtr(right, Address(str, JSRope::offsetOfRight()));
}
void
MacroAssembler::loadDependentStringBase(Register str, Register dest)
{
MOZ_ASSERT(str != dest);
if (JitOptions.spectreStringMitigations) {
// If the string does not have a base-string, zero the |str| register.
// The code below loads str->base so this should block speculative
// execution.
movePtr(ImmWord(0), dest);
test32MovePtr(Assembler::Zero,
Address(str, JSString::offsetOfFlags()), Imm32(JSString::HAS_BASE_BIT),
dest, str);
}
loadPtr(Address(str, JSDependentString::offsetOfBase()), dest);
}
void
MacroAssembler::storeDependentStringBase(Register base, Register str)
{
storePtr(base, Address(str, JSDependentString::offsetOfBase()));
}
void
@ -1397,8 +1533,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output, Re
Label notRope;
branchIfNotRope(str, &notRope);
// Load leftChild.
loadPtr(Address(str, JSRope::offsetOfLeft()), output);
loadRopeLeftChild(str, output);
// Check if the index is contained in the leftChild.
// Todo: Handle index in the rightChild.
@ -1413,14 +1548,13 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output, Re
// We have to check the left/right side for ropes,
// because a TwoByte rope might have a Latin1 child.
branchLatin1String(output, &isLatin1);
loadStringChars(output, output);
load16ZeroExtend(BaseIndex(output, index, TimesTwo), output);
loadStringChars(output, scratch, CharEncoding::TwoByte);
load16ZeroExtend(BaseIndex(scratch, index, TimesTwo), output);
jump(&done);
bind(&isLatin1);
loadStringChars(output, output);
load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
loadStringChars(output, scratch, CharEncoding::Latin1);
load8ZeroExtend(BaseIndex(scratch, index, TimesOne), output);
bind(&done);
}

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

@ -228,6 +228,8 @@ enum class CheckUnsafeCallWithABI {
DontCheckOther,
};
enum class CharEncoding { Latin1, TwoByte };
// The public entrypoint for emitting assembly. Note that a MacroAssembler can
// use cx->lifoAlloc, so take care not to interleave masm use with other
// lifoAlloc use if one will be destroyed before the other.
@ -1368,6 +1370,18 @@ class MacroAssembler : public MacroAssemblerSpecific
Register dest)
DEFINED_ON(arm, arm64, x86_shared);
inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, Register src,
Register dest)
DEFINED_ON(arm, arm64, x86, x64);
inline void test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, const Address& src,
Register dest)
DEFINED_ON(arm, arm64, x86, x64);
inline void test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
Register dest)
DEFINED_ON(arm, arm64, x86, x64);
// Performs a bounds check and zeroes the index register if out-of-bounds
// (to mitigate Spectre).
inline void boundsCheck32ForLoad(Register index, Register length, Register scratch,
@ -1954,10 +1968,24 @@ class MacroAssembler : public MacroAssemblerSpecific
load32(Address(str, JSString::offsetOfLength()), dest);
}
void loadStringChars(Register str, Register dest);
void loadStringChars(Register str, Register dest, CharEncoding encoding);
void loadNonInlineStringChars(Register str, Register dest, CharEncoding encoding);
void loadNonInlineStringCharsForStore(Register str, Register dest);
void storeNonInlineStringChars(Register chars, Register str);
void loadInlineStringChars(Register str, Register dest, CharEncoding encoding);
void loadInlineStringCharsForStore(Register str, Register dest);
void loadStringChar(Register str, Register index, Register output, Register scratch,
Label* fail);
void loadRopeLeftChild(Register str, Register dest);
void storeRopeChildren(Register left, Register right, Register str);
void loadDependentStringBase(Register str, Register dest);
void storeDependentStringBase(Register base, Register str);
void loadStringIndexValue(Register str, Register dest, Label* fail);
void loadJSContext(Register dest);

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

@ -2144,6 +2144,14 @@ MacroAssembler::cmp32Move32(Condition cond, Register lhs, Register rhs, Register
ma_mov(src, dest, LeaveCC, cond);
}
void
MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, Register src,
Register dest)
{
cmp32(lhs, rhs);
ma_mov(src, dest, LeaveCC, cond);
}
void
MacroAssembler::cmp32Move32(Condition cond, Register lhs, const Address& rhs, Register src,
Register dest)
@ -2154,6 +2162,25 @@ MacroAssembler::cmp32Move32(Condition cond, Register lhs, const Address& rhs, Re
cmp32Move32(cond, lhs, scratch, src, dest);
}
void
MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, const Address& src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
ScratchRegisterScope scratch(*this);
ma_ldr(src, dest, scratch, Offset, cond);
}
void
MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
ma_mov(src, dest, LeaveCC, cond);
}
void
MacroAssembler::boundsCheck32ForLoad(Register index, Register length, Register scratch,
Label* failure)

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

@ -1726,6 +1726,40 @@ MacroAssembler::cmp32Move32(Condition cond, Register lhs, const Address& rhs, Re
Csel(ARMRegister(dest, 32), ARMRegister(src, 32), ARMRegister(dest, 32), cond);
}
void
MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, Register src,
Register dest)
{
cmp32(lhs, rhs);
Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64), cond);
}
void
MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, const Address& src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
// ARM64 does not support conditional loads, so we use a branch with a CSel
// (to prevent Spectre attacks).
vixl::UseScratchRegisterScope temps(this);
const ARMRegister scratch64 = temps.AcquireX();
Label done;
branchTest32(Assembler::InvertCondition(cond), addr, mask, &done);
loadPtr(src, scratch64.asUnsized());
Csel(ARMRegister(dest, 64), scratch64, ARMRegister(dest, 64), cond);
bind(&done);
}
void
MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
Csel(ARMRegister(dest, 64), ARMRegister(src, 64), ARMRegister(dest, 64), cond);
}
void
MacroAssembler::boundsCheck32ForLoad(Register index, Register length, Register scratch,
Label* failure)

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

@ -820,6 +820,32 @@ MacroAssembler::branchToComputedAddress(const BaseIndex& address)
jmp(Operand(address));
}
void
MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, Register src,
Register dest)
{
cmp32(lhs, rhs);
cmovCCq(cond, Operand(src), dest);
}
void
MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, const Address& src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
cmovCCq(cond, Operand(src), dest);
}
void
MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
cmovCCq(cond, Operand(src), dest);
}
// ========================================================================
// Truncate floating point.

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

@ -1009,6 +1009,32 @@ MacroAssembler::branchToComputedAddress(const BaseIndex& addr)
jmp(Operand(addr));
}
void
MacroAssembler::cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, Register src,
Register dest)
{
cmp32(lhs, rhs);
cmovCCl(cond, Operand(src), dest);
}
void
MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, Imm32 mask, const Address& src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
cmovCCl(cond, Operand(src), dest);
}
void
MacroAssembler::test32MovePtr(Condition cond, const Address& addr, Imm32 mask, Register src,
Register dest)
{
MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero);
test32(addr, mask);
cmovCCl(cond, Operand(src), dest);
}
// ========================================================================
// Truncate floating point.

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

@ -7256,6 +7256,9 @@ JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t v
case JSJITCOMPILER_SPECTRE_INDEX_MASKING:
jit::JitOptions.spectreIndexMasking = !!value;
break;
case JSJITCOMPILER_SPECTRE_STRING_MITIGATIONS:
jit::JitOptions.spectreStringMitigations = !!value;
break;
case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE:
jit::JitOptions.asmJSAtomicsEnable = !!value;
break;

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

@ -5912,7 +5912,8 @@ JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled);
Register(JUMP_THRESHOLD, "jump-threshold") \
Register(SIMULATOR_ALWAYS_INTERRUPT, "simulator.always-interrupt") \
Register(SPECTRE_INDEX_MASKING, "spectre.index-masking") \
Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \
Register(SPECTRE_STRING_MITIGATIONS, "spectre.string-mitigations") \
Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \
Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets")
typedef enum JSJitCompilerOption {

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

@ -8523,12 +8523,15 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
}
if (const char* str = op.getStringOption("spectre-mitigations")) {
if (strcmp(str, "on") == 0)
if (strcmp(str, "on") == 0) {
jit::JitOptions.spectreIndexMasking = true;
else if (strcmp(str, "off") == 0)
jit::JitOptions.spectreStringMitigations = true;
} else if (strcmp(str, "off") == 0) {
jit::JitOptions.spectreIndexMasking = false;
else
jit::JitOptions.spectreStringMitigations = false;
} else {
return OptionFailure("spectre-mitigations", str);
}
}
if (const char* str = op.getStringOption("ion-scalar-replacement")) {

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

@ -516,6 +516,10 @@ class JSString : public js::gc::TenuredCell
return offsetof(JSString, d.u1.flags);
}
private:
// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
// to call the method below.
friend class js::jit::MacroAssembler;
static size_t offsetOfNonInlineChars() {
static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) ==
offsetof(JSString, d.s.u2.nonInlineCharsLatin1),
@ -523,6 +527,7 @@ class JSString : public js::gc::TenuredCell
return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte);
}
public:
static const JS::TraceKind TraceKind = JS::TraceKind::String;
#ifdef DEBUG
@ -610,16 +615,21 @@ class JSRope : public JSString
void traceChildren(JSTracer* trc);
#ifdef DEBUG
void dumpRepresentation(js::GenericPrinter& out, int indent) const;
#endif
private:
// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
// to call the methods below.
friend class js::jit::MacroAssembler;
static size_t offsetOfLeft() {
return offsetof(JSRope, d.s.u2.left);
}
static size_t offsetOfRight() {
return offsetof(JSRope, d.s.u3.right);
}
#ifdef DEBUG
void dumpRepresentation(js::GenericPrinter& out, int indent) const;
#endif
};
static_assert(sizeof(JSRope) == sizeof(JSString),
@ -742,13 +752,18 @@ class JSDependentString : public JSLinearString
static inline JSLinearString* new_(JSContext* cx, JSLinearString* base,
size_t start, size_t length);
inline static size_t offsetOfBase() {
return offsetof(JSDependentString, d.s.u3.base);
}
#ifdef DEBUG
void dumpRepresentation(js::GenericPrinter& out, int indent) const;
#endif
private:
// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
// to call the method below.
friend class js::jit::MacroAssembler;
inline static size_t offsetOfBase() {
return offsetof(JSDependentString, d.s.u3.base);
}
};
static_assert(sizeof(JSDependentString) == sizeof(JSString),
@ -880,13 +895,17 @@ class JSInlineString : public JSFlatString
template<typename CharT>
static bool lengthFits(size_t length);
static size_t offsetOfInlineStorage() {
return offsetof(JSInlineString, d.inlineStorageTwoByte);
}
#ifdef DEBUG
void dumpRepresentation(js::GenericPrinter& out, int indent) const;
#endif
private:
// To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
// to call the method below.
friend class js::jit::MacroAssembler;
static size_t offsetOfInlineStorage() {
return offsetof(JSInlineString, d.inlineStorageTwoByte);
}
};
static_assert(sizeof(JSInlineString) == sizeof(JSString),

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

@ -809,6 +809,8 @@ ReloadPrefsCallback(const char* pref, void* data)
bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
bool spectreIndexMasking = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.index_masking");
bool spectreStringMitigations =
Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.string_mitigations");
sSharedMemoryEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory");
@ -871,6 +873,8 @@ ReloadPrefsCallback(const char* pref, void* data)
#endif
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_INDEX_MASKING, spectreIndexMasking);
JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_STRING_MITIGATIONS,
spectreStringMitigations);
}
XPCJSContext::~XPCJSContext()

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

@ -3631,6 +3631,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
PaintFrameFlags aFlags)
{
AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS);
typedef RetainedDisplayListBuilder::PartialUpdateResult PartialUpdateResult;
#ifdef MOZ_DUMP_PAINTING
if (!gPaintCountStack) {
@ -3788,6 +3789,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
}
nsRect visibleRect = visibleRegion.GetBounds();
PartialUpdateResult updateState = PartialUpdateResult::Failed;
{
AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame:BuildDisplayList",
@ -3837,22 +3839,20 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
// Attempt to do a partial build and merge into the existing list.
// This calls BuildDisplayListForStacking context on a subset of the
// viewport.
bool merged = false;
if (useRetainedBuilder) {
if (gfxPrefs::LayoutVerifyRetainDisplayList()) {
beforeMergeChecker.Set(&list, "BM");
}
merged = retainedBuilder->AttemptPartialUpdate(
updateState = retainedBuilder->AttemptPartialUpdate(
aBackstop, beforeMergeChecker ? &toBeMergedChecker : nullptr);
if (merged && beforeMergeChecker) {
if ((updateState != PartialUpdateResult::Failed) && beforeMergeChecker) {
afterMergeChecker.Set(&list, "AM");
}
}
if (merged &&
if ((updateState != PartialUpdateResult::Failed) &&
(gfxPrefs::LayoutDisplayListBuildTwice() || afterMergeChecker)) {
merged = false;
updateState = PartialUpdateResult::Failed;
if (gfxPrefs::LayersDrawFPS()) {
if (RefPtr<LayerManager> lm = builder.GetWidgetLayerManager()) {
if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
@ -3863,7 +3863,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
dlStart = TimeStamp::Now();
}
if (!merged) {
if (updateState == PartialUpdateResult::Failed) {
list.DeleteAll(&builder);
builder.EnterPresShell(aFrame);
builder.SetDirtyRect(visibleRect);
@ -3872,6 +3872,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
builder.LeavePresShell(aFrame, &list);
updateState = PartialUpdateResult::Updated;
if (afterMergeChecker) {
DisplayListChecker nonRetainedChecker(&list, "NR");
@ -3909,6 +3910,7 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
}
}
MOZ_ASSERT(updateState != PartialUpdateResult::Failed);
builder.Check();
Telemetry::AccumulateTimeDelta(Telemetry::PAINT_BUILD_DISPLAYLIST_TIME,
@ -3983,6 +3985,9 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
if (aFlags & PaintFrameFlags::PAINT_COMPRESSED) {
flags |= nsDisplayList::PAINT_COMPRESSED;
}
if (updateState == PartialUpdateResult::NoChange) {
flags |= nsDisplayList::PAINT_IDENTICAL_DISPLAY_LIST;
}
TimeStamp paintStart = TimeStamp::Now();
RefPtr<LayerManager> layerManager

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

@ -194,7 +194,7 @@ nsPresContext::MakeColorPref(const nsString& aColor)
bool
nsPresContext::IsDOMPaintEventPending()
{
if (mFireAfterPaintEvents) {
if (!mTransactions.IsEmpty()) {
return true;
}
nsRootPresContext* drpc = GetRootPresContext();
@ -203,7 +203,6 @@ nsPresContext::IsDOMPaintEventPending()
// fired, we record an empty invalidation in case display list
// invalidation doesn't invalidate anything further.
NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId() + 1, nsRect(0, 0, 0, 0));
NS_ASSERTION(mFireAfterPaintEvents, "Why aren't we planning to fire the event?");
return true;
}
return false;
@ -315,7 +314,6 @@ nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
mFontFeatureValuesDirty(true),
mSuppressResizeReflow(false),
mIsVisual(false),
mFireAfterPaintEvents(false),
mIsChrome(false),
mIsChromeOriginImage(false),
mPaintFlashing(false),
@ -2596,6 +2594,17 @@ nsPresContext::NotifyInvalidation(uint64_t aTransactionId, const nsIntRect& aRec
NotifyInvalidation(aTransactionId, rect);
}
nsPresContext::TransactionInvalidations*
nsPresContext::GetInvalidations(uint64_t aTransactionId)
{
for (TransactionInvalidations& t : mTransactions) {
if (t.mTransactionId == aTransactionId) {
return &t;
}
}
return nullptr;
}
void
nsPresContext::NotifyInvalidation(uint64_t aTransactionId, const nsRect& aRect)
{
@ -2609,9 +2618,13 @@ nsPresContext::NotifyInvalidation(uint64_t aTransactionId, const nsRect& aRect)
nsPresContext* pc;
for (pc = this; pc; pc = pc->GetParentPresContext()) {
if (pc->mFireAfterPaintEvents)
TransactionInvalidations* transaction = pc->GetInvalidations(aTransactionId);
if (transaction) {
break;
pc->mFireAfterPaintEvents = true;
} else {
transaction = pc->mTransactions.AppendElement();
transaction->mTransactionId = aTransactionId;
}
}
if (!pc) {
nsRootPresContext* rpc = GetRootPresContext();
@ -2620,18 +2633,8 @@ nsPresContext::NotifyInvalidation(uint64_t aTransactionId, const nsRect& aRect)
}
}
TransactionInvalidations* transaction = nullptr;
for (TransactionInvalidations& t : mTransactions) {
if (t.mTransactionId == aTransactionId) {
transaction = &t;
break;
}
}
if (!transaction) {
transaction = mTransactions.AppendElement();
transaction->mTransactionId = aTransactionId;
}
TransactionInvalidations* transaction = GetInvalidations(aTransactionId);
MOZ_ASSERT(transaction);
transaction->mInvalidations.AppendElement(aRect);
}
@ -2683,7 +2686,6 @@ nsPresContext::ClearNotifySubDocInvalidationData(ContainerLayer* aContainer)
struct NotifyDidPaintSubdocumentCallbackClosure {
uint64_t mTransactionId;
const mozilla::TimeStamp& mTimeStamp;
bool mNeedsAnotherDidPaintNotification;
};
/* static */ bool
nsPresContext::NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData)
@ -2696,9 +2698,6 @@ nsPresContext::NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* a
if (pc) {
pc->NotifyDidPaintForSubtree(closure->mTransactionId,
closure->mTimeStamp);
if (pc->mFireAfterPaintEvents) {
closure->mNeedsAnotherDidPaintNotification = true;
}
}
}
return true;
@ -2743,12 +2742,12 @@ nsPresContext::NotifyDidPaintForSubtree(uint64_t aTransactionId,
if (IsRoot()) {
static_cast<nsRootPresContext*>(this)->CancelDidPaintTimers(aTransactionId);
if (!mFireAfterPaintEvents) {
if (mTransactions.IsEmpty()) {
return;
}
}
if (!PresShell()->IsVisible() && !mFireAfterPaintEvents) {
if (!PresShell()->IsVisible() && mTransactions.IsEmpty()) {
return;
}
@ -2762,11 +2761,13 @@ nsPresContext::NotifyDidPaintForSubtree(uint64_t aTransactionId,
uint32_t i = 0;
while (i < mTransactions.Length()) {
if (mTransactions[i].mTransactionId <= aTransactionId) {
nsCOMPtr<nsIRunnable> ev =
new DelayedFireDOMPaintEvent(this, &mTransactions[i].mInvalidations,
mTransactions[i].mTransactionId, aTimeStamp);
nsContentUtils::AddScriptRunner(ev);
sent = true;
if (!mTransactions[i].mInvalidations.IsEmpty()) {
nsCOMPtr<nsIRunnable> ev =
new DelayedFireDOMPaintEvent(this, &mTransactions[i].mInvalidations,
mTransactions[i].mTransactionId, aTimeStamp);
nsContentUtils::AddScriptRunner(ev);
sent = true;
}
mTransactions.RemoveElementAt(i);
} else {
i++;
@ -2781,14 +2782,8 @@ nsPresContext::NotifyDidPaintForSubtree(uint64_t aTransactionId,
nsContentUtils::AddScriptRunner(ev);
}
NotifyDidPaintSubdocumentCallbackClosure closure = { aTransactionId, aTimeStamp, false };
NotifyDidPaintSubdocumentCallbackClosure closure = { aTransactionId, aTimeStamp };
mDocument->EnumerateSubDocuments(nsPresContext::NotifyDidPaintSubdocumentCallback, &closure);
if (!closure.mNeedsAnotherDidPaintNotification &&
mTransactions.IsEmpty()) {
// Nothing more to do for the moment.
mFireAfterPaintEvents = false;
}
}
bool

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

@ -1291,6 +1291,12 @@ protected:
const char* aName,
uint32_t aDelay);
struct TransactionInvalidations {
uint64_t mTransactionId;
nsTArray<nsRect> mInvalidations;
};
TransactionInvalidations* GetInvalidations(uint64_t aTransactionId);
// IMPORTANT: The ownership implicit in the following member variables
// has been explicitly checked. If you add any members to this class,
// please make the ownership explicit (pinkerton, scc).
@ -1359,10 +1365,6 @@ protected:
mozilla::UniquePtr<nsBidi> mBidiEngine;
struct TransactionInvalidations {
uint64_t mTransactionId;
nsTArray<nsRect> mInvalidations;
};
AutoTArray<TransactionInvalidations, 4> mTransactions;
// text performance metrics
@ -1489,8 +1491,6 @@ protected:
unsigned mIsVisual : 1;
unsigned mFireAfterPaintEvents : 1;
unsigned mIsChrome : 1;
unsigned mIsChromeOriginImage : 1;

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

@ -104,21 +104,25 @@ SelectAGRForFrame(nsIFrame* aFrame, AnimatedGeometryRoot* aParentAGR)
// TODO: We currently descend into all children even if we don't have an AGR
// to mark, as child stacking contexts might. It would be nice if we could
// jump into those immediately rather than walking the entire thing.
void
bool
RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
AnimatedGeometryRoot* aAGR)
{
bool modified = false;
nsDisplayList saved;
while (nsDisplayItem* i = aList->RemoveBottom()) {
if (i->HasDeletedFrame() || !i->CanBeReused()) {
i->Destroy(&mBuilder);
modified = true;
continue;
}
nsIFrame* f = i->Frame();
if (i->GetChildren()) {
PreProcessDisplayList(i->GetChildren(), SelectAGRForFrame(f, aAGR));
if (PreProcessDisplayList(i->GetChildren(), SelectAGRForFrame(f, aAGR))) {
modified = true;
}
}
// TODO: We should be able to check the clipped bounds relative
@ -126,6 +130,7 @@ RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
// frame) and determine if they can ever intersect.
if (aAGR && i->GetAnimatedGeometryRoot()->GetAsyncAGR() != aAGR) {
mBuilder.MarkFrameForDisplayIfVisible(f, mBuilder.RootReferenceFrame());
modified = true;
}
// TODO: This is here because we sometimes reuse the previous display list
@ -137,6 +142,7 @@ RetainedDisplayListBuilder::PreProcessDisplayList(nsDisplayList* aList,
}
aList->AppendToTop(&saved);
aList->RestoreState();
return modified;
}
bool IsSameItem(nsDisplayItem* aFirst, nsDisplayItem* aSecond)
@ -199,12 +205,13 @@ void SwapAndRemove(nsTArray<T>& aArray, uint32_t aIndex)
aArray.RemoveElementAt(aArray.Length() - 1);
}
static void
static bool
MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
nsDisplayLayerEventRegions* aNewItem,
nsDisplayLayerEventRegions::FrameRects nsDisplayLayerEventRegions::*aRectList,
nsTArray<nsIFrame*>& aAddedFrames)
{
bool modified = false;
// Go through the old item's rect list and remove any rectangles
// belonging to invalidated frames (deleted frames should
// already be gone at this point)
@ -219,12 +226,13 @@ MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
f->RemoveDisplayItem(aOldItem);
SwapAndRemove(oldRects.mFrames, i);
SwapAndRemove(oldRects.mBoxes, i);
modified = true;
} else {
i++;
}
}
if (!aNewItem) {
return;
return modified;
}
// Copy items from the source list to the dest list, but
@ -245,12 +253,15 @@ MergeFrameRects(nsDisplayLayerEventRegions* aOldItem,
// FrameRects lists, so defer that until the end.
aAddedFrames.AppendElement(f);
MOZ_ASSERT(f != aOldItem->Frame());
modified = true;
}
}
return modified;
}
void MergeLayerEventRegions(nsDisplayItem* aOldItem,
bool MergeLayerEventRegions(nsDisplayItem* aOldItem,
nsDisplayItem* aNewItem)
{
nsDisplayLayerEventRegions* oldItem =
@ -260,12 +271,13 @@ void MergeLayerEventRegions(nsDisplayItem* aOldItem,
nsTArray<nsIFrame*> addedFrames;
MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, addedFrames);
MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, addedFrames);
MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, addedFrames);
MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, addedFrames);
MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, addedFrames);
MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, addedFrames);
bool modified = false;
modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHitRegion, addedFrames);
modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mMaybeHitRegion, addedFrames);
modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mDispatchToContentHitRegion, addedFrames);
modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mNoActionRegion, addedFrames);
modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mHorizontalPanRegion, addedFrames);
modified |= MergeFrameRects(oldItem, newItem, &nsDisplayLayerEventRegions::mVerticalPanRegion, addedFrames);
// MergeFrameRects deferred updating the display item data list during
// processing so that earlier calls didn't change the result of later
@ -275,6 +287,7 @@ void MergeLayerEventRegions(nsDisplayItem* aOldItem,
f->AddDisplayItem(aOldItem);
}
}
return modified;
}
void
@ -388,12 +401,14 @@ void UpdateASR(nsDisplayItem* aItem,
*
* Merged List: C, A, B
*/
void
bool
RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
nsDisplayList* aOldList,
nsDisplayList* aOutList,
Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
{
bool modified = false;
nsDisplayList merged;
const auto UseItem = [&](nsDisplayItem* aItem) {
const ActiveScrolledRoot* itemClipASR =
@ -457,9 +472,11 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
aOldList->RemoveBottom();
old->Destroy(&mBuilder);
modified = true;
} else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
// This old item is also in the new list, but we haven't got to it yet.
// Stop now, and we'll deal with it when we get to the new entry.
modified = true;
break;
} else {
// Recurse into the child list (without a matching new list) to
@ -467,8 +484,10 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
if (old->GetChildren()) {
nsDisplayList empty;
Maybe<const ActiveScrolledRoot*> containerASRForChildren;
MergeDisplayLists(&empty, old->GetChildren(),
old->GetChildren(), containerASRForChildren);
if (MergeDisplayLists(&empty, old->GetChildren(),
old->GetChildren(), containerASRForChildren)) {
modified = true;
}
UpdateASR(old, containerASRForChildren);
old->UpdateBounds(&mBuilder);
}
@ -498,16 +517,21 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
// the lists of regions and frames, so we have no need to use the
// newer item. Always use the old item instead since we assume it's
// likely to have the bigger lists and merging will be quicker.
MergeLayerEventRegions(oldItem, newItem);
if (MergeLayerEventRegions(oldItem, newItem)) {
modified = true;
}
ReuseItem(oldItem);
newItem->Destroy(&mBuilder);
} else {
if (!IsAnyAncestorModified(oldItem->FrameForInvalidation()) &&
oldItem->GetChildren()) {
if (IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
modified = true;
} else if (oldItem->GetChildren()) {
MOZ_ASSERT(newItem->GetChildren());
Maybe<const ActiveScrolledRoot*> containerASRForChildren;
MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
newItem->GetChildren(), containerASRForChildren);
if (MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
newItem->GetChildren(), containerASRForChildren)) {
modified = true;
}
UpdateASR(newItem, containerASRForChildren);
newItem->UpdateBounds(&mBuilder);
}
@ -520,6 +544,7 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
} else {
// If there was no matching item in the old list, then we only need to
// add the new item to the merged list.
modified = true;
UseItem(newItem);
}
}
@ -537,21 +562,27 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
nsDisplayList empty;
Maybe<const ActiveScrolledRoot*> containerASRForChildren;
MergeDisplayLists(&empty, old->GetChildren(),
old->GetChildren(), containerASRForChildren);
if (MergeDisplayLists(&empty, old->GetChildren(),
old->GetChildren(), containerASRForChildren)) {
modified = true;
}
UpdateASR(old, containerASRForChildren);
old->UpdateBounds(&mBuilder);
}
if (old->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
MergeLayerEventRegions(old, nullptr);
if (MergeLayerEventRegions(old, nullptr)) {
modified = true;
}
}
ReuseItem(old);
} else {
old->Destroy(&mBuilder);
modified = true;
}
}
aOutList->AppendToTop(&merged);
return modified;
}
static void
@ -719,6 +750,8 @@ ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
{
nsIFrame* currentFrame = aFrame;
aBuilder.MarkFrameForDisplayIfVisible(aFrame, aBuilder.RootReferenceFrame());
while (currentFrame != aStopAtFrame) {
CRR_LOG("currentFrame: %p (placeholder=%d), aOverflow: %d %d %d %d\n",
currentFrame, !aStopAtStackingContext,
@ -769,6 +802,10 @@ ProcessFrame(nsIFrame* aFrame, nsDisplayListBuilder& aBuilder,
/* aStopAtStackingContextAndDisplayPortAndOOFFrame = */ true,
&currentFrame);
MOZ_ASSERT(currentFrame);
aOverflow.IntersectRect(aOverflow, currentFrame->GetVisualOverflowRectRelativeToSelf());
if (aOverflow.IsEmpty()) {
break;
}
if (nsLayoutUtils::FrameHasDisplayPort(currentFrame)) {
CRR_LOG("Frame belongs to displayport frame %p\n", currentFrame);
@ -925,18 +962,20 @@ RetainedDisplayListBuilder::ComputeRebuildRegion(nsTArray<nsIFrame*>& aModifiedF
ProcessFrame(f, mBuilder, &agr, overflow, mBuilder.RootReferenceFrame(),
aOutFramesWithProps, true);
aOutDirty->UnionRect(*aOutDirty, overflow);
CRR_LOG("Adding area to root draw area: %d %d %d %d\n",
overflow.x, overflow.y, overflow.width, overflow.height);
if (!overflow.IsEmpty()) {
aOutDirty->UnionRect(*aOutDirty, overflow);
CRR_LOG("Adding area to root draw area: %d %d %d %d\n",
overflow.x, overflow.y, overflow.width, overflow.height);
// If we get changed frames from multiple AGRS, then just give up as it gets really complex to
// track which items would need to be marked in MarkFramesForDifferentAGR.
if (!*aOutModifiedAGR) {
CRR_LOG("Setting %p as root stacking context AGR\n", agr);
*aOutModifiedAGR = agr;
} else if (agr && *aOutModifiedAGR != agr) {
CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
return false;
// If we get changed frames from multiple AGRS, then just give up as it gets really complex to
// track which items would need to be marked in MarkFramesForDifferentAGR.
if (!*aOutModifiedAGR) {
CRR_LOG("Setting %p as root stacking context AGR\n", agr);
*aOutModifiedAGR = agr;
} else if (agr && *aOutModifiedAGR != agr) {
CRR_LOG("Found multiple AGRs in root stacking context, giving up\n");
return false;
}
}
}
@ -989,21 +1028,36 @@ ClearFrameProps(nsTArray<nsIFrame*>& aFrames)
}
}
class AutoClearFramePropsArray
{
public:
AutoClearFramePropsArray() = default;
~AutoClearFramePropsArray()
{
ClearFrameProps(mFrames);
}
nsTArray<nsIFrame*>& Frames() { return mFrames; }
bool IsEmpty() const { return mFrames.IsEmpty(); }
private:
nsTArray<nsIFrame*> mFrames;
};
void
RetainedDisplayListBuilder::ClearFramesWithProps()
{
nsTArray<nsIFrame*> modifiedFrames;
nsTArray<nsIFrame*> framesWithProps;
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames, &framesWithProps);
ClearFrameProps(modifiedFrames);
ClearFrameProps(framesWithProps);
AutoClearFramePropsArray modifiedFrames;
AutoClearFramePropsArray framesWithProps;
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(), &framesWithProps.Frames());
}
bool
auto
RetainedDisplayListBuilder::AttemptPartialUpdate(
nscolor aBackstop,
mozilla::DisplayListChecker* aChecker)
mozilla::DisplayListChecker* aChecker) -> PartialUpdateResult
{
mBuilder.RemoveModifiedWindowRegions();
mBuilder.ClearWindowOpaqueRegion();
@ -1014,24 +1068,27 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(
mBuilder.EnterPresShell(mBuilder.RootReferenceFrame());
nsTArray<nsIFrame*> modifiedFrames;
nsTArray<nsIFrame*> framesWithProps;
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames, &framesWithProps);
// We set the override dirty regions during ComputeRebuildRegion or in
// nsLayoutUtils::InvalidateForDisplayPortChange. The display port change also
// marks the frame modified, so those regions are cleared here as well.
AutoClearFramePropsArray modifiedFrames;
AutoClearFramePropsArray framesWithProps;
GetModifiedAndFramesWithProps(&mBuilder, &modifiedFrames.Frames(), &framesWithProps.Frames());
// Do not allow partial builds if the retained display list is empty, or if
// ShouldBuildPartial heuristic fails.
const bool shouldBuildPartial = !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames);
const bool shouldBuildPartial = !mList.IsEmpty() && ShouldBuildPartial(modifiedFrames.Frames());
if (mPreviousCaret != mBuilder.GetCaretFrame()) {
if (mPreviousCaret) {
if (mBuilder.MarkFrameModifiedDuringBuilding(mPreviousCaret)) {
modifiedFrames.AppendElement(mPreviousCaret);
modifiedFrames.Frames().AppendElement(mPreviousCaret);
}
}
if (mBuilder.GetCaretFrame()) {
if (mBuilder.MarkFrameModifiedDuringBuilding(mBuilder.GetCaretFrame())) {
modifiedFrames.AppendElement(mBuilder.GetCaretFrame());
modifiedFrames.Frames().AppendElement(mBuilder.GetCaretFrame());
}
}
@ -1040,61 +1097,60 @@ RetainedDisplayListBuilder::AttemptPartialUpdate(
nsRect modifiedDirty;
AnimatedGeometryRoot* modifiedAGR = nullptr;
bool merged = false;
if (shouldBuildPartial &&
ComputeRebuildRegion(modifiedFrames, &modifiedDirty,
&modifiedAGR, framesWithProps)) {
modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
PreProcessDisplayList(&mList, modifiedAGR);
nsDisplayList modifiedDL;
if (!modifiedDirty.IsEmpty() || !framesWithProps.IsEmpty()) {
mBuilder.SetDirtyRect(modifiedDirty);
mBuilder.SetPartialUpdate(true);
mBuilder.RootReferenceFrame()->BuildDisplayListForStackingContext(&mBuilder, &modifiedDL);
nsLayoutUtils::AddExtraBackgroundItems(mBuilder, modifiedDL, mBuilder.RootReferenceFrame(),
nsRect(nsPoint(0, 0), mBuilder.RootReferenceFrame()->GetSize()),
mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf(),
aBackstop);
mBuilder.SetPartialUpdate(false);
//printf_stderr("Painting --- Modified list (dirty %d,%d,%d,%d):\n",
// modifiedDirty.x, modifiedDirty.y, modifiedDirty.width, modifiedDirty.height);
//nsFrame::PrintDisplayList(&mBuilder, modifiedDL);
} else {
// TODO: We can also skip layer building and painting if
// PreProcessDisplayList didn't end up changing anything
// Invariant: display items should have their original state here.
// printf_stderr("Skipping display list building since nothing needed to be done\n");
}
if (aChecker) {
aChecker->Set(&modifiedDL, "TM");
}
// |modifiedDL| can sometimes be empty here. We still perform the
// display list merging to prune unused items (for example, items that
// are not visible anymore) from the old list.
// TODO: Optimization opportunity. In this case, MergeDisplayLists()
// unnecessarily creates a hashtable of the old items.
Maybe<const ActiveScrolledRoot*> dummy;
MergeDisplayLists(&modifiedDL, &mList, &mList, dummy);
//printf_stderr("Painting --- Merged list:\n");
//nsFrame::PrintDisplayList(&mBuilder, mList);
merged = true;
if (!shouldBuildPartial ||
!ComputeRebuildRegion(modifiedFrames.Frames(), &modifiedDirty,
&modifiedAGR, framesWithProps.Frames())) {
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
return PartialUpdateResult::Failed;
}
modifiedDirty.IntersectRect(modifiedDirty, mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf());
PartialUpdateResult result = PartialUpdateResult::NoChange;
if (PreProcessDisplayList(&mList, modifiedAGR) ||
!modifiedDirty.IsEmpty() ||
!framesWithProps.IsEmpty()) {
result = PartialUpdateResult::Updated;
}
mBuilder.SetDirtyRect(modifiedDirty);
mBuilder.SetPartialUpdate(true);
nsDisplayList modifiedDL;
mBuilder.RootReferenceFrame()->BuildDisplayListForStackingContext(&mBuilder, &modifiedDL);
if (!modifiedDL.IsEmpty()) {
nsLayoutUtils::AddExtraBackgroundItems(mBuilder, modifiedDL, mBuilder.RootReferenceFrame(),
nsRect(nsPoint(0, 0), mBuilder.RootReferenceFrame()->GetSize()),
mBuilder.RootReferenceFrame()->GetVisualOverflowRectRelativeToSelf(),
aBackstop);
}
mBuilder.SetPartialUpdate(false);
if (aChecker) {
aChecker->Set(&modifiedDL, "TM");
}
//printf_stderr("Painting --- Modified list (dirty %d,%d,%d,%d):\n",
// modifiedDirty.x, modifiedDirty.y, modifiedDirty.width, modifiedDirty.height);
//nsFrame::PrintDisplayList(&mBuilder, modifiedDL);
// |modifiedDL| can sometimes be empty here. We still perform the
// display list merging to prune unused items (for example, items that
// are not visible anymore) from the old list.
// TODO: Optimization opportunity. In this case, MergeDisplayLists()
// unnecessarily creates a hashtable of the old items.
// TODO: Ideally we could skip this if result is NoChange, but currently when
// we call RestoreState on nsDisplayWrapList it resets the clip to the base
// clip, and we need the UpdateBounds call (within MergeDisplayLists) to
// move it to the correct inner clip.
Maybe<const ActiveScrolledRoot*> dummy;
if (MergeDisplayLists(&modifiedDL, &mList, &mList, dummy)) {
result = PartialUpdateResult::Updated;
}
//printf_stderr("Painting --- Merged list:\n");
//nsFrame::PrintDisplayList(&mBuilder, mList);
mBuilder.LeavePresShell(mBuilder.RootReferenceFrame(), &mList);
// We set the override dirty regions during ComputeRebuildRegion or in
// nsLayoutUtils::InvalidateForDisplayPortChange. The display port change also
// marks the frame modified, so those regions are cleared here as well.
ClearFrameProps(modifiedFrames);
ClearFrameProps(framesWithProps);
return merged;
return result;
}

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

@ -29,8 +29,14 @@ struct RetainedDisplayListBuilder {
nsDisplayList* List() { return &mList; }
bool AttemptPartialUpdate(nscolor aBackstop,
mozilla::DisplayListChecker* aChecker);
enum class PartialUpdateResult {
Failed,
NoChange,
Updated
};
PartialUpdateResult AttemptPartialUpdate(nscolor aBackstop,
mozilla::DisplayListChecker* aChecker);
/**
* Iterates through the display list builder reference frame document and
@ -43,9 +49,9 @@ struct RetainedDisplayListBuilder {
NS_DECLARE_FRAME_PROPERTY_DELETABLE(Cached, RetainedDisplayListBuilder)
private:
void PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
bool PreProcessDisplayList(nsDisplayList* aList, AnimatedGeometryRoot* aAGR);
void MergeDisplayLists(nsDisplayList* aNewList,
bool MergeDisplayLists(nsDisplayList* aNewList,
nsDisplayList* aOldList,
nsDisplayList* aOutList,
mozilla::Maybe<const mozilla::ActiveScrolledRoot*>& aOutContainerASR);

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

@ -54,7 +54,7 @@ DECLARE_DISPLAY_ITEM_TYPE(SELECTION_OVERLAY, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR_REGION, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(MASK, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(MASK, 0)
DECLARE_DISPLAY_ITEM_TYPE(FILTER, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(SVG_OUTER_SVG, TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(SVG_GEOMETRY, 0)

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

@ -2420,6 +2420,100 @@ nsDisplayListBuilder::GetWidgetLayerManager(nsView** aView)
return nullptr;
}
FrameLayerBuilder*
nsDisplayList::BuildLayers(nsDisplayListBuilder* aBuilder,
LayerManager* aLayerManager,
uint32_t aFlags,
bool aIsWidgetTransaction)
{
nsIFrame* frame = aBuilder->RootReferenceFrame();
nsPresContext* presContext = frame->PresContext();
nsIPresShell* presShell = presContext->PresShell();
FrameLayerBuilder *layerBuilder = new FrameLayerBuilder();
layerBuilder->Init(aBuilder, aLayerManager);
if (aFlags & PAINT_COMPRESSED) {
layerBuilder->SetLayerTreeCompressionMode();
}
RefPtr<ContainerLayer> root;
{
AUTO_PROFILER_TRACING("Paint", "LayerBuilding");
if (XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
FrameLayerBuilder::InvalidateAllLayers(aLayerManager);
}
if (aIsWidgetTransaction) {
layerBuilder->DidBeginRetainedLayerTransaction(aLayerManager);
}
// Clear any ScrollMetadata that may have been set on the root layer on a
// previous paint. This paint will set new metrics if necessary, and if we
// don't clear the old one here, we may be left with extra metrics.
if (Layer* rootLayer = aLayerManager->GetRoot()) {
rootLayer->SetScrollMetadata(nsTArray<ScrollMetadata>());
}
ContainerLayerParameters containerParameters
(presShell->GetResolution(), presShell->GetResolution());
{
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization);
root = layerBuilder->
BuildContainerLayerFor(aBuilder, aLayerManager, frame, nullptr, this,
containerParameters, nullptr);
if (!record.GetStart().IsNull() && gfxPrefs::LayersDrawFPS()) {
if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(aLayerManager)) {
pt->flbMs() = (TimeStamp::Now() - record.GetStart()).ToMilliseconds();
}
}
}
if (!root) {
return nullptr;
}
// Root is being scaled up by the X/Y resolution. Scale it back down.
root->SetPostScale(1.0f/containerParameters.mXScale,
1.0f/containerParameters.mYScale);
root->SetScaleToResolution(presShell->ScaleToResolution(),
containerParameters.mXScale);
auto callback = [root](FrameMetrics::ViewID aScrollId) -> bool {
return nsLayoutUtils::ContainsMetricsWithId(root, aScrollId);
};
if (Maybe<ScrollMetadata> rootMetadata = nsLayoutUtils::GetRootMetadata(
aBuilder, root->Manager(), containerParameters, callback)) {
root->SetScrollMetadata(rootMetadata.value());
}
// NS_WARNING is debug-only, so don't even bother checking the conditions in
// a release build.
#ifdef DEBUG
bool usingDisplayport = false;
if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
nsIContent* content = rootScrollFrame->GetContent();
if (content) {
usingDisplayport = nsLayoutUtils::HasDisplayPort(content);
}
}
if (usingDisplayport &&
!(root->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
SpammyLayoutWarningsEnabled()) {
// See bug 693938, attachment 567017
NS_WARNING("Transparent content with displayports can be expensive.");
}
#endif
aLayerManager->SetRoot(root);
layerBuilder->WillEndTransaction();
}
return layerBuilder;
}
/**
* We paint by executing a layer manager transaction, constructing a
* single layer representing the display list, and then making it the
@ -2512,119 +2606,25 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
presContext->MayHavePaintEventListenerInSubDocument() ? nsPresContext::NotifySubDocInvalidation : 0;
UniquePtr<LayerProperties> props;
RefPtr<ContainerLayer> root;
// Store the existing layer builder to reinstate it on return.
FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder();
bool computeInvalidRect = (computeInvalidFunc ||
(!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) &&
widgetTransaction;
FrameLayerBuilder *layerBuilder = new FrameLayerBuilder();
layerBuilder->Init(aBuilder, layerManager);
if (aFlags & PAINT_COMPRESSED) {
layerBuilder->SetLayerTreeCompressionMode();
if (computeInvalidRect) {
props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
}
{
AUTO_PROFILER_TRACING("Paint", "LayerBuilding");
if (doBeginTransaction) {
if (aCtx) {
if (!layerManager->BeginTransactionWithTarget(aCtx)) {
return nullptr;
}
} else {
if (!layerManager->BeginTransaction()) {
return nullptr;
}
if (doBeginTransaction) {
if (aCtx) {
if (!layerManager->BeginTransactionWithTarget(aCtx)) {
return nullptr;
}
} else {
if (!layerManager->BeginTransaction()) {
return nullptr;
}
}
if (XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
FrameLayerBuilder::InvalidateAllLayers(layerManager);
}
if (widgetTransaction) {
layerBuilder->DidBeginRetainedLayerTransaction(layerManager);
}
bool computeInvalidRect = (computeInvalidFunc ||
(!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) &&
widgetTransaction;
if (computeInvalidRect) {
props = Move(LayerProperties::CloneFrom(layerManager->GetRoot()));
}
// Clear any ScrollMetadata that may have been set on the root layer on a
// previous paint. This paint will set new metrics if necessary, and if we
// don't clear the old one here, we may be left with extra metrics.
if (Layer* rootLayer = layerManager->GetRoot()) {
rootLayer->SetScrollMetadata(nsTArray<ScrollMetadata>());
}
ContainerLayerParameters containerParameters
(presShell->GetResolution(), presShell->GetResolution());
{
PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization);
root = layerBuilder->
BuildContainerLayerFor(aBuilder, layerManager, frame, nullptr, this,
containerParameters, nullptr);
if (!record.GetStart().IsNull() && gfxPrefs::LayersDrawFPS()) {
if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(layerManager)) {
pt->flbMs() = (TimeStamp::Now() - record.GetStart()).ToMilliseconds();
}
}
}
if (!root) {
layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
return nullptr;
}
// Root is being scaled up by the X/Y resolution. Scale it back down.
root->SetPostScale(1.0f/containerParameters.mXScale,
1.0f/containerParameters.mYScale);
root->SetScaleToResolution(presShell->ScaleToResolution(),
containerParameters.mXScale);
auto callback = [root](FrameMetrics::ViewID aScrollId) -> bool {
return nsLayoutUtils::ContainsMetricsWithId(root, aScrollId);
};
if (Maybe<ScrollMetadata> rootMetadata = nsLayoutUtils::GetRootMetadata(
aBuilder, root->Manager(), containerParameters, callback)) {
root->SetScrollMetadata(rootMetadata.value());
}
// NS_WARNING is debug-only, so don't even bother checking the conditions in
// a release build.
#ifdef DEBUG
bool usingDisplayport = false;
if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
nsIContent* content = rootScrollFrame->GetContent();
if (content) {
usingDisplayport = nsLayoutUtils::HasDisplayPort(content);
}
}
if (usingDisplayport &&
!(root->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
SpammyLayoutWarningsEnabled()) {
// See bug 693938, attachment 567017
NS_WARNING("Transparent content with displayports can be expensive.");
}
#endif
layerManager->SetRoot(root);
layerBuilder->WillEndTransaction();
}
if (widgetTransaction ||
// SVG-as-an-image docs don't paint as part of the retained layer tree,
// but they still need the invalidation state bits cleared in order for
// invalidation for CSS/SMIL animation to work properly.
(document && document->IsBeingUsedAsImage())) {
frame->ClearInvalidationStateBits();
}
bool temp = aBuilder->SetIsCompositingCheap(layerManager->IsCompositingCheap());
@ -2641,25 +2641,53 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
}
}
// If this is the content process, we ship plugin geometry updates over with layer
// updates, so calculate that now before we call EndTransaction.
nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
if (rootPresContext && XRE_IsContentProcess()) {
if (aBuilder->WillComputePluginGeometry()) {
rootPresContext->ComputePluginGeometryUpdates(aBuilder->RootReferenceFrame(), aBuilder, this);
}
// The layer system caches plugin configuration information for forwarding
// with layer updates which needs to get set during reflow. This must be
// called even if there are no windowed plugins in the page.
rootPresContext->CollectPluginGeometryUpdates(layerManager);
}
MaybeSetupTransactionIdAllocator(layerManager, presContext);
layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
aBuilder, flags);
// Store the existing layer builder to reinstate it on return.
FrameLayerBuilder *oldBuilder = layerManager->GetLayerBuilder();
FrameLayerBuilder *layerBuilder = nullptr;
bool sent = false;
if (aFlags & PAINT_IDENTICAL_DISPLAY_LIST) {
sent = layerManager->EndEmptyTransaction(flags);
}
if (!sent) {
layerBuilder = BuildLayers(aBuilder, layerManager,
aFlags, widgetTransaction);
if (!layerBuilder) {
layerManager->SetUserData(&gLayerManagerLayerBuilder, oldBuilder);
return nullptr;
}
// If this is the content process, we ship plugin geometry updates over with layer
// updates, so calculate that now before we call EndTransaction.
nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
if (rootPresContext && XRE_IsContentProcess()) {
if (aBuilder->WillComputePluginGeometry()) {
rootPresContext->ComputePluginGeometryUpdates(aBuilder->RootReferenceFrame(), aBuilder, this);
}
// The layer system caches plugin configuration information for forwarding
// with layer updates which needs to get set during reflow. This must be
// called even if there are no windowed plugins in the page.
rootPresContext->CollectPluginGeometryUpdates(layerManager);
}
layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
aBuilder, flags);
layerBuilder->DidEndTransaction();
}
if (widgetTransaction ||
// SVG-as-an-image docs don't paint as part of the retained layer tree,
// but they still need the invalidation state bits cleared in order for
// invalidation for CSS/SMIL animation to work properly.
(document && document->IsBeingUsedAsImage())) {
frame->ClearInvalidationStateBits();
}
aBuilder->SetIsCompositingCheap(temp);
layerBuilder->DidEndTransaction();
if (document && widgetTransaction) {
TriggerPendingAnimations(document, layerManager->GetAnimationReadyTime());
@ -2668,11 +2696,11 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
nsIntRegion invalid;
bool areaOverflowed = false;
if (props) {
if (!props->ComputeDifferences(root, invalid, computeInvalidFunc)) {
if (!props->ComputeDifferences(layerManager->GetRoot(), invalid, computeInvalidFunc)) {
areaOverflowed = true;
}
} else if (widgetTransaction) {
LayerProperties::ClearInvalidations(root);
LayerProperties::ClearInvalidations(layerManager->GetRoot());
}
bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();

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

@ -3064,11 +3064,17 @@ public:
PAINT_USE_WIDGET_LAYERS = 0x01,
PAINT_EXISTING_TRANSACTION = 0x04,
PAINT_NO_COMPOSITE = 0x08,
PAINT_COMPRESSED = 0x10
PAINT_COMPRESSED = 0x10,
PAINT_IDENTICAL_DISPLAY_LIST = 0x20
};
already_AddRefed<LayerManager> PaintRoot(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx,
uint32_t aFlags);
mozilla::FrameLayerBuilder* BuildLayers(nsDisplayListBuilder* aBuilder,
LayerManager* aLayerManager,
uint32_t aFlags,
bool aIsWidgetTransaction);
/**
* Get the bounds. Takes the union of the bounds of all children.
* The result is not cached.
@ -4737,7 +4743,7 @@ private:
}
}
friend void MergeLayerEventRegions(nsDisplayItem*, nsDisplayItem*);
friend bool MergeLayerEventRegions(nsDisplayItem*, nsDisplayItem*);
// Relative to aFrame's reference frame.
// These are the points that are definitely in the hit region.

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

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<body>
<div style="opacity:0.5" id="hi">
<div style="position:fixed; width:200px; height:200px; background-color:blue"></div>
</div>
</body>
</html>

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

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en" class="reftest-wait">
<body>
<div style="opacity:0.5" id="hi">
<div style="position:fixed; width:200px; height:200px; background-color:blue"></div>
</div>
<script>
function doTest()
{
var opacityElement = document.getElementById("hi");
opacityElement.style.left = '100px';
document.documentElement.removeAttribute("class");
}
document.addEventListener("MozReftestInvalidate", doTest);
</script>
</body>
</html>

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

@ -19,3 +19,4 @@ skip-if(Android) == 1428993-1.html 1428993-1-ref.html
needs-focus == 1429027-1.html 1429027-1-ref.html
== 1432553-1.html 1432553-1-ref.html
== 1432553-2.html 1432553-2-ref.html
== 1436189-1.html 1436189-1-ref.html

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

@ -1552,6 +1552,7 @@ pref("javascript.options.dump_stack_on_debuggee_would_run", false);
// Spectre security vulnerability mitigations.
pref("javascript.options.spectre.index_masking", true);
pref("javascript.options.spectre.string_mitigations", false);
// Streams API
pref("javascript.options.streams", false);

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

@ -49,11 +49,13 @@ if (gInContentProcess) {
.getService(Ci.nsIMemoryReporterManager);
let rss = memMgr.resident;
let uss = memMgr.residentUnique;
let ghosts = memMgr.ghostWindows;
Services.cpmm.sendAsyncMessage("Memory:Summary", {
pid,
summary: {
uss,
rss,
ghosts,
}
});
},

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

@ -22,10 +22,12 @@ this.Memory = {
* "parent": {
* uss: <int>,
* rss: <int>,
* ghosts: <int>,
* },
* <pid>: {
* uss: <int>,
* rss: <int>,
* ghosts: <int>,
* },
* ...
* }
@ -65,7 +67,8 @@ this.Memory = {
.getService(Ci.nsIMemoryReporterManager);
let rss = memMgr.resident;
let uss = memMgr.residentUnique;
this._summaries.Parent = { uss, rss };
let ghosts = memMgr.ghostWindows;
this._summaries.Parent = { uss, rss, ghosts };
this._pendingResolve(this._summaries);
this._pendingResolve = null;
this._summaries = null;

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

@ -305,6 +305,7 @@ GetGtkWindow(nsIDOMDocument *aDocument)
NS_IMETHODIMP
nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
const nsACString& aPrincipalURISpec,
nsIArray * aArrayTransferables,
nsIScriptableRegion * aRegion,
uint32_t aActionType,
@ -320,7 +321,8 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
if (mSourceNode)
return NS_ERROR_NOT_AVAILABLE;
return nsBaseDragService::InvokeDragSession(aDOMNode, aArrayTransferables,
return nsBaseDragService::InvokeDragSession(aDOMNode, aPrincipalURISpec,
aArrayTransferables,
aRegion, aActionType,
aContentPolicyType);
}

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

@ -64,6 +64,7 @@ public:
uint32_t aActionType) override;
// nsIDragService
NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode,
const nsACString& aPrincipalURISpec,
nsIArray * anArrayTransferables,
nsIScriptableRegion * aRegion,
uint32_t aActionType,

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

@ -169,6 +169,19 @@ nsBaseDragService::GetSourceNode(nsIDOMNode** aSourceNode)
return NS_OK;
}
NS_IMETHODIMP
nsBaseDragService::GetTriggeringPrincipalURISpec(nsACString& aPrincipalURISpec)
{
aPrincipalURISpec = mTriggeringPrincipalURISpec;
return NS_OK;
}
NS_IMETHODIMP
nsBaseDragService::SetTriggeringPrincipalURISpec(const nsACString& aPrincipalURISpec)
{
mTriggeringPrincipalURISpec = aPrincipalURISpec;
return NS_OK;
}
//-------------------------------------------------------------------------
@ -205,6 +218,7 @@ nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer)
//-------------------------------------------------------------------------
NS_IMETHODIMP
nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
const nsACString& aPrincipalURISpec,
nsIArray* aTransferableArray,
nsIScriptableRegion* aDragRgn,
uint32_t aActionType,
@ -219,6 +233,7 @@ nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
// stash the document of the dom node
nsCOMPtr<nsINode> node = do_QueryInterface(aDOMNode);
mSourceDocument = do_QueryInterface(node->OwnerDoc());
mTriggeringPrincipalURISpec.Assign(aPrincipalURISpec);
mSourceNode = aDOMNode;
mContentPolicyType = aContentPolicyType;
mEndDragPoint = LayoutDeviceIntPoint(0, 0);
@ -234,6 +249,7 @@ nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
if (NS_FAILED(rv)) {
mSourceNode = nullptr;
mTriggeringPrincipalURISpec.Truncate(0);
mSourceDocument = nullptr;
}
@ -242,6 +258,7 @@ nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
NS_IMETHODIMP
nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
const nsACString& aPrincipalURISpec,
nsIArray* aTransferableArray,
nsIScriptableRegion* aRegion,
uint32_t aActionType,
@ -265,7 +282,8 @@ nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
aDragEvent->GetScreenY(&mScreenPosition.y);
aDragEvent->GetMozInputSource(&mInputSource);
nsresult rv = InvokeDragSession(aDOMNode, aTransferableArray,
nsresult rv = InvokeDragSession(aDOMNode, aPrincipalURISpec,
aTransferableArray,
aRegion, aActionType,
nsIContentPolicy::TYPE_INTERNAL_IMAGE);
@ -280,6 +298,7 @@ nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
NS_IMETHODIMP
nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
const nsACString& aPrincipalURISpec,
nsIArray* aTransferableArray,
uint32_t aActionType,
nsIDOMDragEvent* aDragEvent,
@ -306,7 +325,8 @@ nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
nsCOMPtr<nsIDOMNode> node;
aSelection->GetFocusNode(getter_AddRefs(node));
nsresult rv = InvokeDragSession(node, aTransferableArray,
nsresult rv = InvokeDragSession(node, aPrincipalURISpec,
aTransferableArray,
nullptr, aActionType,
nsIContentPolicy::TYPE_OTHER);
@ -422,6 +442,7 @@ nsBaseDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
// release the source we've been holding on to.
mSourceDocument = nullptr;
mSourceNode = nullptr;
mTriggeringPrincipalURISpec.Truncate(0);
mSelection = nullptr;
mDataTransfer = nullptr;
mHasImage = false;

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

@ -14,6 +14,7 @@
#include "nsCOMPtr.h"
#include "nsRect.h"
#include "nsPoint.h"
#include "nsString.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/HTMLCanvasElement.h"
@ -159,6 +160,7 @@ protected:
nsSize mTargetSize;
nsCOMPtr<nsIDOMNode> mSourceNode;
nsCString mTriggeringPrincipalURISpec;
nsCOMPtr<nsIDOMDocument> mSourceDocument; // the document at the drag source. will be null
// if it came from outside the app.
nsContentPolicyType mContentPolicyType; // the contentpolicy type passed to the channel

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

@ -29,6 +29,25 @@ nsDragServiceProxy::~nsDragServiceProxy()
{
}
static void
GetPrincipalURIFromNode(nsCOMPtr<nsIDOMNode>& sourceNode,
nsCString& aPrincipalURISpec)
{
nsCOMPtr<nsINode> node = do_QueryInterface(sourceNode);
if (!node) {
return;
}
nsCOMPtr<nsIPrincipal> principal = node->NodePrincipal();
nsCOMPtr<nsIURI> principalURI;
nsresult rv = principal->GetURI(getter_AddRefs(principalURI));
if (NS_FAILED(rv)) {
return;
}
principalURI->GetSpec(aPrincipalURISpec);
}
nsresult
nsDragServiceProxy::InvokeDragSessionImpl(nsIArray* aArrayTransferables,
nsIScriptableRegion* aRegion,
@ -45,6 +64,9 @@ nsDragServiceProxy::InvokeDragSessionImpl(nsIArray* aArrayTransferables,
child->Manager(),
nullptr);
nsCString principalURISpec;
GetPrincipalURIFromNode(mSourceNode, principalURISpec);
LayoutDeviceIntRect dragRect;
if (mHasImage || mSelection) {
nsPresContext* pc;
@ -76,7 +98,7 @@ nsDragServiceProxy::InvokeDragSessionImpl(nsIArray* aArrayTransferables,
mozilla::Unused <<
child->SendInvokeDragSession(dataTransfers, aActionType, surfaceData,
stride, static_cast<uint8_t>(dataSurface->GetFormat()),
dragRect);
dragRect, principalURISpec);
StartDragSession();
return NS_OK;
}
@ -84,7 +106,8 @@ nsDragServiceProxy::InvokeDragSessionImpl(nsIArray* aArrayTransferables,
}
mozilla::Unused << child->SendInvokeDragSession(dataTransfers, aActionType,
mozilla::void_t(), 0, 0, dragRect);
mozilla::void_t(), 0, 0, dragRect,
principalURISpec);
StartDragSession();
return NS_OK;
}

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

@ -42,6 +42,8 @@ interface nsIDragService : nsISupports
*
* Note: This method is deprecated for non-native code.
*
* @param aPrincipalURISpec - the URI of the triggering principal of the
* drag, or an empty string if it's from browser chrome or OS
* @param aTransferables - an array of transferables to be dragged
* @param aRegion - a region containing rectangles for cursor feedback,
* in window coordinates.
@ -51,6 +53,7 @@ interface nsIDragService : nsISupports
* (defaults to TYPE_OTHER)
*/
void invokeDragSession (in nsIDOMNode aDOMNode,
in AUTF8String aPrincipalURISpec,
in nsIArray aTransferables,
in nsIScriptableRegion aRegion,
in unsigned long aActionType,
@ -84,6 +87,7 @@ interface nsIDragService : nsISupports
* event are needed to calculate the image location.
*/
void invokeDragSessionWithImage(in nsIDOMNode aDOMNode,
in AUTF8String aPrincipalURISpec,
in nsIArray aTransferableArray,
in nsIScriptableRegion aRegion,
in unsigned long aActionType,
@ -101,6 +105,7 @@ interface nsIDragService : nsISupports
* Note: This method is deprecated for non-native code.
*/
void invokeDragSessionWithSelection(in nsISelection aSelection,
in AUTF8String aPrincipalURISpec,
in nsIArray aTransferableArray,
in unsigned long aActionType,
in nsIDOMDragEvent aDragEvent,

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

@ -62,6 +62,13 @@ interface nsIDragSession : nsISupports
*/
readonly attribute nsIDOMNode sourceNode;
/**
* The URI spec of the triggering principal. This may be different than
* sourceNode's principal when sourceNode is xul:browser and the drag is
* triggered in a browsing context inside it.
*/
attribute AUTF8String triggeringPrincipalURISpec;
/**
* The data transfer object for the current drag.
*/