зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
628c59112e
|
@ -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), ¬Inline);
|
||||
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(¬Inline);
|
||||
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, ¬Rope);
|
||||
|
||||
// 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,
|
||||
¤tFrame);
|
||||
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.
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче