mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 10:44:56 +00:00
Merge inbound to m-c. a=merge
This commit is contained in:
commit
e1fdfb3b73
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bug 1297276 - renaming a file with a different case needed a clobber on case insensitive filesystem
|
||||
Bug 1294660 - CSS properties regeneration needs a clobber
|
||||
|
@ -240,21 +240,6 @@ appUpdater.prototype =
|
||||
Components.interfaces.nsIAppStartup.eRestart);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles oncommand for the "Apply Update…" button
|
||||
* which is presented if we need to show the billboard.
|
||||
*/
|
||||
buttonApplyBillboard: function() {
|
||||
const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
|
||||
var ary = null;
|
||||
ary = Components.classes["@mozilla.org/supports-array;1"].
|
||||
createInstance(Components.interfaces.nsISupportsArray);
|
||||
ary.AppendElement(this.update);
|
||||
var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
|
||||
Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
|
||||
window.close(); // close the "About" window; updates.xul takes over.
|
||||
},
|
||||
|
||||
/**
|
||||
* Implements nsIUpdateCheckListener. The methods implemented by
|
||||
* nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload
|
||||
@ -287,11 +272,6 @@ appUpdater.prototype =
|
||||
return;
|
||||
}
|
||||
|
||||
if (gAppUpdater.update.billboardURL) {
|
||||
gAppUpdater.selectPanel("applyBillboard");
|
||||
return;
|
||||
}
|
||||
|
||||
if (gAppUpdater.updateAuto) // automatically download and install
|
||||
gAppUpdater.startDownload();
|
||||
else // ask
|
||||
|
@ -78,13 +78,6 @@
|
||||
oncommand="gAppUpdater.buttonRestartAfterDownload();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="applyBillboard" align="center">
|
||||
<button id="applyButtonBillboard" align="start"
|
||||
label="&update.applyButtonBillboard.label;"
|
||||
accesskey="&update.applyButtonBillboard.accesskey;"
|
||||
oncommand="gAppUpdater.buttonApplyBillboard();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="checkingForUpdates" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
|
||||
</hbox>
|
||||
|
@ -91,6 +91,14 @@ XPCOMUtils.defineLazyGetter(this, "standaloneStylesheets", () => {
|
||||
return stylesheets;
|
||||
});
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
extensions.on("page-shutdown", (type, context) => {
|
||||
if (context.type == "popup" && context.active) {
|
||||
context.contentWindow.close();
|
||||
}
|
||||
});
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
class BasePopup {
|
||||
constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false) {
|
||||
this.extension = extension;
|
||||
@ -129,8 +137,10 @@ class BasePopup {
|
||||
this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
|
||||
this.viewNode.style.maxHeight = "";
|
||||
|
||||
if (this.panel) {
|
||||
this.panel.style.removeProperty("--panel-arrowcontent-background");
|
||||
this.panel.style.removeProperty("--panel-arrow-image-vertical");
|
||||
}
|
||||
|
||||
this.browser = null;
|
||||
this.viewNode = null;
|
||||
@ -154,7 +164,7 @@ class BasePopup {
|
||||
|
||||
get panel() {
|
||||
let panel = this.viewNode;
|
||||
while (panel.localName != "panel") {
|
||||
while (panel && panel.localName != "panel") {
|
||||
panel = panel.parentNode;
|
||||
}
|
||||
return panel;
|
||||
|
@ -49,6 +49,7 @@ tags = webextensions
|
||||
[browser_ext_popup_api_injection.js]
|
||||
[browser_ext_popup_background.js]
|
||||
[browser_ext_popup_corners.js]
|
||||
[browser_ext_popup_shutdown.js]
|
||||
[browser_ext_runtime_openOptionsPage.js]
|
||||
[browser_ext_runtime_openOptionsPage_uninstall.js]
|
||||
[browser_ext_runtime_setUninstallURL.js]
|
||||
|
@ -0,0 +1,74 @@
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
"use strict";
|
||||
|
||||
let getExtension = () => {
|
||||
return ExtensionTestUtils.loadExtension({
|
||||
background() {
|
||||
browser.tabs.query({active: true, currentWindow: true}, tabs => {
|
||||
browser.pageAction.show(tabs[0].id);
|
||||
});
|
||||
},
|
||||
|
||||
manifest: {
|
||||
"browser_action": {
|
||||
"default_popup": "popup.html",
|
||||
"browser_style": false,
|
||||
},
|
||||
|
||||
"page_action": {
|
||||
"default_popup": "popup.html",
|
||||
"browser_style": false,
|
||||
},
|
||||
},
|
||||
|
||||
files: {
|
||||
"popup.html": `<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"></head></html>`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
add_task(function* testStandaloneBrowserAction() {
|
||||
info("Test stand-alone browserAction popup");
|
||||
|
||||
let extension = getExtension();
|
||||
yield extension.startup();
|
||||
|
||||
clickBrowserAction(extension);
|
||||
let browser = yield awaitExtensionPanel(extension);
|
||||
let panel = getPanelForNode(browser);
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
is(panel.parentNode, null, "Panel should be removed from the document");
|
||||
});
|
||||
|
||||
add_task(function* testMenuPanelBrowserAction() {
|
||||
let extension = getExtension();
|
||||
yield extension.startup();
|
||||
|
||||
let widget = getBrowserActionWidget(extension);
|
||||
CustomizableUI.addWidgetToArea(widget.id, CustomizableUI.AREA_PANEL);
|
||||
|
||||
clickBrowserAction(extension);
|
||||
let browser = yield awaitExtensionPanel(extension);
|
||||
let panel = getPanelForNode(browser);
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
is(panel.state, "closed", "Panel should be closed");
|
||||
});
|
||||
|
||||
add_task(function* testPageAction() {
|
||||
let extension = getExtension();
|
||||
yield extension.startup();
|
||||
|
||||
clickPageAction(extension);
|
||||
let browser = yield awaitExtensionPanel(extension);
|
||||
let panel = getPanelForNode(browser);
|
||||
|
||||
yield extension.unload();
|
||||
|
||||
is(panel.parentNode, null, "Panel should be removed from the document");
|
||||
});
|
@ -3,7 +3,7 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<!ENTITY aboutDialog.title "About &brandFullName;">
|
||||
|
||||
<!-- LOCALIZATION NOTE (update.checkForUpdatesButton.*, update.updateButton.*, update.applyButtonBillboard.*):
|
||||
<!-- LOCALIZATION NOTE (update.checkForUpdatesButton.*, update.updateButton.*):
|
||||
# Only one button is present at a time.
|
||||
# The button when displayed is located directly under the Firefox version in
|
||||
# the about dialog (see bug 596813 for screenshots).
|
||||
@ -12,8 +12,6 @@
|
||||
<!ENTITY update.checkForUpdatesButton.accesskey "C">
|
||||
<!ENTITY update.updateButton.label2 "Restart &brandShortName; to Update">
|
||||
<!ENTITY update.updateButton.accesskey "R">
|
||||
<!ENTITY update.applyButtonBillboard.label "Apply Update…">
|
||||
<!ENTITY update.applyButtonBillboard.accesskey "A">
|
||||
|
||||
|
||||
<!-- LOCALIZATION NOTE (warningDesc.version): This is a warning about the experimental nature of Nightly and Aurora builds. It is only shown in those versions. -->
|
||||
|
@ -64,8 +64,6 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
list-style-image: url(chrome://browser/skin/identity-icon.svg#normal);
|
||||
filter: url(chrome://browser/skin/filters.svg#fill);
|
||||
fill: currentColor;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
@ -151,8 +149,6 @@
|
||||
margin-inline-start: 2px;
|
||||
margin-inline-end: 0;
|
||||
list-style-image: url(chrome://browser/skin/tracking-protection-16.svg);
|
||||
filter: url(chrome://browser/skin/filters.svg#fill);
|
||||
fill: currentColor;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
<style>
|
||||
path {
|
||||
fill-rule: evenodd;
|
||||
fill: -moz-fieldtext;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
@ -17,5 +17,5 @@
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
<use xlink:href="#shape-shield-outer" mask="url(#mask-shield-cutout)"/>
|
||||
<use xlink:href="#shape-shield-outer" mask="url(#mask-shield-cutout)" fill="-moz-fieldtext"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
File diff suppressed because one or more lines are too long
@ -12,6 +12,7 @@
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "nsIAnonymousContentCreator.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsCSSAnonBoxes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -375,6 +376,31 @@ AllChildrenIterator::Seek(nsIContent* aChildToFind)
|
||||
return child == aChildToFind;
|
||||
}
|
||||
|
||||
void
|
||||
AllChildrenIterator::AppendNativeAnonymousChildren()
|
||||
{
|
||||
AppendNativeAnonymousChildrenFromFrame(mOriginalContent->GetPrimaryFrame());
|
||||
|
||||
// The root scroll frame is not the primary frame of the root element.
|
||||
// Detect and handle this case.
|
||||
if (mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) {
|
||||
nsIPresShell* presShell = mOriginalContent->OwnerDoc()->GetShell();
|
||||
nsIFrame* scrollFrame = presShell ? presShell->GetRootScrollFrame() : nullptr;
|
||||
if (scrollFrame) {
|
||||
AppendNativeAnonymousChildrenFromFrame(scrollFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AllChildrenIterator::AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame)
|
||||
{
|
||||
nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame);
|
||||
if (ac) {
|
||||
ac->AppendAnonymousContentTo(mAnonKids, mFlags);
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
AllChildrenIterator::GetNextChild()
|
||||
{
|
||||
@ -406,11 +432,7 @@ AllChildrenIterator::GetNextChild()
|
||||
if (mPhase == eAtAnonKids) {
|
||||
if (mAnonKids.IsEmpty()) {
|
||||
MOZ_ASSERT(mAnonKidsIdx == UINT32_MAX);
|
||||
nsIAnonymousContentCreator* ac =
|
||||
do_QueryFrame(mOriginalContent->GetPrimaryFrame());
|
||||
if (ac) {
|
||||
ac->AppendAnonymousContentTo(mAnonKids, mFlags);
|
||||
}
|
||||
AppendNativeAnonymousChildren();
|
||||
mAnonKidsIdx = 0;
|
||||
}
|
||||
else {
|
||||
@ -462,13 +484,9 @@ AllChildrenIterator::GetPreviousChild()
|
||||
|
||||
if (mPhase == eAtAnonKids) {
|
||||
if (mAnonKids.IsEmpty()) {
|
||||
nsIAnonymousContentCreator* ac =
|
||||
do_QueryFrame(mOriginalContent->GetPrimaryFrame());
|
||||
if (ac) {
|
||||
ac->AppendAnonymousContentTo(mAnonKids, mFlags);
|
||||
AppendNativeAnonymousChildren();
|
||||
mAnonKidsIdx = mAnonKids.Length();
|
||||
}
|
||||
}
|
||||
|
||||
// If 0 then it turns into UINT32_MAX, which indicates the iterator is
|
||||
// before the anonymous children.
|
||||
@ -499,5 +517,100 @@ AllChildrenIterator::GetPreviousChild()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsNativeAnonymousImplementationOfPseudoElement(nsIContent* aContent)
|
||||
{
|
||||
// First, we need a frame. This leads to the tricky issue of what we can
|
||||
// infer if the frame is null.
|
||||
//
|
||||
// Unlike regular nodes, native anonymous content (NAC) gets created during
|
||||
// frame construction, which happens after the main style traversal. This
|
||||
// means that we have to manually resolve style for those nodes shortly after
|
||||
// they're created, either by (a) invoking ResolvePseudoElementStyle (for PE
|
||||
// NAC), or (b) handing the subtree off to Servo for a mini-traversal (for
|
||||
// non-PE NAC). We have assertions in nsCSSFrameConstructor that we don't do
|
||||
// both.
|
||||
//
|
||||
// Once that happens, the NAC has a frame. So if we have no frame here,
|
||||
// we're either not NAC, or in the process of doing (b). Either way, this
|
||||
// isn't a PE.
|
||||
nsIFrame* f = aContent->GetPrimaryFrame();
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the pseudo type.
|
||||
CSSPseudoElementType pseudoType = f->StyleContext()->GetPseudoType();
|
||||
|
||||
// In general nodes never get anonymous box style. However, there are a few
|
||||
// special cases:
|
||||
//
|
||||
// * We somewhat-confusingly give text nodes a style context tagged with
|
||||
// ":-moz-text", so we need to check for the anonymous box case here.
|
||||
// * The primary frame for table elements is an anonymous box that inherits
|
||||
// from the table's style.
|
||||
if (pseudoType == CSSPseudoElementType::AnonBox) {
|
||||
MOZ_ASSERT(f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::mozText ||
|
||||
f->StyleContext()->GetPseudo() == nsCSSAnonBoxes::tableWrapper);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally check the actual pseudo type.
|
||||
bool isImpl = pseudoType != CSSPseudoElementType::NotPseudo;
|
||||
MOZ_ASSERT_IF(isImpl, aContent->IsRootOfNativeAnonymousSubtree());
|
||||
return isImpl;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
StyleChildrenIterator::IsNeeded(Element* aElement)
|
||||
{
|
||||
// If the node is in an anonymous subtree, we conservatively return true to
|
||||
// handle insertion points.
|
||||
if (aElement->IsInAnonymousSubtree()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the node has an XBL binding with anonymous content return true.
|
||||
if (aElement->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
|
||||
nsBindingManager* manager = aElement->OwnerDoc()->BindingManager();
|
||||
nsXBLBinding* binding = manager->GetBindingWithContent(aElement);
|
||||
if (binding && binding->GetAnonymousContent()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the node has native anonymous content, return true.
|
||||
nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
|
||||
if (ac) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The root element has a scroll frame that is not the primary frame, so we
|
||||
// need to do special checking for that case.
|
||||
if (aElement == aElement->OwnerDoc()->GetRootElement()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
nsIContent*
|
||||
StyleChildrenIterator::GetNextChild()
|
||||
{
|
||||
while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
|
||||
if (IsNativeAnonymousImplementationOfPseudoElement(child)) {
|
||||
// Skip any native-anonymous children that are used to implement pseudo-
|
||||
// elements. These match pseudo-element selectors instead of being
|
||||
// considered a child of their host, and thus the style system needs to
|
||||
// handle them separately.
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -225,6 +225,10 @@ public:
|
||||
IteratorPhase Phase() const { return mPhase; }
|
||||
|
||||
private:
|
||||
// Helpers.
|
||||
void AppendNativeAnonymousChildren();
|
||||
void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame);
|
||||
|
||||
nsIContent* mOriginalContent;
|
||||
|
||||
// mAnonKids is an array of native anonymous children, mAnonKidsIdx is index
|
||||
@ -245,6 +249,33 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* StyleChildrenIterator traverses the children of the element from the
|
||||
* perspective of the style system, particularly the children we need to traverse
|
||||
* during restyle. This is identical to AllChildrenIterator with eAllChildren,
|
||||
* _except_ that we detect and skip any native anonymous children that are used
|
||||
* to implement pseudo-elements (since the style system needs to cascade those
|
||||
* using different algorithms).
|
||||
*
|
||||
* Note: it assumes that no mutation of the DOM or frame tree takes place during
|
||||
* iteration, and will break horribly if that is not true.
|
||||
*/
|
||||
class StyleChildrenIterator : private AllChildrenIterator {
|
||||
public:
|
||||
explicit StyleChildrenIterator(nsIContent* aContent)
|
||||
: AllChildrenIterator(aContent, nsIContent::eAllChildren)
|
||||
{
|
||||
MOZ_COUNT_CTOR(StyleChildrenIterator);
|
||||
}
|
||||
~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
|
||||
|
||||
nsIContent* GetNextChild();
|
||||
|
||||
// Returns true if we cannot find all the children we need to style by
|
||||
// traversing the siblings of the first child.
|
||||
static bool IsNeeded(Element* aParent);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -1732,6 +1732,18 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
}
|
||||
}
|
||||
|
||||
// It would be cleanest to mark nodes as dirty when (a) they're created and
|
||||
// (b) they're unbound from a tree. However, we can't easily do (a) right now,
|
||||
// because IsStyledByServo() is not always easy to check at node creation time,
|
||||
// and the bits have different meaning in the non-IsStyledByServo case.
|
||||
//
|
||||
// So for now, we just mark nodes as dirty when they're inserted into a
|
||||
// document or shadow tree.
|
||||
if (IsStyledByServo() && IsInComposedDoc()) {
|
||||
MOZ_ASSERT(!ServoData().get());
|
||||
SetIsDirtyForServo();
|
||||
}
|
||||
|
||||
// XXXbz script execution during binding can trigger some of these
|
||||
// postcondition asserts.... But we do want that, since things will
|
||||
// generally be quite broken when that happens.
|
||||
@ -1840,11 +1852,15 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
|
||||
ClearInDocument();
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
// Drop any servo node data, since it will generally need to be recomputed on
|
||||
// re-insertion anyway.
|
||||
// Computed styled data isn't useful for detached nodes, and we'll need to
|
||||
// recomputed it anyway if we ever insert the nodes back into a document.
|
||||
if (IsStyledByServo()) {
|
||||
ServoData().reset();
|
||||
} else {
|
||||
#ifdef MOZ_STYLO
|
||||
MOZ_ASSERT(!ServoData());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Editable descendant count only counts descendants that
|
||||
// are in the uncomposed document.
|
||||
|
@ -2387,3 +2387,51 @@ FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope)
|
||||
shadowRoot = shadowRoot->GetOlderShadowRoot();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
AssertDirtyDescendantsBitPropagated(nsINode* aNode)
|
||||
{
|
||||
MOZ_ASSERT(aNode->HasDirtyDescendantsForServo());
|
||||
nsINode* parent = aNode->GetFlattenedTreeParentNode();
|
||||
if (!parent->IsContent()) {
|
||||
MOZ_ASSERT(parent == aNode->OwnerDoc());
|
||||
MOZ_ASSERT(parent->HasDirtyDescendantsForServo());
|
||||
} else {
|
||||
AssertDirtyDescendantsBitPropagated(parent);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void AssertDirtyDescendantsBitPropagated(nsINode* aNode) {}
|
||||
#endif
|
||||
|
||||
void
|
||||
nsIContent::MarkAncestorsAsHavingDirtyDescendantsForServo()
|
||||
{
|
||||
MOZ_ASSERT(IsInComposedDoc());
|
||||
|
||||
// Get the parent in the flattened tree.
|
||||
nsINode* parent = GetFlattenedTreeParentNode();
|
||||
|
||||
// Loop until we hit a base case.
|
||||
while (true) {
|
||||
|
||||
// Base case: the document.
|
||||
if (!parent->IsContent()) {
|
||||
MOZ_ASSERT(parent == OwnerDoc());
|
||||
parent->SetHasDirtyDescendantsForServo();
|
||||
return;
|
||||
}
|
||||
|
||||
// Base case: the parent is already marked, and therefore
|
||||
// so are all its ancestors.
|
||||
if (parent->HasDirtyDescendantsForServo()) {
|
||||
AssertDirtyDescendantsBitPropagated(parent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the parent and iterate.
|
||||
parent->SetHasDirtyDescendantsForServo();
|
||||
parent = parent->GetFlattenedTreeParentNode();
|
||||
}
|
||||
}
|
||||
|
@ -1468,7 +1468,7 @@ nsIDocument::nsIDocument()
|
||||
mHasScrollLinkedEffect(false),
|
||||
mUserHasInteracted(false)
|
||||
{
|
||||
SetIsDocument();
|
||||
SetIsInDocument();
|
||||
|
||||
PR_INIT_CLIST(&mDOMMediaQueryLists);
|
||||
}
|
||||
|
@ -559,6 +559,18 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
||||
UpdateEditableState(false);
|
||||
|
||||
// It would be cleanest to mark nodes as dirty when (a) they're created and
|
||||
// (b) they're unbound from a tree. However, we can't easily do (a) right now,
|
||||
// because IsStyledByServo() is not always easy to check at node creation time,
|
||||
// and the bits have different meaning in the non-IsStyledByServo case.
|
||||
//
|
||||
// So for now, we just mark nodes as dirty when they're inserted into a
|
||||
// document or shadow tree.
|
||||
if (IsStyledByServo() && IsInComposedDoc()) {
|
||||
MOZ_ASSERT(!ServoData().get());
|
||||
SetIsDirtyForServo();
|
||||
}
|
||||
|
||||
NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document");
|
||||
NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
|
||||
NS_POSTCONDITION(aBindingParent == GetBindingParent(),
|
||||
@ -590,11 +602,15 @@ nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
}
|
||||
ClearInDocument();
|
||||
|
||||
#ifdef MOZ_STYLO
|
||||
// Drop any servo node data, since it will generally need to be recomputed on
|
||||
// re-insertion anyway.
|
||||
// Computed styled data isn't useful for detached nodes, and we'll need to
|
||||
// recomputed it anyway if we ever insert the nodes back into a document.
|
||||
if (IsStyledByServo()) {
|
||||
ServoData().reset();
|
||||
} else {
|
||||
#ifdef MOZ_STYLO
|
||||
MOZ_ASSERT(!ServoData());
|
||||
#endif
|
||||
}
|
||||
|
||||
if (aNullParent || !mParent->IsInShadowTree()) {
|
||||
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
|
||||
|
@ -938,6 +938,14 @@ public:
|
||||
*/
|
||||
mozilla::dom::Element* GetEditingHost();
|
||||
|
||||
|
||||
/**
|
||||
* Set NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO all the way up the flattened
|
||||
* parent chain to the document. If an ancestor is found with the bit already
|
||||
* set, this method asserts that all of its ancestors also have the bit set.
|
||||
*/
|
||||
void MarkAncestorsAsHavingDirtyDescendantsForServo();
|
||||
|
||||
/**
|
||||
* Determining language. Look at the nearest ancestor element that has a lang
|
||||
* attribute in the XML namespace or is an HTML/SVG element and has a lang in
|
||||
|
@ -1000,33 +1000,48 @@ public:
|
||||
bool IsStyledByServo() const { return false; }
|
||||
#endif
|
||||
|
||||
inline bool IsDirtyForServo() const
|
||||
bool IsDirtyForServo() const
|
||||
{
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
return HasFlag(NODE_IS_DIRTY_FOR_SERVO);
|
||||
}
|
||||
|
||||
inline bool HasDirtyDescendantsForServo() const
|
||||
bool HasDirtyDescendantsForServo() const
|
||||
{
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
||||
}
|
||||
|
||||
inline void SetIsDirtyForServo() {
|
||||
void SetIsDirtyForServo() {
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
SetFlags(NODE_IS_DIRTY_FOR_SERVO);
|
||||
}
|
||||
|
||||
inline void SetHasDirtyDescendantsForServo() {
|
||||
void SetHasDirtyDescendantsForServo() {
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
||||
}
|
||||
|
||||
inline void SetIsDirtyAndHasDirtyDescendantsForServo() {
|
||||
void SetIsDirtyAndHasDirtyDescendantsForServo() {
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO);
|
||||
}
|
||||
|
||||
void UnsetIsDirtyForServo() {
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
|
||||
}
|
||||
|
||||
void UnsetHasDirtyDescendantsForServo() {
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
||||
}
|
||||
|
||||
void UnsetIsDirtyAndHasDirtyDescendantsForServo() {
|
||||
MOZ_ASSERT(IsStyledByServo());
|
||||
UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO);
|
||||
}
|
||||
|
||||
inline void UnsetRestyleFlagsIfGecko();
|
||||
|
||||
/**
|
||||
@ -1751,17 +1766,7 @@ public:
|
||||
}
|
||||
protected:
|
||||
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
|
||||
/**
|
||||
* This is a special case of SetIsInDocument used to special-case it for the
|
||||
* document constructor (which can't do the IsStyledByServo() check).
|
||||
*/
|
||||
void SetIsDocument() { SetBoolFlag(IsInDocument); }
|
||||
void SetIsInDocument() {
|
||||
if (IsStyledByServo()) {
|
||||
SetIsDirtyAndHasDirtyDescendantsForServo();
|
||||
}
|
||||
SetBoolFlag(IsInDocument);
|
||||
}
|
||||
void SetIsInDocument() { SetBoolFlag(IsInDocument); }
|
||||
void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
|
||||
void ClearInDocument() { ClearBoolFlag(IsInDocument); }
|
||||
void SetIsElement() { SetBoolFlag(NodeIsElement); }
|
||||
|
@ -226,6 +226,8 @@ HTMLMenuElement::TraverseContent(nsIContent* aContent,
|
||||
aBuilder->AddItemFor(menuitem, CanLoadIcon(child, icon));
|
||||
|
||||
aSeparator = ST_FALSE;
|
||||
} else if (child->IsHTMLElement(nsGkAtoms::hr)) {
|
||||
aBuilder->AddSeparator();
|
||||
} else if (child->IsHTMLElement(nsGkAtoms::menu) && !element->IsHidden()) {
|
||||
if (child->HasAttr(kNameSpaceID_None, nsGkAtoms::label)) {
|
||||
nsAutoString label;
|
||||
|
@ -23,7 +23,12 @@ add_task(function* () {
|
||||
let pageMenuSep = document.getElementById("page-menu-separator");
|
||||
ok(pageMenuSep && !pageMenuSep.hidden,
|
||||
"Page menu separator should be shown");
|
||||
let testMenuItem = pageMenuSep.previousSibling;
|
||||
|
||||
let testMenuSep = pageMenuSep.previousSibling;
|
||||
ok(testMenuSep && !testMenuSep.hidden,
|
||||
"User-added menu separator should be shown");
|
||||
|
||||
let testMenuItem = testMenuSep.previousSibling;
|
||||
is(testMenuItem.label, "Test Context Menu Click", "Got context menu item");
|
||||
|
||||
let promiseCtxMenuClick = ContentTask.spawn(aBrowser, null, function*() {
|
||||
|
@ -13,7 +13,8 @@
|
||||
</head>
|
||||
<body contextmenu="testmenu">
|
||||
<menu type="context" id="testmenu">
|
||||
<menuitem label="Test Context Menu Click" id="menuitem">
|
||||
<menuitem label="Test Context Menu Click" id="menuitem"></menuitem>
|
||||
<hr>
|
||||
</menu>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -304,10 +304,8 @@ nsPluginHost::nsPluginHost()
|
||||
Preferences::GetBool("plugin.override_internal_types", false);
|
||||
|
||||
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
|
||||
mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
|
||||
|
||||
Preferences::AddStrongObserver(this, "plugin.disable");
|
||||
Preferences::AddStrongObserver(this, "plugins.click_to_play");
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsService =
|
||||
mozilla::services::GetObserverService();
|
||||
@ -3624,7 +3622,6 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
|
||||
}
|
||||
if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
|
||||
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
|
||||
mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
|
||||
// Unload or load plugins as needed
|
||||
if (mPluginsDisabled) {
|
||||
UnloadPlugins();
|
||||
|
@ -386,8 +386,6 @@ private:
|
||||
|
||||
// set by pref plugin.disable
|
||||
bool mPluginsDisabled;
|
||||
// set by pref plugins.click_to_play
|
||||
bool mPluginsClickToPlay;
|
||||
|
||||
// Any instances in this array will have valid plugin objects via GetPlugin().
|
||||
// When removing an instance it might not die - be sure to null out it's plugin.
|
||||
|
@ -65,10 +65,55 @@ function compileManySuccess() {
|
||||
);
|
||||
}
|
||||
|
||||
function compileInWorker() {
|
||||
var w = new Worker(`data:text/plain,
|
||||
onmessage = e => {
|
||||
WebAssembly.compile(e.data).then(m => {
|
||||
var i = new WebAssembly.Instance(m);
|
||||
if (i.exports.foo() !== 42)
|
||||
throw "bad i.exports.foo() result";
|
||||
postMessage("ok");
|
||||
close();
|
||||
}).catch(err => { throw err });
|
||||
}
|
||||
`);
|
||||
w.postMessage(fooModuleCode);
|
||||
w.onmessage = e => {
|
||||
ok(e.data === "ok", "worker test");
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
function terminateCompileInWorker() {
|
||||
var w = new Worker(`data:text/plain,
|
||||
var fooModuleCode;
|
||||
function spawnWork() {
|
||||
const N = 100;
|
||||
var arr = [];
|
||||
for (var i = 0; i < N; i++)
|
||||
arr.push(WebAssembly.compile(fooModuleCode));
|
||||
Promise.all(arr).then(spawnWork);
|
||||
}
|
||||
onmessage = e => {
|
||||
fooModuleCode = e.data;
|
||||
spawnWork();
|
||||
postMessage("ok");
|
||||
}
|
||||
`);
|
||||
w.postMessage(fooModuleCode);
|
||||
w.onmessage = e => {
|
||||
ok(e.data === "ok", "worker finished first step");
|
||||
w.terminate();
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
var tests = [ propertiesExist,
|
||||
compileFail,
|
||||
compileSuccess,
|
||||
compileManySuccess
|
||||
compileManySuccess,
|
||||
compileInWorker,
|
||||
terminateCompileInWorker
|
||||
];
|
||||
|
||||
function runTest() {
|
||||
|
@ -269,16 +269,6 @@ SVGUseElement::CreateAnonymousContent()
|
||||
if (!newcontent)
|
||||
return nullptr;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Our anonymous clone can get restyled by various things
|
||||
// (e.g. SMIL). Reconstructing its frame is OK, though, because
|
||||
// it's going to be our _only_ child in the frame tree, so can't get
|
||||
// mis-ordered with anything.
|
||||
newcontent->SetProperty(nsGkAtoms::restylableAnonymousNode,
|
||||
reinterpret_cast<void*>(true));
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
if (newcontent->IsSVGElement(nsGkAtoms::symbol)) {
|
||||
nsIDocument *document = GetComposedDoc();
|
||||
if (!document)
|
||||
@ -341,6 +331,16 @@ SVGUseElement::CreateAnonymousContent()
|
||||
|
||||
targetContent->AddMutationObserver(this);
|
||||
mClone = newcontent;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Our anonymous clone can get restyled by various things
|
||||
// (e.g. SMIL). Reconstructing its frame is OK, though, because
|
||||
// it's going to be our _only_ child in the frame tree, so can't get
|
||||
// mis-ordered with anything.
|
||||
mClone->SetProperty(nsGkAtoms::restylableAnonymousNode,
|
||||
reinterpret_cast<void*>(true));
|
||||
#endif // DEBUG
|
||||
|
||||
return mClone;
|
||||
}
|
||||
|
||||
|
16
dom/svg/crashtests/1250725.html
Normal file
16
dom/svg/crashtests/1250725.html
Normal file
@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
|
||||
<script>
|
||||
function boom()
|
||||
{
|
||||
document.getElementById("u").setAttribute("width", "30");
|
||||
document.getElementById("p").remove();
|
||||
}
|
||||
window.addEventListener("load", boom, false);
|
||||
</script>
|
||||
|
||||
<symbol id="p" viewBox="0 0 100 20"/>
|
||||
|
||||
<use id="u" xlink:href="#p"/>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 393 B |
@ -75,6 +75,7 @@ load 898915-1.svg
|
||||
load 1035248-1.svg
|
||||
load 1035248-2.svg
|
||||
load 1244898-1.xhtml
|
||||
load 1250725.html
|
||||
load 1267272-1.svg
|
||||
load 1282985-1.svg
|
||||
# Disabled for now due to it taking a very long time to run - bug 1259356
|
||||
|
@ -352,7 +352,7 @@ VRPose::GetLinearAcceleration(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mLinearAcceleration && mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) {
|
||||
if (!mLinearAcceleration && mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration) {
|
||||
// Lazily create the Float32Array
|
||||
mLinearAcceleration = dom::Float32Array::Create(aCx, this, 3, mVRState.linearAcceleration);
|
||||
if (!mLinearAcceleration) {
|
||||
@ -409,7 +409,7 @@ VRPose::GetAngularAcceleration(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!mAngularAcceleration && mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation) {
|
||||
if (!mAngularAcceleration && mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration) {
|
||||
// Lazily create the Float32Array
|
||||
mAngularAcceleration = dom::Float32Array::Create(aCx, this, 3, mVRState.angularAcceleration);
|
||||
if (!mAngularAcceleration) {
|
||||
@ -441,9 +441,15 @@ VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient)
|
||||
, mDepthNear(0.01f) // Default value from WebVR Spec
|
||||
, mDepthFar(10000.0f) // Default value from WebVR Spec
|
||||
{
|
||||
mDisplayId = aClient->GetDisplayInfo().GetDisplayID();
|
||||
mDisplayName = NS_ConvertASCIItoUTF16(aClient->GetDisplayInfo().GetDisplayName());
|
||||
mCapabilities = new VRDisplayCapabilities(aWindow, aClient->GetDisplayInfo().GetCapabilities());
|
||||
const gfx::VRDisplayInfo& info = aClient->GetDisplayInfo();
|
||||
mDisplayId = info.GetDisplayID();
|
||||
mDisplayName = NS_ConvertASCIItoUTF16(info.GetDisplayName());
|
||||
mCapabilities = new VRDisplayCapabilities(aWindow, info.GetCapabilities());
|
||||
if (info.GetCapabilities() & gfx::VRDisplayCapabilityFlags::Cap_StageParameters) {
|
||||
mStageParameters = new VRStageParameters(aWindow,
|
||||
info.GetSittingToStandingTransform(),
|
||||
info.GetStageSize());
|
||||
}
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
@ -482,9 +488,7 @@ VRDisplay::Capabilities()
|
||||
VRStageParameters*
|
||||
VRDisplay::GetStageParameters()
|
||||
{
|
||||
// XXX When we implement room scale experiences for OpenVR, we should return
|
||||
// something here.
|
||||
return nullptr;
|
||||
return mStageParameters;
|
||||
}
|
||||
|
||||
already_AddRefed<VRPose>
|
||||
@ -645,7 +649,7 @@ VRDisplay::IsConnected() const
|
||||
return mClient->GetIsConnected();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper, mCapabilities)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper, mCapabilities, mStageParameters)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(VRDisplay, DOMEventTargetHelper)
|
||||
|
@ -283,6 +283,7 @@ protected:
|
||||
nsString mDisplayName;
|
||||
|
||||
RefPtr<VRDisplayCapabilities> mCapabilities;
|
||||
RefPtr<VRStageParameters> mStageParameters;
|
||||
|
||||
double mDepthNear;
|
||||
double mDepthFar;
|
||||
|
@ -713,6 +713,185 @@ AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
|
||||
aSize, aMemory, aHandle);
|
||||
}
|
||||
|
||||
class AsyncTaskWorkerHolder final : public WorkerHolder
|
||||
{
|
||||
bool Notify(Status aStatus) override
|
||||
{
|
||||
// The async task must complete in bounded time and there is not (currently)
|
||||
// a clean way to cancel it. Async tasks do not run arbitrary content.
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
WorkerPrivate* Worker() const
|
||||
{
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
};
|
||||
|
||||
template <class RunnableBase>
|
||||
class AsyncTaskBase : public RunnableBase
|
||||
{
|
||||
UniquePtr<AsyncTaskWorkerHolder> mHolder;
|
||||
|
||||
// Disable the usual pre/post-dispatch thread assertions since we are
|
||||
// dispatching from some random JS engine internal thread:
|
||||
|
||||
bool PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{ }
|
||||
|
||||
protected:
|
||||
explicit AsyncTaskBase(UniquePtr<AsyncTaskWorkerHolder> aHolder)
|
||||
: RunnableBase(aHolder->Worker(),
|
||||
WorkerRunnable::WorkerThreadUnchangedBusyCount)
|
||||
, mHolder(Move(aHolder))
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
}
|
||||
|
||||
~AsyncTaskBase()
|
||||
{
|
||||
MOZ_ASSERT(!mHolder);
|
||||
}
|
||||
|
||||
void DestroyHolder()
|
||||
{
|
||||
MOZ_ASSERT(mHolder);
|
||||
mHolder.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
UniquePtr<AsyncTaskWorkerHolder> StealHolder()
|
||||
{
|
||||
return Move(mHolder);
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncTaskRunnable final : public AsyncTaskBase<WorkerRunnable>
|
||||
{
|
||||
JS::AsyncTask* mTask;
|
||||
|
||||
~AsyncTaskRunnable()
|
||||
{
|
||||
MOZ_ASSERT(!mTask);
|
||||
}
|
||||
|
||||
void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{
|
||||
// For the benefit of the destructor assert.
|
||||
if (!aDispatchResult) {
|
||||
mTask = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AsyncTaskRunnable(UniquePtr<AsyncTaskWorkerHolder> aHolder,
|
||||
JS::AsyncTask* aTask)
|
||||
: AsyncTaskBase<WorkerRunnable>(Move(aHolder))
|
||||
, mTask(aTask)
|
||||
{
|
||||
MOZ_ASSERT(mTask);
|
||||
}
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
|
||||
MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext());
|
||||
MOZ_ASSERT(mTask);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
mTask->finish(mWorkerPrivate->GetJSContext());
|
||||
mTask = nullptr; // mTask may delete itself
|
||||
|
||||
DestroyHolder();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult Cancel() override
|
||||
{
|
||||
MOZ_ASSERT(mTask);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
mTask->cancel(mWorkerPrivate->GetJSContext());
|
||||
mTask = nullptr; // mTask may delete itself
|
||||
|
||||
DestroyHolder();
|
||||
|
||||
return WorkerRunnable::Cancel();
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncTaskControlRunnable final
|
||||
: public AsyncTaskBase<WorkerControlRunnable>
|
||||
{
|
||||
public:
|
||||
explicit AsyncTaskControlRunnable(UniquePtr<AsyncTaskWorkerHolder> aHolder)
|
||||
: AsyncTaskBase<WorkerControlRunnable>(Move(aHolder))
|
||||
{ }
|
||||
|
||||
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
// See comment in FinishAsyncTaskCallback.
|
||||
DestroyHolder();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
StartAsyncTaskCallback(JSContext* aCx, JS::AsyncTask* aTask)
|
||||
{
|
||||
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
auto holder = MakeUnique<AsyncTaskWorkerHolder>();
|
||||
if (!holder->HoldWorker(worker, Status::Closing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Matched by a UniquePtr in FinishAsyncTaskCallback which, by
|
||||
// interface contract, must be called in the future.
|
||||
aTask->user = holder.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
FinishAsyncTaskCallback(JS::AsyncTask* aTask)
|
||||
{
|
||||
// May execute either on the worker thread or a random JS-internal helper
|
||||
// thread.
|
||||
|
||||
// Match the release() in StartAsyncTaskCallback.
|
||||
UniquePtr<AsyncTaskWorkerHolder> holder(
|
||||
static_cast<AsyncTaskWorkerHolder*>(aTask->user));
|
||||
|
||||
RefPtr<AsyncTaskRunnable> r = new AsyncTaskRunnable(Move(holder), aTask);
|
||||
|
||||
// WorkerRunnable::Dispatch() can fail during worker shutdown. In that case,
|
||||
// report failure back to the JS engine but make sure to release the
|
||||
// WorkerHolder on the worker thread using a control runnable. Control
|
||||
// runables aren't suitable for calling AsyncTask::finish() since they are run
|
||||
// via the interrupt callback which breaks JS run-to-completion.
|
||||
if (!r->Dispatch()) {
|
||||
RefPtr<AsyncTaskControlRunnable> cr =
|
||||
new AsyncTaskControlRunnable(r->StealHolder());
|
||||
|
||||
MOZ_ALWAYS_TRUE(cr->Dispatch());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class WorkerJSRuntime;
|
||||
|
||||
class WorkerThreadContextPrivate : private PerThreadAtomCache
|
||||
@ -808,6 +987,8 @@ InitJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSContext* aWorkerCx)
|
||||
};
|
||||
JS::SetAsmJSCacheOps(aWorkerCx, &asmJSCacheOps);
|
||||
|
||||
JS::SetAsyncTaskCallbacks(aWorkerCx, StartAsyncTaskCallback, FinishAsyncTaskCallback);
|
||||
|
||||
if (!JS::InitSelfHostedCode(aWorkerCx)) {
|
||||
NS_WARNING("Could not init self-hosted code!");
|
||||
return false;
|
||||
|
@ -206,10 +206,6 @@ nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElem
|
||||
// (2) The children's parent back pointer should not be to this synthetic root
|
||||
// but should instead point to the enclosing parent element.
|
||||
nsIDocument* doc = aElement->GetUncomposedDoc();
|
||||
ServoStyleSet* servoStyleSet = nullptr;
|
||||
if (nsIPresShell* presShell = aElement->OwnerDoc()->GetShell()) {
|
||||
servoStyleSet = presShell->StyleSet()->GetAsServo();
|
||||
}
|
||||
bool allowScripts = AllowScripts();
|
||||
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
@ -240,10 +236,6 @@ nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElem
|
||||
if (xuldoc)
|
||||
xuldoc->AddSubtreeToDocument(child);
|
||||
#endif
|
||||
|
||||
if (servoStyleSet) {
|
||||
servoStyleSet->RestyleSubtree(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,6 +420,15 @@ nsXBLBinding::GenerateAnonymousContent()
|
||||
if (mContent)
|
||||
mContent->UnsetAttr(namespaceID, name, false);
|
||||
}
|
||||
|
||||
// Now that we've finished shuffling the tree around, go ahead and restyle it
|
||||
// since frame construction is about to happen.
|
||||
nsIPresShell* presShell = mBoundElement->OwnerDoc()->GetShell();
|
||||
ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo();
|
||||
if (servoSet) {
|
||||
mBoundElement->SetHasDirtyDescendantsForServo();
|
||||
servoSet->StyleNewChildren(mBoundElement);
|
||||
}
|
||||
}
|
||||
|
||||
XBLChildrenElement*
|
||||
|
@ -116,6 +116,12 @@ struct BarrierMethods<nsXBLMaybeCompiled<UncompiledT>>
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct IsHeapConstructibleType<nsXBLMaybeCompiled<T>>
|
||||
{ // Yes, this is the exception to the rule. Sorry.
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template <class UncompiledT>
|
||||
class HeapBase<nsXBLMaybeCompiled<UncompiledT>>
|
||||
{
|
||||
|
@ -311,7 +311,7 @@ DrawTargetSkia::ReleaseBits(uint8_t* aData)
|
||||
}
|
||||
|
||||
static void
|
||||
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0)
|
||||
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Point aOffset = Point(0, 0))
|
||||
{
|
||||
switch (aPattern.GetType()) {
|
||||
case PatternType::COLOR: {
|
||||
@ -333,6 +333,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0)
|
||||
|
||||
SkMatrix mat;
|
||||
GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
|
||||
mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
|
||||
sk_sp<SkShader> shader = SkGradientShader::MakeLinear(points,
|
||||
&stops->mColors.front(),
|
||||
&stops->mPositions.front(),
|
||||
@ -357,6 +358,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0)
|
||||
|
||||
SkMatrix mat;
|
||||
GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
|
||||
mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
|
||||
sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(points[0],
|
||||
SkFloatToScalar(pat.mRadius1),
|
||||
points[1],
|
||||
@ -375,6 +377,7 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0)
|
||||
|
||||
SkMatrix mat;
|
||||
GfxMatrixToSkiaMatrix(pat.mMatrix, mat);
|
||||
mat.postTranslate(SkFloatToScalar(aOffset.x), SkFloatToScalar(aOffset.y));
|
||||
|
||||
if (!pat.mSamplingRect.IsEmpty()) {
|
||||
SkIRect rect = IntRectToSkIRect(pat.mSamplingRect);
|
||||
@ -415,17 +418,17 @@ GetClipBounds(SkCanvas *aCanvas)
|
||||
}
|
||||
|
||||
struct AutoPaintSetup {
|
||||
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr)
|
||||
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Pattern& aPattern, const Rect* aMaskBounds = nullptr, Point aOffset = Point(0, 0))
|
||||
: mNeedsRestore(false), mAlpha(1.0)
|
||||
{
|
||||
Init(aCanvas, aOptions, aMaskBounds);
|
||||
SetPaintPattern(mPaint, aPattern, mAlpha);
|
||||
Init(aCanvas, aOptions, aMaskBounds, false);
|
||||
SetPaintPattern(mPaint, aPattern, mAlpha, aOffset);
|
||||
}
|
||||
|
||||
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr)
|
||||
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
|
||||
: mNeedsRestore(false), mAlpha(1.0)
|
||||
{
|
||||
Init(aCanvas, aOptions, aMaskBounds);
|
||||
Init(aCanvas, aOptions, aMaskBounds, aForceGroup);
|
||||
}
|
||||
|
||||
~AutoPaintSetup()
|
||||
@ -435,7 +438,7 @@ struct AutoPaintSetup {
|
||||
}
|
||||
}
|
||||
|
||||
void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds)
|
||||
void Init(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds, bool aForceGroup)
|
||||
{
|
||||
mPaint.setXfermodeMode(GfxOpToSkiaOp(aOptions.mCompositionOp));
|
||||
mCanvas = aCanvas;
|
||||
@ -447,8 +450,9 @@ struct AutoPaintSetup {
|
||||
mPaint.setAntiAlias(false);
|
||||
}
|
||||
|
||||
bool needsGroup = !IsOperatorBoundByMask(aOptions.mCompositionOp) &&
|
||||
(!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas)));
|
||||
bool needsGroup = aForceGroup ||
|
||||
(!IsOperatorBoundByMask(aOptions.mCompositionOp) &&
|
||||
(!aMaskBounds || !aMaskBounds->Contains(GetClipBounds(aCanvas))));
|
||||
|
||||
// TODO: We could skip the temporary for operator_source and just
|
||||
// clear the clip rect. The other operators would be harder
|
||||
@ -507,10 +511,11 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
|
||||
|
||||
SkRect destRect = RectToSkRect(aDest);
|
||||
SkRect sourceRect = RectToSkRect(aSource);
|
||||
|
||||
SkBitmap bitmap = GetBitmapForSurface(aSurface);
|
||||
bool forceGroup = bitmap.colorType() == kAlpha_8_SkColorType &&
|
||||
aOptions.mCompositionOp != CompositionOp::OP_OVER;
|
||||
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest);
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, &aDest, forceGroup);
|
||||
if (aSurfOptions.mSamplingFilter == SamplingFilter::POINT) {
|
||||
paint.mPaint.setFilterQuality(kNone_SkFilterQuality);
|
||||
}
|
||||
@ -1311,7 +1316,7 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
|
||||
const DrawOptions &aOptions)
|
||||
{
|
||||
MarkChanged();
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
|
||||
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
|
||||
|
||||
SkBitmap bitmap = GetBitmapForSurface(aMask);
|
||||
if (bitmap.colorType() != kAlpha_8_SkColorType &&
|
||||
@ -1320,14 +1325,6 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
|
||||
return;
|
||||
}
|
||||
|
||||
if (aOffset != Point(0, 0) &&
|
||||
paint.mPaint.getShader()) {
|
||||
SkMatrix transform;
|
||||
transform.setTranslate(PointToSkPoint(-aOffset));
|
||||
sk_sp<SkShader> matrixShader = paint.mPaint.getShader()->makeWithLocalMatrix(transform);
|
||||
paint.mPaint.setShader(matrixShader);
|
||||
}
|
||||
|
||||
mCanvas->drawBitmap(bitmap, aOffset.x, aOffset.y, &paint.mPaint);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,9 @@ class gfxVarReceiver;
|
||||
_(ContentBackend, BackendType, BackendType::NONE) \
|
||||
_(TileSize, IntSize, IntSize(-1, -1)) \
|
||||
_(UseXRender, bool, false) \
|
||||
_(OffscreenFormat, gfxImageFormat, mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) \
|
||||
_(RequiresAcceleratedGLContextForCompositorOGL, bool, false) \
|
||||
|
||||
/* Add new entries above this line. */
|
||||
|
||||
// Some graphics settings are computed on the UI process and must be
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
#include "nsDebugImpl.h"
|
||||
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
||||
#include "ProcessUtils.h"
|
||||
#include "VRManager.h"
|
||||
#include "VRManagerParent.h"
|
||||
#include "VsyncBridgeParent.h"
|
||||
@ -64,6 +65,7 @@ GPUParent::Init(base::ProcessId aParentPid,
|
||||
CompositorThreadHolder::Start();
|
||||
VRManager::ManagerInit();
|
||||
LayerTreeOwnerTracker::Initialize();
|
||||
mozilla::ipc::SetThisProcessName("GPU Process");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
|
||||
using mozilla::gfx::FeatureStatus from "gfxTelemetry.h";
|
||||
using mozilla::gfx::BackendType from "mozilla/gfx/Types.h";
|
||||
using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
|
||||
using gfxImageFormat from "mozilla/gfx/Types.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
@ -65,6 +66,7 @@ union GfxVarValue
|
||||
{
|
||||
BackendType;
|
||||
bool;
|
||||
gfxImageFormat;
|
||||
IntSize;
|
||||
};
|
||||
|
||||
|
@ -64,6 +64,8 @@ IPDL_SOURCES = [
|
||||
'PVsyncBridge.ipdl',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += ['/dom/ipc']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -133,6 +133,11 @@ public:
|
||||
|
||||
void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
|
||||
|
||||
virtual void Disconnect() override
|
||||
{
|
||||
ClientLayer::Disconnect();
|
||||
}
|
||||
|
||||
protected:
|
||||
ClientLayerManager* ClientManager()
|
||||
{
|
||||
|
@ -795,34 +795,6 @@ ClientLayerManager::GetBackendName(nsAString& aName)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
|
||||
FrameMetrics& aMetrics,
|
||||
bool aDrawingCritical)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
MOZ_ASSERT(aMetrics.IsScrollable());
|
||||
// This is derived from the code in
|
||||
// gfx/layers/ipc/CompositorBridgeParent.cpp::TransformShadowTree.
|
||||
CSSToLayerScale paintScale = aMetrics.LayersPixelsPerCSSPixel().ToScaleFactor();
|
||||
const CSSRect& metricsDisplayPort =
|
||||
(aDrawingCritical && !aMetrics.GetCriticalDisplayPort().IsEmpty()) ?
|
||||
aMetrics.GetCriticalDisplayPort() : aMetrics.GetDisplayPort();
|
||||
LayerRect displayPort = (metricsDisplayPort + aMetrics.GetScrollOffset()) * paintScale;
|
||||
|
||||
ParentLayerPoint scrollOffset;
|
||||
CSSToParentLayerScale zoom;
|
||||
bool ret = AndroidBridge::Bridge()->ProgressiveUpdateCallback(
|
||||
aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical,
|
||||
scrollOffset, zoom);
|
||||
aMetrics.SetScrollOffset(scrollOffset / zoom);
|
||||
aMetrics.SetZoom(CSSToParentLayerScale2D(zoom));
|
||||
return ret;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ClientLayerManager::AsyncPanZoomEnabled() const
|
||||
{
|
||||
|
@ -155,22 +155,6 @@ public:
|
||||
// Disable component alpha layers with the software compositor.
|
||||
virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); }
|
||||
|
||||
/**
|
||||
* Called for each iteration of a progressive tile update. Updates
|
||||
* aMetrics with the current scroll offset and scale being used to composite
|
||||
* the primary scrollable layer in this manager, to determine what area
|
||||
* intersects with the target composition bounds.
|
||||
* aDrawingCritical will be true if the current drawing operation is using
|
||||
* the critical displayport.
|
||||
* Returns true if the update should continue, or false if it should be
|
||||
* cancelled.
|
||||
* This is only called if gfxPlatform::UseProgressiveTilePainting() returns
|
||||
* true.
|
||||
*/
|
||||
bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
|
||||
FrameMetrics& aMetrics,
|
||||
bool aDrawingCritical);
|
||||
|
||||
bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
|
||||
#ifdef DEBUG
|
||||
bool InDrawing() { return mPhase == PHASE_DRAWING; }
|
||||
|
@ -47,7 +47,6 @@
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/layers/PLayerTransactionParent.h"
|
||||
#include "mozilla/layers/RemoteContentController.h"
|
||||
#include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
|
||||
#include "mozilla/layout/RenderFrameParent.h"
|
||||
#include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
|
||||
#include "mozilla/mozalloc.h" // for operator new, etc
|
||||
@ -103,6 +102,82 @@ using namespace std;
|
||||
using base::ProcessId;
|
||||
using base::Thread;
|
||||
|
||||
ProcessId
|
||||
CompositorBridgeParentBase::GetChildProcessId()
|
||||
{
|
||||
return OtherPid();
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParentBase::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
|
||||
{
|
||||
RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(texture->GetFlags() & TextureFlags::RECYCLE) &&
|
||||
!texture->NeedsFenceHandle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture->GetFlags() & TextureFlags::RECYCLE) {
|
||||
SendFenceHandleIfPresent(aTexture);
|
||||
uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
|
||||
mPendingAsyncMessage.push_back(
|
||||
OpNotifyNotUsed(textureId, aTransactionId));
|
||||
return;
|
||||
}
|
||||
|
||||
// Gralloc requests to deliver fence to client side.
|
||||
// If client side does not use TextureFlags::RECYCLE flag,
|
||||
// The fence can not be delivered via LayerTransactionParent.
|
||||
// TextureClient might wait the fence delivery on main thread.
|
||||
|
||||
MOZ_ASSERT(ImageBridgeParent::GetInstance(GetChildProcessId()));
|
||||
if (ImageBridgeParent::GetInstance(GetChildProcessId())) {
|
||||
// Send message back via PImageBridge.
|
||||
ImageBridgeParent::NotifyNotUsedToNonRecycle(
|
||||
GetChildProcessId(),
|
||||
aTexture,
|
||||
aTransactionId);
|
||||
} else {
|
||||
NS_ERROR("ImageBridgeParent should exist");
|
||||
}
|
||||
|
||||
if (!IsAboutToSendAsyncMessages()) {
|
||||
SendPendingAsyncMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParentBase::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
|
||||
{
|
||||
Unused << SendParentAsyncMessages(aMessage);
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParentBase::AllocShmem(size_t aSize,
|
||||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem);
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParentBase::AllocUnsafeShmem(size_t aSize,
|
||||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParentBase::DeallocShmem(ipc::Shmem& aShmem)
|
||||
{
|
||||
PCompositorBridgeParent::DeallocShmem(aShmem);
|
||||
}
|
||||
|
||||
CompositorBridgeParent::LayerTreeState::LayerTreeState()
|
||||
: mApzcTreeManagerParent(nullptr)
|
||||
, mParent(nullptr)
|
||||
@ -2005,10 +2080,7 @@ CompositorBridgeParent::FinishPendingComposite()
|
||||
* these updates, it doesn't actually drive compositing itself. For that it
|
||||
* hands off work to the CompositorBridgeParent it's associated with.
|
||||
*/
|
||||
class CrossProcessCompositorBridgeParent final : public PCompositorBridgeParent,
|
||||
public ShadowLayersManager,
|
||||
public CompositorBridgeParentIPCAllocator,
|
||||
public ShmemAllocator
|
||||
class CrossProcessCompositorBridgeParent final : public CompositorBridgeParentBase
|
||||
{
|
||||
friend class CompositorBridgeParent;
|
||||
|
||||
@ -2152,28 +2224,6 @@ public:
|
||||
|
||||
virtual bool IsSameProcess() const override;
|
||||
|
||||
virtual ShmemAllocator* AsShmemAllocator() override { return this; }
|
||||
|
||||
virtual bool AllocShmem(size_t aSize,
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType aType,
|
||||
mozilla::ipc::Shmem* aShmem) override;
|
||||
|
||||
virtual bool AllocUnsafeShmem(size_t aSize,
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType aType,
|
||||
mozilla::ipc::Shmem* aShmem) override;
|
||||
|
||||
virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
|
||||
|
||||
virtual base::ProcessId GetChildProcessId() override
|
||||
{
|
||||
return OtherPid();
|
||||
}
|
||||
|
||||
virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override
|
||||
{
|
||||
Unused << SendParentAsyncMessages(aMessage);
|
||||
}
|
||||
|
||||
PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override {
|
||||
// Not allowed.
|
||||
return nullptr;
|
||||
@ -2191,8 +2241,6 @@ public:
|
||||
virtual PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override;
|
||||
virtual bool DeallocPAPZParent(PAPZParent* aActor) override;
|
||||
|
||||
virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() override { return this; }
|
||||
|
||||
virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override {
|
||||
uint64_t id = aLayerTree->GetId();
|
||||
MOZ_ASSERT(id != 0);
|
||||
@ -2423,34 +2471,6 @@ CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
|
||||
return TextureHost::DestroyIPDLActor(actor);
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParent::AllocShmem(size_t aSize,
|
||||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem);
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParent::AllocUnsafeShmem(size_t aSize,
|
||||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParent::DeallocShmem(ipc::Shmem& aShmem)
|
||||
{
|
||||
PCompositorBridgeParent::DeallocShmem(aShmem);
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
|
||||
{
|
||||
Unused << SendParentAsyncMessages(aMessage);
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorBridgeParent::IsSameProcess() const
|
||||
{
|
||||
@ -3087,28 +3107,6 @@ CrossProcessCompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor)
|
||||
return TextureHost::DestroyIPDLActor(actor);
|
||||
}
|
||||
|
||||
bool
|
||||
CrossProcessCompositorBridgeParent::AllocShmem(size_t aSize,
|
||||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
return PCompositorBridgeParent::AllocShmem(aSize, aType, aShmem);
|
||||
}
|
||||
|
||||
bool
|
||||
CrossProcessCompositorBridgeParent::AllocUnsafeShmem(size_t aSize,
|
||||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
return PCompositorBridgeParent::AllocUnsafeShmem(aSize, aType, aShmem);
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessCompositorBridgeParent::DeallocShmem(ipc::Shmem& aShmem)
|
||||
{
|
||||
PCompositorBridgeParent::DeallocShmem(aShmem);
|
||||
}
|
||||
|
||||
bool
|
||||
CrossProcessCompositorBridgeParent::IsSameProcess() const
|
||||
{
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "mozilla/layers/ISurfaceAllocator.h" // for ShmemAllocator
|
||||
#include "mozilla/layers/LayersMessages.h" // for TargetConfig
|
||||
#include "mozilla/layers/PCompositorBridgeParent.h"
|
||||
#include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
|
||||
#include "mozilla/layers/APZTestData.h"
|
||||
#include "mozilla/widget/CompositorWidget.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
@ -203,10 +202,57 @@ protected:
|
||||
virtual ~CompositorUpdateObserver() {}
|
||||
};
|
||||
|
||||
class CompositorBridgeParent final : public PCompositorBridgeParent,
|
||||
public ShadowLayersManager,
|
||||
public CompositorBridgeParentIPCAllocator,
|
||||
class CompositorBridgeParentBase : public PCompositorBridgeParent,
|
||||
public HostIPCAllocator,
|
||||
public ShmemAllocator
|
||||
{
|
||||
public:
|
||||
virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
||||
const uint64_t& aTransactionId,
|
||||
const TargetConfig& aTargetConfig,
|
||||
const InfallibleTArray<PluginWindowData>& aPlugins,
|
||||
bool aIsFirstPaint,
|
||||
bool aScheduleComposite,
|
||||
uint32_t aPaintSequenceNumber,
|
||||
bool aIsRepeatTransaction,
|
||||
int32_t aPaintSyncId,
|
||||
bool aHitTestUpdate) = 0;
|
||||
|
||||
virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) { return nullptr; }
|
||||
|
||||
virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { }
|
||||
|
||||
virtual void ForceComposite(LayerTransactionParent* aLayerTree) { }
|
||||
virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
|
||||
const TimeStamp& aTime) { return true; }
|
||||
virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) { }
|
||||
virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
|
||||
virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0;
|
||||
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
||||
APZTestData* aOutData) { }
|
||||
virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
|
||||
const uint64_t& aInputBlockId,
|
||||
const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
|
||||
virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
|
||||
|
||||
virtual ShmemAllocator* AsShmemAllocator() override { return this; }
|
||||
|
||||
// HostIPCAllocator
|
||||
virtual base::ProcessId GetChildProcessId() override;
|
||||
virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
|
||||
virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
|
||||
|
||||
// ShmemAllocator
|
||||
virtual bool AllocShmem(size_t aSize,
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType aType,
|
||||
mozilla::ipc::Shmem* aShmem) override;
|
||||
virtual bool AllocUnsafeShmem(size_t aSize,
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType aType,
|
||||
mozilla::ipc::Shmem* aShmem) override;
|
||||
virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
|
||||
};
|
||||
|
||||
class CompositorBridgeParent final : public CompositorBridgeParentBase
|
||||
{
|
||||
friend class CompositorVsyncScheduler;
|
||||
friend class CompositorThreadHolder;
|
||||
@ -306,30 +352,10 @@ public:
|
||||
|
||||
virtual bool IsSameProcess() const override;
|
||||
|
||||
virtual ShmemAllocator* AsShmemAllocator() override { return this; }
|
||||
|
||||
virtual bool AllocShmem(size_t aSize,
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType aType,
|
||||
mozilla::ipc::Shmem* aShmem) override;
|
||||
|
||||
virtual bool AllocUnsafeShmem(size_t aSize,
|
||||
mozilla::ipc::SharedMemory::SharedMemoryType aType,
|
||||
mozilla::ipc::Shmem* aShmem) override;
|
||||
|
||||
virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
|
||||
|
||||
PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
|
||||
bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
|
||||
|
||||
virtual base::ProcessId GetChildProcessId() override
|
||||
{
|
||||
return OtherPid();
|
||||
}
|
||||
|
||||
virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
|
||||
|
||||
virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() override { return this; }
|
||||
|
||||
/**
|
||||
* Request that the compositor be recreated due to a shared device reset.
|
||||
* This must be called on the main thread, and blocks until a task posted
|
||||
|
@ -87,48 +87,6 @@ HostIPCAllocator::SendPendingAsyncMessages()
|
||||
mPendingAsyncMessage.clear();
|
||||
}
|
||||
|
||||
void
|
||||
CompositorBridgeParentIPCAllocator::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
|
||||
{
|
||||
RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(texture->GetFlags() & TextureFlags::RECYCLE) &&
|
||||
!texture->NeedsFenceHandle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture->GetFlags() & TextureFlags::RECYCLE) {
|
||||
SendFenceHandleIfPresent(aTexture);
|
||||
uint64_t textureId = TextureHost::GetTextureSerial(aTexture);
|
||||
mPendingAsyncMessage.push_back(
|
||||
OpNotifyNotUsed(textureId, aTransactionId));
|
||||
return;
|
||||
}
|
||||
|
||||
// Gralloc requests to deliver fence to client side.
|
||||
// If client side does not use TextureFlags::RECYCLE flag,
|
||||
// The fence can not be delivered via LayerTransactionParent.
|
||||
// TextureClient might wait the fence delivery on main thread.
|
||||
|
||||
MOZ_ASSERT(ImageBridgeParent::GetInstance(GetChildProcessId()));
|
||||
if (ImageBridgeParent::GetInstance(GetChildProcessId())) {
|
||||
// Send message back via PImageBridge.
|
||||
ImageBridgeParent::NotifyNotUsedToNonRecycle(
|
||||
GetChildProcessId(),
|
||||
aTexture,
|
||||
aTransactionId);
|
||||
} else {
|
||||
NS_ERROR("ImageBridgeParent should exist");
|
||||
}
|
||||
|
||||
if (!IsAboutToSendAsyncMessages()) {
|
||||
SendPendingAsyncMessages();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX - We should actually figure out the minimum shmem allocation size on
|
||||
// a certain platform and use that.
|
||||
const uint32_t sShmemPageSize = 4096;
|
||||
|
@ -169,14 +169,6 @@ protected:
|
||||
bool mAboutToSendAsyncMessages = false;
|
||||
};
|
||||
|
||||
/// Specific to the CompositorBridgeParent/CrossProcessCompositorBridgeParent.
|
||||
class CompositorBridgeParentIPCAllocator : public HostIPCAllocator
|
||||
{
|
||||
public:
|
||||
CompositorBridgeParentIPCAllocator() {}
|
||||
virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
|
||||
};
|
||||
|
||||
/// An allocator can provide shared memory.
|
||||
///
|
||||
/// The allocated shmems can be deallocated on either process, as long as they
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "Layers.h" // for Layer, ContainerLayer, etc
|
||||
#include "ShadowLayerParent.h" // for ShadowLayerParent
|
||||
#include "CompositableTransactionParent.h" // for EditReplyVector
|
||||
#include "CompositorBridgeParent.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
|
||||
#include "mozilla/layers/CanvasLayerComposite.h"
|
||||
@ -29,7 +30,6 @@
|
||||
#include "mozilla/layers/PLayerParent.h" // for PLayerParent
|
||||
#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
|
||||
#include "mozilla/layers/PaintedLayerComposite.h"
|
||||
#include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsCoord.h" // for NSAppUnitsToFloatPixels
|
||||
@ -144,10 +144,10 @@ ShadowChild(const OpRaiseToTopChild& op)
|
||||
//--------------------------------------------------
|
||||
// LayerTransactionParent
|
||||
LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager,
|
||||
ShadowLayersManager* aLayersManager,
|
||||
CompositorBridgeParentBase* aBridge,
|
||||
uint64_t aId)
|
||||
: mLayerManager(aManager)
|
||||
, mShadowLayersManager(aLayersManager)
|
||||
, mCompositorBridge(aBridge)
|
||||
, mId(aId)
|
||||
, mPendingTransaction(0)
|
||||
, mPendingCompositorUpdates(0)
|
||||
@ -232,7 +232,7 @@ bool
|
||||
LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId,
|
||||
const TimeDuration& aPaintTime)
|
||||
{
|
||||
mShadowLayersManager->UpdatePaintTime(this, aPaintTime);
|
||||
mCompositorBridge->UpdatePaintTime(this, aPaintTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -274,7 +274,7 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
|
||||
EditReplyVector replyv;
|
||||
|
||||
{
|
||||
AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this));
|
||||
AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
|
||||
layer_manager()->BeginTransaction();
|
||||
}
|
||||
|
||||
@ -657,13 +657,13 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
|
||||
}
|
||||
}
|
||||
|
||||
mShadowLayersManager->ShadowLayersUpdated(this, aTransactionId, targetConfig,
|
||||
mCompositorBridge->ShadowLayersUpdated(this, aTransactionId, targetConfig,
|
||||
aPlugins, isFirstPaint, scheduleComposite,
|
||||
paintSequenceNumber, isRepeatTransaction,
|
||||
aPaintSyncId, updateHitTestingTree);
|
||||
|
||||
{
|
||||
AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this));
|
||||
AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
|
||||
layer_manager()->EndTransaction(TimeStamp(), LayerManager::END_NO_IMMEDIATE_REDRAW);
|
||||
}
|
||||
|
||||
@ -714,13 +714,13 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
|
||||
bool
|
||||
LayerTransactionParent::RecvSetTestSampleTime(const TimeStamp& aTime)
|
||||
{
|
||||
return mShadowLayersManager->SetTestSampleTime(this, aTime);
|
||||
return mCompositorBridge->SetTestSampleTime(this, aTime);
|
||||
}
|
||||
|
||||
bool
|
||||
LayerTransactionParent::RecvLeaveTestMode()
|
||||
{
|
||||
mShadowLayersManager->LeaveTestMode(this);
|
||||
mCompositorBridge->LeaveTestMode(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -739,7 +739,7 @@ LayerTransactionParent::RecvGetAnimationOpacity(PLayerParent* aParent,
|
||||
return false;
|
||||
}
|
||||
|
||||
mShadowLayersManager->ApplyAsyncProperties(this);
|
||||
mCompositorBridge->ApplyAsyncProperties(this);
|
||||
|
||||
if (!layer->AsLayerComposite()->GetShadowOpacitySetByAnimation()) {
|
||||
return true;
|
||||
@ -767,7 +767,7 @@ LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent,
|
||||
// a race between when we temporarily clear the animation transform (in
|
||||
// CompositorBridgeParent::SetShadowProperties) and when animation recalculates
|
||||
// the value.
|
||||
mShadowLayersManager->ApplyAsyncProperties(this);
|
||||
mCompositorBridge->ApplyAsyncProperties(this);
|
||||
|
||||
// This method is specific to transforms applied by animation.
|
||||
// This is because this method uses the information stored with an animation
|
||||
@ -885,14 +885,14 @@ LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID,
|
||||
bool
|
||||
LayerTransactionParent::RecvFlushApzRepaints()
|
||||
{
|
||||
mShadowLayersManager->FlushApzRepaints(this);
|
||||
mCompositorBridge->FlushApzRepaints(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData)
|
||||
{
|
||||
mShadowLayersManager->GetAPZTestData(this, aOutData);
|
||||
mCompositorBridge->GetAPZTestData(this, aOutData);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -913,7 +913,7 @@ bool
|
||||
LayerTransactionParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
|
||||
nsTArray<ScrollableLayerGuid>&& aTargets)
|
||||
{
|
||||
mShadowLayersManager->SetConfirmedTargetAPZC(this, aBlockId, aTargets);
|
||||
mCompositorBridge->SetConfirmedTargetAPZC(this, aBlockId, aTargets);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -960,14 +960,14 @@ LayerTransactionParent::RecvClearCachedResources()
|
||||
// of resources to exactly that subtree, so we specify it here.
|
||||
mLayerManager->ClearCachedResources(mRoot);
|
||||
}
|
||||
mShadowLayersManager->NotifyClearCachedResources(this);
|
||||
mCompositorBridge->NotifyClearCachedResources(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LayerTransactionParent::RecvForceComposite()
|
||||
{
|
||||
mShadowLayersManager->ForceComposite(this);
|
||||
mCompositorBridge->ForceComposite(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1047,13 +1047,13 @@ LayerTransactionParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessa
|
||||
void
|
||||
LayerTransactionParent::SendPendingAsyncMessages()
|
||||
{
|
||||
mShadowLayersManager->AsCompositorBridgeParentIPCAllocator()->SendPendingAsyncMessages();
|
||||
mCompositorBridge->SendPendingAsyncMessages();
|
||||
}
|
||||
|
||||
void
|
||||
LayerTransactionParent::SetAboutToSendAsyncMessages()
|
||||
{
|
||||
mShadowLayersManager->AsCompositorBridgeParentIPCAllocator()->SetAboutToSendAsyncMessages();
|
||||
mCompositorBridge->SetAboutToSendAsyncMessages();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -32,7 +32,7 @@ class Layer;
|
||||
class LayerManagerComposite;
|
||||
class ShadowLayerParent;
|
||||
class CompositableParent;
|
||||
class ShadowLayersManager;
|
||||
class CompositorBridgeParentBase;
|
||||
|
||||
class LayerTransactionParent final : public PLayerTransactionParent,
|
||||
public CompositableParentManager,
|
||||
@ -46,7 +46,7 @@ class LayerTransactionParent final : public PLayerTransactionParent,
|
||||
|
||||
public:
|
||||
LayerTransactionParent(LayerManagerComposite* aManager,
|
||||
ShadowLayersManager* aLayersManager,
|
||||
CompositorBridgeParentBase* aBridge,
|
||||
uint64_t aId);
|
||||
|
||||
protected:
|
||||
@ -187,7 +187,7 @@ protected:
|
||||
|
||||
private:
|
||||
RefPtr<LayerManagerComposite> mLayerManager;
|
||||
ShadowLayersManager* mShadowLayersManager;
|
||||
CompositorBridgeParentBase* mCompositorBridge;
|
||||
// Hold the root because it might be grafted under various
|
||||
// containers in the "real" layer tree
|
||||
RefPtr<Layer> mRoot;
|
||||
|
@ -1,55 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_layers_ShadowLayersManager_h
|
||||
#define mozilla_layers_ShadowLayersManager_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class TargetConfig;
|
||||
class LayerTransactionParent;
|
||||
class AsyncCompositionManager;
|
||||
class APZTestData;
|
||||
class CompositorBridgeParentIPCAllocator;
|
||||
|
||||
class ShadowLayersManager
|
||||
{
|
||||
public:
|
||||
virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
|
||||
const uint64_t& aTransactionId,
|
||||
const TargetConfig& aTargetConfig,
|
||||
const InfallibleTArray<PluginWindowData>& aPlugins,
|
||||
bool aIsFirstPaint,
|
||||
bool aScheduleComposite,
|
||||
uint32_t aPaintSequenceNumber,
|
||||
bool aIsRepeatTransaction,
|
||||
int32_t aPaintSyncId,
|
||||
bool aHitTestUpdate) = 0;
|
||||
|
||||
virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aLayerTree) { return nullptr; }
|
||||
|
||||
virtual void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) { }
|
||||
|
||||
virtual void ForceComposite(LayerTransactionParent* aLayerTree) { }
|
||||
virtual bool SetTestSampleTime(LayerTransactionParent* aLayerTree,
|
||||
const TimeStamp& aTime) { return true; }
|
||||
virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) { }
|
||||
virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0;
|
||||
virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0;
|
||||
virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
|
||||
APZTestData* aOutData) { }
|
||||
virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
|
||||
const uint64_t& aInputBlockId,
|
||||
const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
|
||||
virtual CompositorBridgeParentIPCAllocator* AsCompositorBridgeParentIPCAllocator() { return nullptr; }
|
||||
virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_layers_ShadowLayersManager_h
|
@ -178,7 +178,6 @@ EXPORTS.mozilla.layers += [
|
||||
'ipc/RemoteContentController.h',
|
||||
'ipc/ShadowLayerChild.h',
|
||||
'ipc/ShadowLayers.h',
|
||||
'ipc/ShadowLayersManager.h',
|
||||
'ipc/SharedBufferManagerChild.h',
|
||||
'ipc/SharedBufferManagerParent.h',
|
||||
'ipc/SharedPlanarYCbCrImage.h',
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mozilla/Preferences.h" // for Preferences
|
||||
#include "mozilla/gfx/BasePoint.h" // for BasePoint
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4, Matrix
|
||||
#include "mozilla/gfx/gfxVars.h" // for gfxVars
|
||||
#include "mozilla/layers/LayerManagerComposite.h" // for LayerComposite, etc
|
||||
#include "mozilla/layers/CompositingRenderTargetOGL.h"
|
||||
#include "mozilla/layers/Effects.h" // for EffectChain, TexturedEffect, etc
|
||||
@ -108,7 +109,8 @@ CompositorOGL::CreateContext()
|
||||
RefPtr<GLContext> context;
|
||||
|
||||
// Used by mock widget to create an offscreen context
|
||||
void* widgetOpenGLContext = mWidget->RealWidget()->GetNativeData(NS_NATIVE_OPENGL_CONTEXT);
|
||||
nsIWidget* widget = mWidget->RealWidget();
|
||||
void* widgetOpenGLContext = widget ? widget->GetNativeData(NS_NATIVE_OPENGL_CONTEXT) : nullptr;
|
||||
if (widgetOpenGLContext) {
|
||||
GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
|
||||
return already_AddRefed<GLContext>(alreadyRefed);
|
||||
@ -125,7 +127,7 @@ CompositorOGL::CreateContext()
|
||||
if (!context && gfxEnv::LayersPreferOffscreen()) {
|
||||
SurfaceCaps caps = SurfaceCaps::ForRGB();
|
||||
caps.preserve = false;
|
||||
caps.bpp16 = gfxPlatform::GetPlatform()->GetOffscreenFormat() == SurfaceFormat::R5G6B5_UINT16;
|
||||
caps.bpp16 = gfxVars::OffscreenFormat() == SurfaceFormat::R5G6B5_UINT16;
|
||||
|
||||
nsCString discardFailureId;
|
||||
context = GLContextProvider::CreateOffscreen(mSurfaceSize,
|
||||
@ -135,7 +137,7 @@ CompositorOGL::CreateContext()
|
||||
|
||||
if (!context) {
|
||||
context = gl::GLContextProvider::CreateForCompositorWidget(mWidget,
|
||||
gfxPlatform::GetPlatform()->RequiresAcceleratedGLContextForCompositorOGL());
|
||||
gfxVars::RequiresAcceleratedGLContextForCompositorOGL());
|
||||
}
|
||||
|
||||
if (!context) {
|
||||
@ -143,7 +145,8 @@ CompositorOGL::CreateContext()
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
mWidget->RealWidget()->SetNativeData(
|
||||
MOZ_ASSERT(widget);
|
||||
widget->SetNativeData(
|
||||
NS_NATIVE_OPENGL_CONTEXT, reinterpret_cast<uintptr_t>(context.get()));
|
||||
#endif
|
||||
|
||||
|
@ -2110,6 +2110,9 @@ gfxPlatform::InitAcceleration()
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
gfxVars::SetBrowserTabsRemoteAutostart(BrowserTabsRemoteAutostart());
|
||||
gfxVars::SetOffscreenFormat(GetOffscreenFormat());
|
||||
gfxVars::SetRequiresAcceleratedGLContextForCompositorOGL(
|
||||
RequiresAcceleratedGLContextForCompositorOGL());
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
||||
|
@ -309,6 +309,7 @@ private:
|
||||
DECL_GFX_PREF(Live, "dom.meta-viewport.enabled", MetaViewportEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "dom.vr.oculus.enabled", VROculusEnabled, bool, true);
|
||||
DECL_GFX_PREF(Once, "dom.vr.openvr.enabled", VROpenVREnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "dom.vr.osvr.enabled", VROSVREnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled", VRPosePredictionEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled", PointerEventsEnabled, bool, false);
|
||||
|
@ -1816,16 +1816,22 @@ public:
|
||||
// In these error cases, normalize to Now();
|
||||
if (vsync >= now) {
|
||||
vsync = vsync - mVsyncRate;
|
||||
return vsync <= now ? vsync : now;
|
||||
}
|
||||
}
|
||||
|
||||
// On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
|
||||
// from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
|
||||
// If we got here on Windows 10, it means we got a weird timestamp.
|
||||
if (vsync >= now) {
|
||||
vsync = now;
|
||||
}
|
||||
|
||||
// Our vsync time is some time very far in the past, adjust to Now.
|
||||
// 4 ms is arbitrary, so feel free to pick something else if this isn't
|
||||
// working. See the comment above within IsWin10OrLater().
|
||||
if ((now - vsync).ToMilliseconds() > 4.0) {
|
||||
vsync = now;
|
||||
}
|
||||
|
||||
return vsync;
|
||||
}
|
||||
|
||||
@ -1891,6 +1897,12 @@ public:
|
||||
vsync = TimeStamp::Now();
|
||||
}
|
||||
|
||||
if ((now - vsync).ToMilliseconds() > 2.0) {
|
||||
// Account for time drift here where vsync never quite catches up to
|
||||
// Now and we'd fall ever so slightly further behind Now().
|
||||
vsync = GetVBlankTime();
|
||||
}
|
||||
|
||||
mPrevVsync = vsync;
|
||||
}
|
||||
} // end for
|
||||
|
@ -12,9 +12,7 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class VRDisplayClient;
|
||||
namespace vr {
|
||||
class VRLayerChild;
|
||||
} // namepsace vr
|
||||
|
||||
class VRDisplayPresentation final
|
||||
{
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "VRManager.h"
|
||||
#include "VRManagerParent.h"
|
||||
#include "gfxVR.h"
|
||||
#include "gfxVROpenVR.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/VRDisplay.h"
|
||||
#include "mozilla/layers/TextureHost.h"
|
||||
@ -51,6 +52,20 @@ VRManager::VRManager()
|
||||
|
||||
RefPtr<VRDisplayManager> mgr;
|
||||
|
||||
/**
|
||||
* We must add the VRDisplayManager's to mManagers in a careful order to
|
||||
* ensure that we don't detect the same VRDisplay from multiple API's.
|
||||
*
|
||||
* Oculus comes first, as it will only enumerate Oculus HMD's and is the
|
||||
* native interface for Oculus HMD's.
|
||||
*
|
||||
* OpenvR comes second, as it is the native interface for HTC Vive
|
||||
* which is the most common HMD at this time.
|
||||
*
|
||||
* OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
|
||||
* to support everyone else.
|
||||
*/
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// The Oculus runtime is supported only on Windows
|
||||
mgr = VRDisplayManagerOculus::Create();
|
||||
@ -60,9 +75,15 @@ VRManager::VRManager()
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
|
||||
// OpenVR is cross platform compatible
|
||||
mgr = VRDisplayManagerOpenVR::Create();
|
||||
if (mgr) {
|
||||
mManagers.AppendElement(mgr);
|
||||
}
|
||||
|
||||
// OSVR is cross platform compatible
|
||||
mgr = VRDisplayManagerOSVR::Create();
|
||||
if (mgr){
|
||||
if (mgr) {
|
||||
mManagers.AppendElement(mgr);
|
||||
}
|
||||
#endif
|
||||
@ -172,7 +193,15 @@ VRManager::RefreshVRDisplays(bool aMustDispatch)
|
||||
{
|
||||
nsTArray<RefPtr<gfx::VRDisplayHost> > displays;
|
||||
|
||||
for (uint32_t i = 0; i < mManagers.Length(); ++i) {
|
||||
/** We don't wish to enumerate the same display from multiple managers,
|
||||
* so stop as soon as we get a display.
|
||||
* It is still possible to get multiple displays from a single manager,
|
||||
* but do not wish to mix-and-match for risk of reporting a duplicate.
|
||||
*
|
||||
* XXX - Perhaps there will be a better way to detect duplicate displays
|
||||
* in the future.
|
||||
*/
|
||||
for (uint32_t i = 0; i < mManagers.Length() && displays.Length() == 0; ++i) {
|
||||
mManagers[i]->GetHMDs(displays);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ class VRDisplayHost;
|
||||
|
||||
enum class VRDisplayType : uint16_t {
|
||||
Oculus,
|
||||
OpenVR,
|
||||
OSVR,
|
||||
NumVRDisplayTypes
|
||||
};
|
||||
@ -56,10 +57,25 @@ enum class VRDisplayCapabilityFlags : uint16_t {
|
||||
* or update non-VR UI because that content will not be visible.
|
||||
*/
|
||||
Cap_External = 1 << 4,
|
||||
/**
|
||||
* Cap_AngularAcceleration is set if the VRDisplay is capable of tracking its
|
||||
* angular acceleration.
|
||||
*/
|
||||
Cap_AngularAcceleration = 1 << 5,
|
||||
/**
|
||||
* Cap_LinearAcceleration is set if the VRDisplay is capable of tracking its
|
||||
* linear acceleration.
|
||||
*/
|
||||
Cap_LinearAcceleration = 1 << 6,
|
||||
/**
|
||||
* Cap_StageParameters is set if the VRDisplay is capable of room scale VR
|
||||
* and can report the StageParameters to describe the space.
|
||||
*/
|
||||
Cap_StageParameters = 1 << 7,
|
||||
/**
|
||||
* Cap_All used for validity checking during IPC serialization
|
||||
*/
|
||||
Cap_All = (1 << 5) - 1
|
||||
Cap_All = (1 << 8) - 1
|
||||
};
|
||||
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(VRDisplayCapabilityFlags)
|
||||
@ -70,6 +86,14 @@ struct VRFieldOfView {
|
||||
: upDegrees(up), rightDegrees(right), downDegrees(down), leftDegrees(left)
|
||||
{}
|
||||
|
||||
void SetFromTanRadians(double up, double right, double down, double left)
|
||||
{
|
||||
upDegrees = atan(up) * 180.0 / M_PI;
|
||||
rightDegrees = atan(right) * 180.0 / M_PI;
|
||||
downDegrees = atan(down) * 180.0 / M_PI;
|
||||
leftDegrees = atan(left) * 180.0 / M_PI;
|
||||
}
|
||||
|
||||
bool operator==(const VRFieldOfView& other) const {
|
||||
return other.upDegrees == upDegrees &&
|
||||
other.downDegrees == downDegrees &&
|
||||
@ -108,6 +132,8 @@ struct VRDisplayInfo
|
||||
const VRFieldOfView& GetEyeFOV(uint32_t whichEye) const { return mEyeFOV[whichEye]; }
|
||||
bool GetIsConnected() const { return mIsConnected; }
|
||||
bool GetIsPresenting() const { return mIsPresenting; }
|
||||
const Size& GetStageSize() const { return mStageSize; }
|
||||
const Matrix4x4& GetSittingToStandingTransform() const { return mSittingToStandingTransform; }
|
||||
|
||||
enum Eye {
|
||||
Eye_Left,
|
||||
@ -124,6 +150,8 @@ struct VRDisplayInfo
|
||||
IntSize mEyeResolution;
|
||||
bool mIsConnected;
|
||||
bool mIsPresenting;
|
||||
Size mStageSize;
|
||||
Matrix4x4 mSittingToStandingTransform;
|
||||
|
||||
bool operator==(const VRDisplayInfo& other) const {
|
||||
return mType == other.mType &&
|
||||
@ -136,7 +164,9 @@ struct VRDisplayInfo
|
||||
mEyeFOV[0] == other.mEyeFOV[0] &&
|
||||
mEyeFOV[1] == other.mEyeFOV[1] &&
|
||||
mEyeTranslation[0] == other.mEyeTranslation[0] &&
|
||||
mEyeTranslation[1] == other.mEyeTranslation[1];
|
||||
mEyeTranslation[1] == other.mEyeTranslation[1] &&
|
||||
mStageSize == other.mStageSize &&
|
||||
mSittingToStandingTransform == other.mSittingToStandingTransform;
|
||||
}
|
||||
|
||||
bool operator!=(const VRDisplayInfo& other) const {
|
||||
|
@ -24,8 +24,8 @@
|
||||
#include "mozilla/gfx/Quaternion.h"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include "../layers/d3d11/CompositorD3D11.h"
|
||||
#include "mozilla/layers/TextureD3D11.h"
|
||||
#include "CompositorD3D11.h"
|
||||
#include "TextureD3D11.h"
|
||||
|
||||
#include "gfxVROculus.h"
|
||||
|
||||
@ -337,9 +337,11 @@ VRDisplayOculus::VRDisplayOculus(ovrSession aSession)
|
||||
mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
|
||||
if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
|
||||
}
|
||||
if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
|
||||
}
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
|
||||
mDisplayInfo.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
|
||||
@ -441,6 +443,8 @@ VRDisplayOculus::GetSensorState(double timeOffset)
|
||||
result.angularVelocity[1] = pose.AngularVelocity.y;
|
||||
result.angularVelocity[2] = pose.AngularVelocity.z;
|
||||
|
||||
result.flags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
|
||||
|
||||
result.angularAcceleration[0] = pose.AngularAcceleration.x;
|
||||
result.angularAcceleration[1] = pose.AngularAcceleration.y;
|
||||
result.angularAcceleration[2] = pose.AngularAcceleration.z;
|
||||
@ -457,6 +461,8 @@ VRDisplayOculus::GetSensorState(double timeOffset)
|
||||
result.linearVelocity[1] = pose.LinearVelocity.y;
|
||||
result.linearVelocity[2] = pose.LinearVelocity.z;
|
||||
|
||||
result.flags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
|
||||
|
||||
result.linearAcceleration[0] = pose.LinearAcceleration.x;
|
||||
result.linearAcceleration[1] = pose.LinearAcceleration.y;
|
||||
result.linearAcceleration[2] = pose.LinearAcceleration.z;
|
||||
|
442
gfx/vr/gfxVROpenVR.cpp
Normal file
442
gfx/vr/gfxVROpenVR.cpp
Normal file
@ -0,0 +1,442 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "prlink.h"
|
||||
#include "prmem.h"
|
||||
#include "prenv.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include "mozilla/gfx/Quaternion.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "CompositorD3D11.h"
|
||||
#include "TextureD3D11.h"
|
||||
#endif // XP_WIN
|
||||
|
||||
#include "gfxVROpenVR.h"
|
||||
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIScreenManager.h"
|
||||
#include "openvr/openvr.h"
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::gfx::impl;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
namespace {
|
||||
extern "C" {
|
||||
typedef uint32_t (VR_CALLTYPE * pfn_VR_InitInternal)(::vr::HmdError *peError, ::vr::EVRApplicationType eApplicationType);
|
||||
typedef void (VR_CALLTYPE * pfn_VR_ShutdownInternal)();
|
||||
typedef bool (VR_CALLTYPE * pfn_VR_IsHmdPresent)();
|
||||
typedef bool (VR_CALLTYPE * pfn_VR_IsRuntimeInstalled)();
|
||||
typedef const char * (VR_CALLTYPE * pfn_VR_GetStringForHmdError)(::vr::HmdError error);
|
||||
typedef void * (VR_CALLTYPE * pfn_VR_GetGenericInterface)(const char *pchInterfaceVersion, ::vr::HmdError *peError);
|
||||
} // extern "C"
|
||||
} // namespace
|
||||
|
||||
static pfn_VR_InitInternal vr_InitInternal = nullptr;
|
||||
static pfn_VR_ShutdownInternal vr_ShutdownInternal = nullptr;
|
||||
static pfn_VR_IsHmdPresent vr_IsHmdPresent = nullptr;
|
||||
static pfn_VR_IsRuntimeInstalled vr_IsRuntimeInstalled = nullptr;
|
||||
static pfn_VR_GetStringForHmdError vr_GetStringForHmdError = nullptr;
|
||||
static pfn_VR_GetGenericInterface vr_GetGenericInterface = nullptr;
|
||||
|
||||
bool
|
||||
LoadOpenVRRuntime()
|
||||
{
|
||||
static PRLibrary *openvrLib = nullptr;
|
||||
|
||||
nsAdoptingCString openvrPath = Preferences::GetCString("gfx.vr.openvr-runtime");
|
||||
if (!openvrPath)
|
||||
return false;
|
||||
|
||||
openvrLib = PR_LoadLibrary(openvrPath.BeginReading());
|
||||
if (!openvrLib)
|
||||
return false;
|
||||
|
||||
#define REQUIRE_FUNCTION(_x) do { \
|
||||
*(void **)&vr_##_x = (void *) PR_FindSymbol(openvrLib, "VR_" #_x); \
|
||||
if (!vr_##_x) { printf_stderr("VR_" #_x " symbol missing\n"); return false; } \
|
||||
} while (0)
|
||||
|
||||
REQUIRE_FUNCTION(InitInternal);
|
||||
REQUIRE_FUNCTION(ShutdownInternal);
|
||||
REQUIRE_FUNCTION(IsHmdPresent);
|
||||
REQUIRE_FUNCTION(IsRuntimeInstalled);
|
||||
REQUIRE_FUNCTION(GetStringForHmdError);
|
||||
REQUIRE_FUNCTION(GetGenericInterface);
|
||||
|
||||
#undef REQUIRE_FUNCTION
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VRDisplayOpenVR::VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
|
||||
::vr::IVRChaperone *aVRChaperone,
|
||||
::vr::IVRCompositor *aVRCompositor)
|
||||
: VRDisplayHost(VRDisplayType::OpenVR)
|
||||
, mVRSystem(aVRSystem)
|
||||
, mVRChaperone(aVRChaperone)
|
||||
, mVRCompositor(aVRCompositor)
|
||||
, mIsPresenting(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
|
||||
|
||||
mDisplayInfo.mDisplayName.AssignLiteral("OpenVR HMD");
|
||||
mDisplayInfo.mIsConnected = true;
|
||||
mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
|
||||
VRDisplayCapabilityFlags::Cap_Orientation |
|
||||
VRDisplayCapabilityFlags::Cap_Position |
|
||||
VRDisplayCapabilityFlags::Cap_External |
|
||||
VRDisplayCapabilityFlags::Cap_Present |
|
||||
VRDisplayCapabilityFlags::Cap_StageParameters;
|
||||
|
||||
mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
|
||||
|
||||
uint32_t w, h;
|
||||
mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
|
||||
mDisplayInfo.mEyeResolution.width = w;
|
||||
mDisplayInfo.mEyeResolution.height = h;
|
||||
|
||||
// SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
|
||||
for (uint32_t eye = 0; eye < 2; ++eye) {
|
||||
// get l/r/t/b clip plane coordinates
|
||||
float l, r, t, b;
|
||||
mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &l, &r, &t, &b);
|
||||
mDisplayInfo.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
|
||||
|
||||
::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
|
||||
|
||||
mDisplayInfo.mEyeTranslation[eye].x = eyeToHead.m[0][3];
|
||||
mDisplayInfo.mEyeTranslation[eye].y = eyeToHead.m[1][3];
|
||||
mDisplayInfo.mEyeTranslation[eye].z = eyeToHead.m[2][3];
|
||||
}
|
||||
|
||||
UpdateStageParameters();
|
||||
}
|
||||
|
||||
VRDisplayOpenVR::~VRDisplayOpenVR()
|
||||
{
|
||||
Destroy();
|
||||
MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayHost);
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::Destroy()
|
||||
{
|
||||
StopPresentation();
|
||||
vr_ShutdownInternal();
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::UpdateStageParameters()
|
||||
{
|
||||
float sizeX = 0.0f;
|
||||
float sizeZ = 0.0f;
|
||||
if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
|
||||
::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
|
||||
mDisplayInfo.mStageSize.width = sizeX;
|
||||
mDisplayInfo.mStageSize.height = sizeZ;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._11 = t.m[0][0];
|
||||
mDisplayInfo.mSittingToStandingTransform._12 = t.m[1][0];
|
||||
mDisplayInfo.mSittingToStandingTransform._13 = t.m[2][0];
|
||||
mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._21 = t.m[0][1];
|
||||
mDisplayInfo.mSittingToStandingTransform._22 = t.m[1][1];
|
||||
mDisplayInfo.mSittingToStandingTransform._23 = t.m[2][1];
|
||||
mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._31 = t.m[0][2];
|
||||
mDisplayInfo.mSittingToStandingTransform._32 = t.m[1][2];
|
||||
mDisplayInfo.mSittingToStandingTransform._33 = t.m[2][2];
|
||||
mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._41 = t.m[0][3];
|
||||
mDisplayInfo.mSittingToStandingTransform._42 = t.m[1][3];
|
||||
mDisplayInfo.mSittingToStandingTransform._43 = t.m[2][3];
|
||||
mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
|
||||
} else {
|
||||
// If we fail, fall back to reasonable defaults.
|
||||
// 1m x 1m space, 0.75m high in seated position
|
||||
|
||||
mDisplayInfo.mStageSize.width = 1.0f;
|
||||
mDisplayInfo.mStageSize.height = 1.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._11 = 1.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._12 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._13 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._14 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._21 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._22 = 1.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._23 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._24 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._31 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._32 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._33 = 1.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._34 = 0.0f;
|
||||
|
||||
mDisplayInfo.mSittingToStandingTransform._41 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._42 = 0.75f;
|
||||
mDisplayInfo.mSittingToStandingTransform._43 = 0.0f;
|
||||
mDisplayInfo.mSittingToStandingTransform._44 = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::ZeroSensor()
|
||||
{
|
||||
mVRSystem->ResetSeatedZeroPose();
|
||||
UpdateStageParameters();
|
||||
}
|
||||
|
||||
VRHMDSensorState
|
||||
VRDisplayOpenVR::GetSensorState()
|
||||
{
|
||||
return GetSensorState(0.0f);
|
||||
}
|
||||
|
||||
VRHMDSensorState
|
||||
VRDisplayOpenVR::GetImmediateSensorState()
|
||||
{
|
||||
return GetSensorState(0.0f);
|
||||
}
|
||||
|
||||
VRHMDSensorState
|
||||
VRDisplayOpenVR::GetSensorState(double timeOffset)
|
||||
{
|
||||
{
|
||||
::vr::VREvent_t event;
|
||||
while (mVRSystem->PollNextEvent(&event, sizeof(event))) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
|
||||
// Note: We *must* call WaitGetPoses in order for any rendering to happen at all
|
||||
mVRCompositor->WaitGetPoses(poses, ::vr::k_unMaxTrackedDeviceCount, nullptr, 0);
|
||||
|
||||
VRHMDSensorState result;
|
||||
result.Clear();
|
||||
result.timestamp = PR_Now();
|
||||
|
||||
if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
|
||||
poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
|
||||
poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
|
||||
{
|
||||
const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
|
||||
|
||||
gfx::Matrix4x4 m;
|
||||
// NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4. But
|
||||
// because of its arrangement, we can copy the 12 elements in and
|
||||
// then transpose them to the right place. We do this so we can
|
||||
// pull out a Quaternion.
|
||||
memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(float) * 12);
|
||||
m.Transpose();
|
||||
|
||||
gfx::Quaternion rot;
|
||||
rot.SetFromRotationMatrix(m);
|
||||
rot.Invert();
|
||||
|
||||
result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
|
||||
result.orientation[0] = rot.x;
|
||||
result.orientation[1] = rot.y;
|
||||
result.orientation[2] = rot.z;
|
||||
result.orientation[3] = rot.w;
|
||||
result.angularVelocity[0] = pose.vAngularVelocity.v[0];
|
||||
result.angularVelocity[1] = pose.vAngularVelocity.v[1];
|
||||
result.angularVelocity[2] = pose.vAngularVelocity.v[2];
|
||||
|
||||
result.flags |= VRDisplayCapabilityFlags::Cap_Position;
|
||||
result.position[0] = m._41;
|
||||
result.position[1] = m._42;
|
||||
result.position[2] = m._43;
|
||||
result.linearVelocity[0] = pose.vVelocity.v[0];
|
||||
result.linearVelocity[1] = pose.vVelocity.v[1];
|
||||
result.linearVelocity[2] = pose.vVelocity.v[2];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::StartPresentation()
|
||||
{
|
||||
if (mIsPresenting) {
|
||||
return;
|
||||
}
|
||||
mIsPresenting = true;
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::StopPresentation()
|
||||
{
|
||||
if (!mIsPresenting) {
|
||||
return;
|
||||
}
|
||||
|
||||
mVRCompositor->ClearLastSubmittedFrame();
|
||||
|
||||
mIsPresenting = false;
|
||||
}
|
||||
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::SubmitFrame(TextureSourceD3D11* aSource,
|
||||
const IntSize& aSize,
|
||||
const VRHMDSensorState& aSensorState,
|
||||
const gfx::Rect& aLeftEyeRect,
|
||||
const gfx::Rect& aRightEyeRect)
|
||||
{
|
||||
if (!mIsPresenting) {
|
||||
return;
|
||||
}
|
||||
|
||||
::vr::Texture_t tex;
|
||||
tex.handle = (void *)aSource->GetD3D11Texture();
|
||||
tex.eType = ::vr::EGraphicsAPIConvention::API_DirectX;
|
||||
tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
|
||||
|
||||
::vr::VRTextureBounds_t bounds;
|
||||
bounds.uMin = aLeftEyeRect.x;
|
||||
bounds.vMin = 1.0 - aLeftEyeRect.y;
|
||||
bounds.uMax = aLeftEyeRect.x + aLeftEyeRect.width;
|
||||
bounds.vMax = 1.0 - aLeftEyeRect.y - aLeftEyeRect.height;
|
||||
|
||||
::vr::EVRCompositorError err;
|
||||
err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
|
||||
if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
|
||||
printf_stderr("OpenVR Compositor Submit() failed.\n");
|
||||
}
|
||||
|
||||
bounds.uMin = aRightEyeRect.x;
|
||||
bounds.vMin = 1.0 - aRightEyeRect.y;
|
||||
bounds.uMax = aRightEyeRect.x + aRightEyeRect.width;
|
||||
bounds.vMax = 1.0 - aRightEyeRect.y - aRightEyeRect.height;
|
||||
|
||||
err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
|
||||
if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
|
||||
printf_stderr("OpenVR Compositor Submit() failed.\n");
|
||||
}
|
||||
|
||||
mVRCompositor->PostPresentHandoff();
|
||||
|
||||
// Trigger the next VSync immediately
|
||||
VRManager *vm = VRManager::Get();
|
||||
MOZ_ASSERT(vm);
|
||||
vm->NotifyVRVsync(mDisplayInfo.mDisplayID);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
VRDisplayOpenVR::NotifyVSync()
|
||||
{
|
||||
// We update mIsConneced once per frame.
|
||||
mDisplayInfo.mIsConnected = vr_IsHmdPresent();
|
||||
}
|
||||
|
||||
VRDisplayManagerOpenVR::VRDisplayManagerOpenVR()
|
||||
: mOpenVRInstalled(false)
|
||||
{
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<VRDisplayManagerOpenVR>
|
||||
VRDisplayManagerOpenVR::Create()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!gfxPrefs::VREnabled() || !gfxPrefs::VROpenVREnabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!LoadOpenVRRuntime()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<VRDisplayManagerOpenVR> manager = new VRDisplayManagerOpenVR();
|
||||
return manager.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
VRDisplayManagerOpenVR::Init()
|
||||
{
|
||||
if (mOpenVRInstalled)
|
||||
return true;
|
||||
|
||||
if (!vr_IsRuntimeInstalled())
|
||||
return false;
|
||||
|
||||
mOpenVRInstalled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayManagerOpenVR::Destroy()
|
||||
{
|
||||
if (mOpenVRInstalled) {
|
||||
if (mOpenVRHMD) {
|
||||
mOpenVRHMD = nullptr;
|
||||
}
|
||||
mOpenVRInstalled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VRDisplayManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
|
||||
{
|
||||
if (!mOpenVRInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!vr_IsHmdPresent()) {
|
||||
if (mOpenVRHMD) {
|
||||
mOpenVRHMD = nullptr;
|
||||
}
|
||||
} else if (mOpenVRHMD == nullptr) {
|
||||
::vr::HmdError err;
|
||||
|
||||
vr_InitInternal(&err, ::vr::EVRApplicationType::VRApplication_Scene);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
::vr::IVRSystem *system = (::vr::IVRSystem *)vr_GetGenericInterface(::vr::IVRSystem_Version, &err);
|
||||
if (err || !system) {
|
||||
vr_ShutdownInternal();
|
||||
return;
|
||||
}
|
||||
::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)vr_GetGenericInterface(::vr::IVRChaperone_Version, &err);
|
||||
if (err || !chaperone) {
|
||||
vr_ShutdownInternal();
|
||||
return;
|
||||
}
|
||||
::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)vr_GetGenericInterface(::vr::IVRCompositor_Version, &err);
|
||||
if (err || !compositor) {
|
||||
vr_ShutdownInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
|
||||
}
|
||||
|
||||
if (mOpenVRHMD) {
|
||||
aHMDResult.AppendElement(mOpenVRHMD);
|
||||
}
|
||||
}
|
93
gfx/vr/gfxVROpenVR.h
Normal file
93
gfx/vr/gfxVROpenVR.h
Normal file
@ -0,0 +1,93 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GFX_VR_OPENVR_H
|
||||
#define GFX_VR_OPENVR_H
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsIScreen.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
|
||||
#include "gfxVR.h"
|
||||
|
||||
// OpenVR Interfaces
|
||||
namespace vr {
|
||||
class IVRChaperone;
|
||||
class IVRCompositor;
|
||||
class IVRSystem;
|
||||
struct TrackedDevicePose_t;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
namespace impl {
|
||||
|
||||
class VRDisplayOpenVR : public VRDisplayHost
|
||||
{
|
||||
public:
|
||||
virtual void NotifyVSync() override;
|
||||
virtual VRHMDSensorState GetSensorState() override;
|
||||
virtual VRHMDSensorState GetImmediateSensorState() override;
|
||||
void ZeroSensor() override;
|
||||
|
||||
protected:
|
||||
virtual void StartPresentation() override;
|
||||
virtual void StopPresentation() override;
|
||||
#if defined(XP_WIN)
|
||||
virtual void SubmitFrame(mozilla::layers::TextureSourceD3D11* aSource,
|
||||
const IntSize& aSize,
|
||||
const VRHMDSensorState& aSensorState,
|
||||
const gfx::Rect& aLeftEyeRect,
|
||||
const gfx::Rect& aRightEyeRect) override;
|
||||
#endif
|
||||
|
||||
public:
|
||||
explicit VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
|
||||
::vr::IVRChaperone *aVRChaperone,
|
||||
::vr::IVRCompositor *aVRCompositor);
|
||||
|
||||
protected:
|
||||
virtual ~VRDisplayOpenVR();
|
||||
void Destroy();
|
||||
|
||||
VRHMDSensorState GetSensorState(double timeOffset);
|
||||
|
||||
// not owned by us; global from OpenVR
|
||||
::vr::IVRSystem *mVRSystem;
|
||||
::vr::IVRChaperone *mVRChaperone;
|
||||
::vr::IVRCompositor *mVRCompositor;
|
||||
|
||||
bool mIsPresenting;
|
||||
|
||||
void UpdateStageParameters();
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
class VRDisplayManagerOpenVR : public VRDisplayManager
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<VRDisplayManagerOpenVR> Create();
|
||||
|
||||
virtual bool Init() override;
|
||||
virtual void Destroy() override;
|
||||
virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
|
||||
protected:
|
||||
VRDisplayManagerOpenVR();
|
||||
|
||||
// there can only be one
|
||||
RefPtr<impl::VRDisplayOpenVR> mOpenVRHMD;
|
||||
bool mOpenVRInstalled;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
#endif /* GFX_VR_OPENVR_H */
|
@ -40,6 +40,8 @@ struct ParamTraits<mozilla::gfx::VRDisplayInfo>
|
||||
WriteParam(aMsg, aParam.mEyeResolution);
|
||||
WriteParam(aMsg, aParam.mIsConnected);
|
||||
WriteParam(aMsg, aParam.mIsPresenting);
|
||||
WriteParam(aMsg, aParam.mStageSize);
|
||||
WriteParam(aMsg, aParam.mSittingToStandingTransform);
|
||||
for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
|
||||
WriteParam(aMsg, aParam.mEyeFOV[i]);
|
||||
WriteParam(aMsg, aParam.mEyeTranslation[i]);
|
||||
@ -54,7 +56,9 @@ struct ParamTraits<mozilla::gfx::VRDisplayInfo>
|
||||
!ReadParam(aMsg, aIter, &(aResult->mCapabilityFlags)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mEyeResolution)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mIsConnected)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mIsPresenting))) {
|
||||
!ReadParam(aMsg, aIter, &(aResult->mIsPresenting)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mStageSize)) ||
|
||||
!ReadParam(aMsg, aIter, &(aResult->mSittingToStandingTransform))) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < mozilla::gfx::VRDisplayInfo::NumEyes; i++) {
|
||||
|
@ -16,11 +16,13 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/gfx/layers/d3d11',
|
||||
'/gfx/thebes',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'gfxVR.cpp',
|
||||
'gfxVROpenVR.cpp',
|
||||
'gfxVROSVR.cpp',
|
||||
'ipc/VRLayerChild.cpp',
|
||||
'ipc/VRLayerParent.cpp',
|
||||
|
27
gfx/vr/openvr/LICENSE
Normal file
27
gfx/vr/openvr/LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2015, Valve Corporation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2
gfx/vr/openvr/README
Normal file
2
gfx/vr/openvr/README
Normal file
@ -0,0 +1,2 @@
|
||||
See https://github.com/ValveSoftware/openvr/
|
||||
|
3352
gfx/vr/openvr/openvr.h
Normal file
3352
gfx/vr/openvr/openvr.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -67,12 +67,6 @@ static StaticRefPtr<SurfaceCacheImpl> sInstance;
|
||||
*/
|
||||
typedef size_t Cost;
|
||||
|
||||
// Placeholders do not have surfaces, but need to be given a trivial cost for
|
||||
// our invariants to hold.
|
||||
// XXX(seth): This is only true of old-style placeholders inserted via
|
||||
// InsertPlaceholder().
|
||||
static const Cost sPlaceholderCost = 1;
|
||||
|
||||
static Cost
|
||||
ComputeCost(const IntSize& aSize, uint32_t aBytesPerPixel)
|
||||
{
|
||||
@ -94,14 +88,12 @@ ComputeCost(const IntSize& aSize, uint32_t aBytesPerPixel)
|
||||
class CostEntry
|
||||
{
|
||||
public:
|
||||
CostEntry(CachedSurface* aSurface, Cost aCost)
|
||||
CostEntry(NotNull<CachedSurface*> aSurface, Cost aCost)
|
||||
: mSurface(aSurface)
|
||||
, mCost(aCost)
|
||||
{
|
||||
MOZ_ASSERT(aSurface, "Must have a surface");
|
||||
}
|
||||
{ }
|
||||
|
||||
CachedSurface* GetSurface() const { return mSurface; }
|
||||
NotNull<CachedSurface*> Surface() const { return mSurface; }
|
||||
Cost GetCost() const { return mCost; }
|
||||
|
||||
bool operator==(const CostEntry& aOther) const
|
||||
@ -117,7 +109,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
CachedSurface* mSurface;
|
||||
NotNull<CachedSurface*> mSurface;
|
||||
Cost mCost;
|
||||
};
|
||||
|
||||
@ -132,7 +124,7 @@ public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(CachedSurface)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CachedSurface)
|
||||
|
||||
CachedSurface(ISurfaceProvider* aProvider,
|
||||
CachedSurface(NotNull<ISurfaceProvider*> aProvider,
|
||||
const Cost aCost,
|
||||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
@ -141,8 +133,6 @@ public:
|
||||
, mImageKey(aImageKey)
|
||||
, mSurfaceKey(aSurfaceKey)
|
||||
{
|
||||
MOZ_ASSERT(aProvider || mCost == sPlaceholderCost,
|
||||
"Old-style placeholders should have trivial cost");
|
||||
MOZ_ASSERT(mImageKey, "Must have a valid image key");
|
||||
}
|
||||
|
||||
@ -165,23 +155,15 @@ public:
|
||||
mProvider->SetLocked(aLocked);
|
||||
}
|
||||
|
||||
bool IsPlaceholder() const
|
||||
{
|
||||
return !mProvider || mProvider->Availability().IsPlaceholder();
|
||||
}
|
||||
|
||||
bool IsLocked() const { return !IsPlaceholder() && mProvider->IsLocked(); }
|
||||
bool IsPlaceholder() const { return mProvider->Availability().IsPlaceholder(); }
|
||||
bool IsDecoded() const { return !IsPlaceholder() && mProvider->IsFinished(); }
|
||||
|
||||
ImageKey GetImageKey() const { return mImageKey; }
|
||||
SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
|
||||
CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
|
||||
CostEntry GetCostEntry() { return image::CostEntry(WrapNotNull(this), mCost); }
|
||||
nsExpirationState* GetExpirationState() { return &mExpirationState; }
|
||||
|
||||
bool IsDecoded() const
|
||||
{
|
||||
return !IsPlaceholder() && mProvider->IsFinished();
|
||||
}
|
||||
|
||||
// A helper type used by SurfaceCacheImpl::CollectSizeOfSurfaces.
|
||||
struct MOZ_STACK_CLASS SurfaceMemoryReport
|
||||
{
|
||||
@ -191,10 +173,8 @@ public:
|
||||
, mMallocSizeOf(aMallocSizeOf)
|
||||
{ }
|
||||
|
||||
void Add(CachedSurface* aCachedSurface)
|
||||
void Add(NotNull<CachedSurface*> aCachedSurface)
|
||||
{
|
||||
MOZ_ASSERT(aCachedSurface, "Should have a CachedSurface");
|
||||
|
||||
SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
|
||||
aCachedSurface->IsLocked());
|
||||
|
||||
@ -223,7 +203,7 @@ public:
|
||||
|
||||
private:
|
||||
nsExpirationState mExpirationState;
|
||||
RefPtr<ISurfaceProvider> mProvider;
|
||||
NotNull<RefPtr<ISurfaceProvider>> mProvider;
|
||||
const Cost mCost;
|
||||
const ImageKey mImageKey;
|
||||
const SurfaceKey mSurfaceKey;
|
||||
@ -257,17 +237,15 @@ public:
|
||||
|
||||
bool IsEmpty() const { return mSurfaces.Count() == 0; }
|
||||
|
||||
void Insert(const SurfaceKey& aKey, CachedSurface* aSurface)
|
||||
void Insert(const SurfaceKey& aKey, NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
MOZ_ASSERT(aSurface, "Should have a surface");
|
||||
MOZ_ASSERT(!mLocked || aSurface->IsPlaceholder() || aSurface->IsLocked(),
|
||||
"Inserting an unlocked surface for a locked image");
|
||||
mSurfaces.Put(aKey, aSurface);
|
||||
}
|
||||
|
||||
void Remove(CachedSurface* aSurface)
|
||||
void Remove(NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
MOZ_ASSERT(aSurface, "Should have a surface");
|
||||
MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()),
|
||||
"Should not be removing a surface we don't have");
|
||||
|
||||
@ -294,7 +272,7 @@ public:
|
||||
// There's no perfect match, so find the best match we can.
|
||||
RefPtr<CachedSurface> bestMatch;
|
||||
for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
|
||||
CachedSurface* current = iter.UserData();
|
||||
NotNull<CachedSurface*> current = WrapNotNull(iter.UserData());
|
||||
const SurfaceKey& currentKey = current->GetSurfaceKey();
|
||||
|
||||
// We never match a placeholder.
|
||||
@ -367,7 +345,7 @@ public:
|
||||
}
|
||||
} else {
|
||||
if (exactMatch) {
|
||||
// We found an "exact match", it must have been a placeholder.
|
||||
// We found an "exact match"; it must have been a placeholder.
|
||||
MOZ_ASSERT(exactMatch->IsPlaceholder());
|
||||
matchType = MatchType::PENDING;
|
||||
} else {
|
||||
@ -438,7 +416,7 @@ public:
|
||||
|
||||
Mutex& GetMutex() { return mMutex; }
|
||||
|
||||
InsertOutcome Insert(ISurfaceProvider* aProvider,
|
||||
InsertOutcome Insert(NotNull<ISurfaceProvider*> aProvider,
|
||||
const Cost aCost,
|
||||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
@ -472,7 +450,7 @@ public:
|
||||
while (aCost > mAvailableCost) {
|
||||
MOZ_ASSERT(!mCosts.IsEmpty(),
|
||||
"Removed everything and it still won't fit");
|
||||
Remove(mCosts.LastElement().GetSurface());
|
||||
Remove(mCosts.LastElement().Surface());
|
||||
}
|
||||
|
||||
// Locate the appropriate per-image cache. If there's not an existing cache
|
||||
@ -488,8 +466,8 @@ public:
|
||||
aProvider->Availability().SetAvailable();
|
||||
}
|
||||
|
||||
RefPtr<CachedSurface> surface =
|
||||
new CachedSurface(aProvider, aCost, aImageKey, aSurfaceKey);
|
||||
NotNull<RefPtr<CachedSurface>> surface =
|
||||
WrapNotNull(new CachedSurface(aProvider, aCost, aImageKey, aSurfaceKey));
|
||||
|
||||
// We require that locking succeed if the image is locked and we're not
|
||||
// inserting a placeholder; the caller may need to know this to handle
|
||||
@ -509,9 +487,8 @@ public:
|
||||
return InsertOutcome::SUCCESS;
|
||||
}
|
||||
|
||||
void Remove(CachedSurface* aSurface)
|
||||
void Remove(NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
MOZ_ASSERT(aSurface, "Should have a surface");
|
||||
ImageKey imageKey = aSurface->GetImageKey();
|
||||
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(imageKey);
|
||||
@ -532,7 +509,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void StartTracking(CachedSurface* aSurface)
|
||||
void StartTracking(NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
CostEntry costEntry = aSurface->GetCostEntry();
|
||||
MOZ_ASSERT(costEntry.GetCost() <= mAvailableCost,
|
||||
@ -551,9 +528,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void StopTracking(CachedSurface* aSurface)
|
||||
void StopTracking(NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
MOZ_ASSERT(aSurface, "Should have a surface");
|
||||
CostEntry costEntry = aSurface->GetCostEntry();
|
||||
|
||||
if (aSurface->IsLocked()) {
|
||||
@ -605,12 +581,12 @@ public:
|
||||
if (!drawableSurface) {
|
||||
// The surface was released by the operating system. Remove the cache
|
||||
// entry as well.
|
||||
Remove(surface);
|
||||
Remove(WrapNotNull(surface));
|
||||
return LookupResult(MatchType::NOT_FOUND);
|
||||
}
|
||||
|
||||
if (aMarkUsed) {
|
||||
MarkUsed(surface, cache);
|
||||
MarkUsed(WrapNotNull(surface), WrapNotNull(cache));
|
||||
}
|
||||
|
||||
MOZ_ASSERT(surface->GetSurfaceKey() == aSurfaceKey,
|
||||
@ -650,7 +626,7 @@ public:
|
||||
|
||||
// The surface was released by the operating system. Remove the cache
|
||||
// entry as well.
|
||||
Remove(surface);
|
||||
Remove(WrapNotNull(surface));
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(matchType == MatchType::EXACT,
|
||||
@ -662,7 +638,7 @@ public:
|
||||
surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags());
|
||||
|
||||
if (matchType == MatchType::EXACT) {
|
||||
MarkUsed(surface, cache);
|
||||
MarkUsed(WrapNotNull(surface), WrapNotNull(cache));
|
||||
}
|
||||
|
||||
return LookupResult(Move(drawableSurface), matchType);
|
||||
@ -719,7 +695,7 @@ public:
|
||||
}
|
||||
|
||||
cache->SetLocked(false);
|
||||
DoUnlockSurfaces(cache);
|
||||
DoUnlockSurfaces(WrapNotNull(cache));
|
||||
}
|
||||
|
||||
void UnlockEntries(const ImageKey aImageKey)
|
||||
@ -731,7 +707,7 @@ public:
|
||||
|
||||
// (Note that we *don't* unlock the per-image cache here; that's the
|
||||
// difference between this and UnlockImage.)
|
||||
DoUnlockSurfaces(cache);
|
||||
DoUnlockSurfaces(WrapNotNull(cache));
|
||||
}
|
||||
|
||||
void RemoveImage(const ImageKey aImageKey)
|
||||
@ -747,7 +723,7 @@ public:
|
||||
// small, performance should be good, but if usage patterns change we should
|
||||
// change the data structure used for mCosts.
|
||||
for (auto iter = cache->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
StopTracking(iter.UserData());
|
||||
StopTracking(WrapNotNull(iter.UserData()));
|
||||
}
|
||||
|
||||
// The per-image cache isn't needed anymore, so remove it as well.
|
||||
@ -761,7 +737,7 @@ public:
|
||||
// structures are all hash tables. Note that locked surfaces are not
|
||||
// removed, since they aren't present in mCosts.
|
||||
while (!mCosts.IsEmpty()) {
|
||||
Remove(mCosts.LastElement().GetSurface());
|
||||
Remove(mCosts.LastElement().Surface());
|
||||
}
|
||||
}
|
||||
|
||||
@ -787,11 +763,11 @@ public:
|
||||
// Discard surfaces until we've reduced our cost to our target cost.
|
||||
while (mAvailableCost < targetCost) {
|
||||
MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and still not done");
|
||||
Remove(mCosts.LastElement().GetSurface());
|
||||
Remove(mCosts.LastElement().Surface());
|
||||
}
|
||||
}
|
||||
|
||||
void LockSurface(CachedSurface* aSurface)
|
||||
void LockSurface(NotNull<CachedSurface*> aSurface)
|
||||
{
|
||||
if (aSurface->IsPlaceholder() || aSurface->IsLocked()) {
|
||||
return;
|
||||
@ -845,7 +821,7 @@ public:
|
||||
// Report all surfaces in the per-image cache.
|
||||
CachedSurface::SurfaceMemoryReport report(aCounters, aMallocSizeOf);
|
||||
for (auto iter = cache->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
report.Add(iter.UserData());
|
||||
report.Add(WrapNotNull(iter.UserData()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -867,7 +843,8 @@ private:
|
||||
return aCost <= mMaxCost - mLockedCost;
|
||||
}
|
||||
|
||||
void MarkUsed(CachedSurface* aSurface, ImageSurfaceCache* aCache)
|
||||
void MarkUsed(NotNull<CachedSurface*> aSurface,
|
||||
NotNull<ImageSurfaceCache*> aCache)
|
||||
{
|
||||
if (aCache->IsLocked()) {
|
||||
LockSurface(aSurface);
|
||||
@ -876,11 +853,11 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void DoUnlockSurfaces(ImageSurfaceCache* aCache)
|
||||
void DoUnlockSurfaces(NotNull<ImageSurfaceCache*> aCache)
|
||||
{
|
||||
// Unlock all the surfaces the per-image cache is holding.
|
||||
for (auto iter = aCache->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
CachedSurface* surface = iter.UserData();
|
||||
NotNull<CachedSurface*> surface = WrapNotNull(iter.UserData());
|
||||
if (surface->IsPlaceholder() || !surface->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
@ -903,7 +880,7 @@ private:
|
||||
return; // Lookup in the per-image cache missed.
|
||||
}
|
||||
|
||||
Remove(surface);
|
||||
Remove(WrapNotNull(surface));
|
||||
}
|
||||
|
||||
struct SurfaceTracker : public nsExpirationTracker<CachedSurface, 2>
|
||||
@ -918,7 +895,7 @@ private:
|
||||
{
|
||||
if (sInstance) {
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
sInstance->Remove(aSurface);
|
||||
sInstance->Remove(WrapNotNull(aSurface));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1061,20 +1038,7 @@ SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider,
|
||||
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
Cost cost = aProvider->LogicalSizeInBytes();
|
||||
return sInstance->Insert(aProvider.get(), cost, aImageKey, aSurfaceKey,
|
||||
/* aSetAvailable = */ false);
|
||||
}
|
||||
|
||||
/* static */ InsertOutcome
|
||||
SurfaceCache::InsertPlaceholder(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
if (!sInstance) {
|
||||
return InsertOutcome::FAILURE;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
return sInstance->Insert(nullptr, sPlaceholderCost, aImageKey, aSurfaceKey,
|
||||
return sInstance->Insert(aProvider, cost, aImageKey, aSurfaceKey,
|
||||
/* aSetAvailable = */ false);
|
||||
}
|
||||
|
||||
|
@ -240,8 +240,8 @@ struct SurfaceCache
|
||||
/**
|
||||
* Insert an ISurfaceProvider into the cache. If an entry with the same
|
||||
* ImageKey and SurfaceKey is already in the cache, Insert returns
|
||||
* FAILURE_ALREADY_PRESENT. If a matching placeholder is already present, the
|
||||
* placeholder is removed.
|
||||
* FAILURE_ALREADY_PRESENT. If a matching placeholder is already present, it
|
||||
* is replaced.
|
||||
*
|
||||
* Cache entries will never expire as long as they remain locked, but if they
|
||||
* become unlocked, they can expire either because the SurfaceCache runs out
|
||||
@ -285,33 +285,6 @@ struct SurfaceCache
|
||||
const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
|
||||
/**
|
||||
* Insert a placeholder entry into the cache. If an entry with the same
|
||||
* ImageKey and SurfaceKey is already in the cache, InsertPlaceholder()
|
||||
* returns FAILURE_ALREADY_PRESENT.
|
||||
*
|
||||
* Placeholders exist to allow lazy allocation of surfaces. The Lookup*()
|
||||
* methods will report whether a placeholder for an exactly matching cache
|
||||
* entry existed by returning a MatchType of PENDING or
|
||||
* SUBSTITUTE_BECAUSE_PENDING, but they will never return a placeholder
|
||||
* directly. (They couldn't, since placeholders don't have an associated
|
||||
* surface.)
|
||||
*
|
||||
* Placeholders are automatically removed when a real entry that matches the
|
||||
* placeholder is inserted with Insert(), or when RemoveImage() is called.
|
||||
*
|
||||
* @param aImageKey Key data identifying which image the cache entry
|
||||
* belongs to.
|
||||
* @param aSurfaceKey Key data which uniquely identifies the requested
|
||||
* cache entry.
|
||||
* @return SUCCESS if the placeholder was inserted successfully.
|
||||
* FAILURE if the placeholder could not be inserted for some reason.
|
||||
* FAILURE_ALREADY_PRESENT if an entry with the same ImageKey and
|
||||
* SurfaceKey already exists in the cache.
|
||||
*/
|
||||
static InsertOutcome InsertPlaceholder(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
|
||||
/**
|
||||
* Mark the cache entry @aProvider as having an available surface. This turns
|
||||
* a placeholder cache entry into a normal cache entry. The cache entry
|
||||
@ -325,14 +298,6 @@ struct SurfaceCache
|
||||
* definition, non-placeholder ISurfaceProviders should have a surface
|
||||
* available already.
|
||||
*
|
||||
* XXX(seth): We're currently in a transitional state where two notions of
|
||||
* placeholder exist: the old one (placeholders are an "empty" cache entry
|
||||
* inserted via InsertPlaceholder(), which then gets replaced by inserting a
|
||||
* real cache entry with the same keys via Insert()) and the new one (where
|
||||
* the same cache entry, inserted via Insert(), starts in a placeholder state
|
||||
* and then transitions to being a normal cache entry via this function). The
|
||||
* old mechanism will be removed in bug 1292392.
|
||||
*
|
||||
* @param aProvider The cache entry that now has a surface available.
|
||||
* @param aImageKey Key data identifying which image the cache entry
|
||||
* belongs to.
|
||||
@ -423,7 +388,7 @@ struct SurfaceCache
|
||||
static void UnlockEntries(const ImageKey aImageKey);
|
||||
|
||||
/**
|
||||
* Removes all cache entries (both real and placeholder) associated with the
|
||||
* Removes all cache entries (including placeholders) associated with the
|
||||
* given image from the cache. If the image is locked, it is automatically
|
||||
* unlocked.
|
||||
*
|
||||
|
@ -87,7 +87,7 @@ fuzzy(20,999) != downscale-2c.html?205,53,bottom about:blank
|
||||
fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank
|
||||
fuzzy(20,999) fails-if(OSX>=1008&&!skiaContent) != downscale-2e.html?205,53,bottom about:blank
|
||||
|
||||
fuzzy(52,3386) == downscale-moz-icon-1.html downscale-moz-icon-1-ref.html
|
||||
fuzzy(63,3386) == downscale-moz-icon-1.html downscale-moz-icon-1-ref.html
|
||||
|
||||
== downscale-png.html?16,16,interlaced downscale-png.html?16,16,normal
|
||||
== downscale-png.html?24,24,interlaced downscale-png.html?24,24,normal
|
||||
@ -170,7 +170,8 @@ fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank
|
||||
fuzzy(20,999) != downscale-2e.html?205,53,bottom about:blank
|
||||
fuzzy(20,999) != downscale-2f.html?205,53,bottom about:blank
|
||||
|
||||
fuzzy(71,4439) == downscale-moz-icon-1.html downscale-moz-icon-1-ref.html
|
||||
# Skip on WinXP with skia content
|
||||
fuzzy(71,4439) fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == downscale-moz-icon-1.html downscale-moz-icon-1-ref.html
|
||||
|
||||
== downscale-png.html?16,16,interlaced downscale-png.html?16,16,normal
|
||||
== downscale-png.html?24,24,interlaced downscale-png.html?24,24,normal
|
||||
|
@ -109,6 +109,41 @@ class UTF8CharsZ : public mozilla::RangedPtr<unsigned char>
|
||||
char* c_str() { return reinterpret_cast<char*>(get()); }
|
||||
};
|
||||
|
||||
/*
|
||||
* A wrapper for a "const char*" that is encoded using UTF-8.
|
||||
* This class does not manage ownership of the data; that is left
|
||||
* to others. This differs from UTF8CharsZ in that the chars are
|
||||
* const and it allows assignment.
|
||||
*/
|
||||
class ConstUTF8CharsZ
|
||||
{
|
||||
const char* data_;
|
||||
|
||||
public:
|
||||
ConstUTF8CharsZ() : data_(nullptr)
|
||||
{}
|
||||
|
||||
ConstUTF8CharsZ(const char* aBytes, size_t aLength)
|
||||
: data_(aBytes)
|
||||
{
|
||||
MOZ_ASSERT(aBytes[aLength] == '\0');
|
||||
#ifdef DEBUG
|
||||
validate(aLength);
|
||||
#endif
|
||||
}
|
||||
|
||||
const void* get() const { return data_; }
|
||||
|
||||
const char* c_str() const { return data_; }
|
||||
|
||||
explicit operator bool() const { return data_ != nullptr; }
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
void validate(size_t aLength);
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* SpiderMonkey uses a 2-byte character representation: it is a
|
||||
* 2-byte-at-a-time view of a UTF-16 byte stream. This is similar to UCS-2,
|
||||
@ -197,6 +232,12 @@ Utf8ToOneUcs4Char(const uint8_t* utf8Buffer, int utf8Length);
|
||||
extern TwoByteCharsZ
|
||||
UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* Like UTF8CharsToNewTwoByteCharsZ, but for ConstUTF8CharsZ.
|
||||
*/
|
||||
extern TwoByteCharsZ
|
||||
UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* The same as UTF8CharsToNewTwoByteCharsZ(), except that any malformed UTF-8 characters
|
||||
* will be replaced by \uFFFD. No exception will be thrown for malformed UTF-8
|
||||
@ -205,6 +246,9 @@ UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen)
|
||||
extern TwoByteCharsZ
|
||||
LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen);
|
||||
|
||||
extern TwoByteCharsZ
|
||||
LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen);
|
||||
|
||||
/*
|
||||
* Returns the length of the char buffer required to encode |s| as UTF8.
|
||||
* Does not include the null-terminator.
|
||||
|
@ -45,6 +45,23 @@
|
||||
#include "js/TraceKind.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
// Expand the given macro D for each public GC pointer.
|
||||
#define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
|
||||
D(JS::Symbol*) \
|
||||
D(JSAtom*) \
|
||||
D(JSFunction*) \
|
||||
D(JSObject*) \
|
||||
D(JSScript*) \
|
||||
D(JSString*)
|
||||
|
||||
// Expand the given macro D for each public tagged GC pointer type.
|
||||
#define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
|
||||
D(JS::Value) \
|
||||
D(jsid)
|
||||
|
||||
#define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) \
|
||||
D(JSPropertyDescriptor)
|
||||
|
||||
class JSAtom;
|
||||
class JSFunction;
|
||||
class JSObject;
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "js/GCAnnotations.h"
|
||||
@ -123,6 +125,14 @@ class MutableHandleBase {};
|
||||
template <typename T>
|
||||
class HeapBase {};
|
||||
|
||||
// Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many macros into scope
|
||||
template <typename T> struct IsHeapConstructibleType { static constexpr bool value = false; };
|
||||
#define DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
|
||||
template <> struct IsHeapConstructibleType<T> { static constexpr bool value = true; };
|
||||
FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
|
||||
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
|
||||
#undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
|
||||
|
||||
template <typename T>
|
||||
class PersistentRootedBase {};
|
||||
|
||||
@ -214,11 +224,14 @@ AssertGCThingIsNotAnObjectSubclass(js::gc::Cell* cell) {}
|
||||
* Heap<T> objects should only be used on the heap. GC references stored on the
|
||||
* C/C++ stack must use Rooted/Handle/MutableHandle instead.
|
||||
*
|
||||
* Type T must be one of: JS::Value, jsid, JSObject*, JSString*, JSScript*
|
||||
* Type T must be a public GC pointer type.
|
||||
*/
|
||||
template <typename T>
|
||||
class Heap : public js::HeapBase<T>
|
||||
{
|
||||
// Please note: this can actually also be used by nsXBLMaybeCompiled<T>, for legacy reasons.
|
||||
static_assert(js::IsHeapConstructibleType<T>::value,
|
||||
"Type T must be a public GC pointer type");
|
||||
public:
|
||||
Heap() {
|
||||
static_assert(sizeof(T) == sizeof(Heap<T>),
|
||||
|
@ -2791,9 +2791,12 @@ EdgeNeedsSweep(JS::Heap<T>* thingp)
|
||||
template bool IsMarked<type>(WriteBarrieredBase<type>*); \
|
||||
template bool IsAboutToBeFinalizedUnbarriered<type>(type*); \
|
||||
template bool IsAboutToBeFinalized<type>(WriteBarrieredBase<type>*); \
|
||||
template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*); \
|
||||
template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*);
|
||||
#define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \
|
||||
template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*);
|
||||
FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
|
||||
FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
|
||||
FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
|
||||
#undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
|
||||
|
||||
} /* namespace gc */
|
||||
|
@ -58,23 +58,6 @@ class JitCode;
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
// Expand the given macro D for each public GC pointer.
|
||||
#define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
|
||||
D(JS::Symbol*) \
|
||||
D(JSAtom*) \
|
||||
D(JSFunction*) \
|
||||
D(JSObject*) \
|
||||
D(JSScript*) \
|
||||
D(JSString*)
|
||||
|
||||
// Expand the given macro D for each public tagged GC pointer type.
|
||||
#define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
|
||||
D(JS::Value) \
|
||||
D(jsid)
|
||||
|
||||
#define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) \
|
||||
D(JSPropertyDescriptor)
|
||||
|
||||
// Expand the given macro D for each valid GC reference type.
|
||||
#define FOR_EACH_INTERNAL_GC_POINTER_TYPE(D) \
|
||||
D(JSFlatString*) \
|
||||
|
8
js/src/jit-test/tests/debug/Frame-eval-32.js
Normal file
8
js/src/jit-test/tests/debug/Frame-eval-32.js
Normal file
@ -0,0 +1,8 @@
|
||||
// |jit-test| error: ReferenceError
|
||||
|
||||
// Test the TDZ works for glbao lexicals through Debugger environments in
|
||||
// compound assignments.
|
||||
load(libdir + "evalInFrame.js");
|
||||
|
||||
evalInFrame(0, "x |= 0");
|
||||
let x;
|
@ -1,3 +1,5 @@
|
||||
// |jit-test| error: ReferenceError
|
||||
|
||||
load(libdir + "evalInFrame.js");
|
||||
evalInFrame(1, "a = 43");
|
||||
let a = 42;
|
||||
|
10
js/src/jit-test/tests/promise/no-reentrant-drainjobqueue.js
Normal file
10
js/src/jit-test/tests/promise/no-reentrant-drainjobqueue.js
Normal file
@ -0,0 +1,10 @@
|
||||
let thenCalled = false;
|
||||
let p1 = new Promise(res => res('result')).then(val => {
|
||||
Promise.resolve(1).then(_=>{thenCalled = true;});
|
||||
// This reentrant call is ignored.
|
||||
drainJobQueue();
|
||||
assertEq(thenCalled, false);
|
||||
});
|
||||
|
||||
drainJobQueue();
|
||||
assertEq(thenCalled, true);
|
@ -1113,7 +1113,7 @@ GenerateCallGetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
||||
|
||||
// Note: this may clobber the object register if it's used as scratch.
|
||||
if (obj != holder)
|
||||
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures);
|
||||
GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, maybePopAndFail);
|
||||
|
||||
// Guard on the holder's shape.
|
||||
Register holderReg = scratchReg;
|
||||
|
@ -4518,7 +4518,8 @@ struct JS_PUBLIC_API(AsyncTask)
|
||||
*
|
||||
* If this function succeeds, SpiderMonkey will call the FinishAsyncTaskCallback
|
||||
* at some point in the future. Otherwise, FinishAsyncTaskCallback will *not*
|
||||
* be called.
|
||||
* be called. SpiderMonkey assumes that, if StartAsyncTaskCallback fails, it is
|
||||
* because the JSContext is being shut down.
|
||||
*/
|
||||
typedef bool
|
||||
(*StartAsyncTaskCallback)(JSContext* cx, AsyncTask* task);
|
||||
|
@ -2230,7 +2230,22 @@ js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject e
|
||||
|
||||
// See note above RuntimeLexicalErrorObject.
|
||||
if (pobj == env) {
|
||||
if (name != cx->names().dotThis && IsUninitializedLexicalSlot(env, shape)) {
|
||||
bool isTDZ = false;
|
||||
if (shape && name != cx->names().dotThis) {
|
||||
// Treat Debugger environments specially for TDZ checks, as they
|
||||
// look like non-native environments but in fact wrap native
|
||||
// environments.
|
||||
if (env->is<DebugEnvironmentProxy>()) {
|
||||
RootedValue v(cx);
|
||||
if (!env->as<DebugEnvironmentProxy>().getMaybeSentinelValue(cx, id, &v))
|
||||
return false;
|
||||
isTDZ = IsUninitializedLexical(v);
|
||||
} else {
|
||||
isTDZ = IsUninitializedLexicalSlot(env, shape);
|
||||
}
|
||||
}
|
||||
|
||||
if (isTDZ) {
|
||||
env = RuntimeLexicalErrorObject::create(cx, env, JSMSG_UNINITIALIZED_LEXICAL);
|
||||
if (!env)
|
||||
return false;
|
||||
|
@ -1218,7 +1218,7 @@ dnl Checks for library functions.
|
||||
dnl ========================================================
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_FUNC_MEMCMP
|
||||
AC_CHECK_FUNCS([getc_unlocked _getc_nolock gmtime_r localtime_r])
|
||||
AC_CHECK_FUNCS([getc_unlocked _getc_nolock gmtime_r localtime_r pthread_getname_np])
|
||||
|
||||
dnl check for clock_gettime(), the CLOCK_MONOTONIC clock
|
||||
AC_CACHE_CHECK(for clock_gettime(CLOCK_MONOTONIC),
|
||||
|
@ -264,6 +264,7 @@ struct ShellContext
|
||||
JS::PersistentRootedValue promiseRejectionTrackerCallback;
|
||||
JS::PersistentRooted<JobQueue> jobQueue;
|
||||
ExclusiveData<ShellAsyncTasks> asyncTasks;
|
||||
bool drainingJobQueue;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/*
|
||||
@ -428,6 +429,7 @@ ShellContext::ShellContext(JSContext* cx)
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
promiseRejectionTrackerCallback(cx, NullValue()),
|
||||
asyncTasks(cx),
|
||||
drainingJobQueue(false),
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
exitCode(0),
|
||||
quitting(false),
|
||||
@ -771,7 +773,7 @@ DrainJobQueue(JSContext* cx)
|
||||
{
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
if (sc->quitting)
|
||||
if (sc->quitting || sc->drainingJobQueue)
|
||||
return true;
|
||||
|
||||
// Wait for any outstanding async tasks to finish so that the
|
||||
@ -792,6 +794,12 @@ DrainJobQueue(JSContext* cx)
|
||||
asyncTasks->finished.clear();
|
||||
}
|
||||
|
||||
// It doesn't make sense for job queue draining to be reentrant. At the
|
||||
// same time we don't want to assert against it, because that'd make
|
||||
// drainJobQueue unsafe for fuzzers. We do want fuzzers to test this, so
|
||||
// we simply ignore nested calls of drainJobQueue.
|
||||
sc->drainingJobQueue = true;
|
||||
|
||||
RootedObject job(cx);
|
||||
JS::HandleValueArray args(JS::HandleValueArray::empty());
|
||||
RootedValue rval(cx);
|
||||
@ -808,6 +816,7 @@ DrainJobQueue(JSContext* cx)
|
||||
sc->jobQueue[i].set(nullptr);
|
||||
}
|
||||
sc->jobQueue.clear();
|
||||
sc->drainingJobQueue = false;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
return true;
|
||||
}
|
||||
|
@ -185,6 +185,12 @@ Thread::Id GetId();
|
||||
// nothing.
|
||||
void SetName(const char* name);
|
||||
|
||||
// Get the current thread name. As with SetName, not available on all
|
||||
// platforms. On these platforms getName() will give back an empty string (by
|
||||
// storing NUL in nameBuffer[0]). 'len' is the bytes available to be written in
|
||||
// 'nameBuffer', including the terminating NUL.
|
||||
void GetName(char* nameBuffer, size_t len);
|
||||
|
||||
} // namespace ThisThread
|
||||
|
||||
namespace detail {
|
||||
|
@ -164,3 +164,24 @@ js::ThisThread::SetName(const char* name)
|
||||
#endif
|
||||
MOZ_RELEASE_ASSERT(!rv);
|
||||
}
|
||||
|
||||
void
|
||||
js::ThisThread::GetName(char* nameBuffer, size_t len)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(len >= 16);
|
||||
|
||||
int rv;
|
||||
#ifdef HAVE_PTHREAD_GETNAME_NP
|
||||
rv = pthread_getname_np(pthread_self(), nameBuffer, len);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_get_name_np(pthread_self(), nameBuffer, len);
|
||||
rv = 0;
|
||||
#elif defined(__linux__)
|
||||
rv = prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(nameBuffer));
|
||||
#else
|
||||
# error "unsupported platform: no way to read thread name"
|
||||
#endif
|
||||
|
||||
if (rv)
|
||||
nameBuffer[0] = '\0';
|
||||
}
|
||||
|
@ -155,3 +155,10 @@ js::ThisThread::SetName(const char* name)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js::ThisThread::GetName(char* nameBuffer, size_t len)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(len > 0);
|
||||
*nameBuffer = '\0';
|
||||
}
|
||||
|
@ -249,6 +249,7 @@ ReportTooBigCharacter(JSContext* cx, uint32_t v)
|
||||
enum InflateUTF8Action {
|
||||
CountAndReportInvalids,
|
||||
CountAndIgnoreInvalids,
|
||||
AssertNoInvalids,
|
||||
Copy
|
||||
};
|
||||
|
||||
@ -256,11 +257,12 @@ static const uint32_t REPLACE_UTF8 = 0xFFFD;
|
||||
|
||||
// If making changes to this algorithm, make sure to also update
|
||||
// LossyConvertUTF8toUTF16() in dom/wifi/WifiUtils.cpp
|
||||
template <InflateUTF8Action action>
|
||||
template <InflateUTF8Action Action>
|
||||
static bool
|
||||
InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, size_t* dstlenp,
|
||||
bool* isAsciip)
|
||||
{
|
||||
if (Action != AssertNoInvalids)
|
||||
*isAsciip = true;
|
||||
|
||||
// Count how many char16_t characters need to be in the inflated string.
|
||||
@ -271,11 +273,12 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz
|
||||
uint32_t v = uint32_t(src[i]);
|
||||
if (!(v & 0x80)) {
|
||||
// ASCII code unit. Simple copy.
|
||||
if (action == Copy)
|
||||
if (Action == Copy)
|
||||
dst[j] = char16_t(v);
|
||||
|
||||
} else {
|
||||
// Non-ASCII code unit. Determine its length in bytes (n).
|
||||
if (Action != AssertNoInvalids)
|
||||
*isAsciip = false;
|
||||
uint32_t n = 1;
|
||||
while (v & (0x80 >> n))
|
||||
@ -283,14 +286,16 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz
|
||||
|
||||
#define INVALID(report, arg, n2) \
|
||||
do { \
|
||||
if (action == CountAndReportInvalids) { \
|
||||
if (Action == CountAndReportInvalids) { \
|
||||
report(cx, arg); \
|
||||
return false; \
|
||||
} else if (Action == AssertNoInvalids) { \
|
||||
MOZ_CRASH("invalid UTF-8 string: " # report); \
|
||||
} else { \
|
||||
if (action == Copy) \
|
||||
if (Action == Copy) \
|
||||
dst[j] = char16_t(REPLACE_UTF8); \
|
||||
else \
|
||||
MOZ_ASSERT(action == CountAndIgnoreInvalids); \
|
||||
MOZ_ASSERT(Action == CountAndIgnoreInvalids); \
|
||||
n = n2; \
|
||||
goto invalidMultiByteCodeUnit; \
|
||||
} \
|
||||
@ -323,17 +328,17 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz
|
||||
v = JS::Utf8ToOneUcs4Char((uint8_t*)&src[i], n);
|
||||
if (v < 0x10000) {
|
||||
// The n-byte UTF8 code unit will fit in a single char16_t.
|
||||
if (action == Copy)
|
||||
if (Action == Copy)
|
||||
dst[j] = char16_t(v);
|
||||
|
||||
} else {
|
||||
v -= 0x10000;
|
||||
if (v <= 0xFFFFF) {
|
||||
// The n-byte UTF8 code unit will fit in two char16_t units.
|
||||
if (action == Copy)
|
||||
if (Action == Copy)
|
||||
dst[j] = char16_t((v >> 10) + 0xD800);
|
||||
j++;
|
||||
if (action == Copy)
|
||||
if (Action == Copy)
|
||||
dst[j] = char16_t((v & 0x3FF) + 0xDC00);
|
||||
|
||||
} else {
|
||||
@ -350,20 +355,20 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz
|
||||
}
|
||||
}
|
||||
|
||||
if (Action != AssertNoInvalids)
|
||||
*dstlenp = j;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*CountAction)(JSContext*, const UTF8Chars, char16_t*, size_t*, bool* isAsciip);
|
||||
|
||||
template <InflateUTF8Action Action>
|
||||
static TwoByteCharsZ
|
||||
InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, CountAction countAction, size_t* outlen)
|
||||
InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, size_t* outlen)
|
||||
{
|
||||
*outlen = 0;
|
||||
|
||||
bool isAscii;
|
||||
if (!countAction(cx, src, /* dst = */ nullptr, outlen, &isAscii))
|
||||
if (!InflateUTF8StringToBuffer<Action>(cx, src, /* dst = */ nullptr, outlen, &isAscii))
|
||||
return TwoByteCharsZ();
|
||||
|
||||
char16_t* dst = cx->pod_malloc<char16_t>(*outlen + 1); // +1 for NUL
|
||||
@ -390,14 +395,35 @@ InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, CountAction countAct
|
||||
TwoByteCharsZ
|
||||
JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen)
|
||||
{
|
||||
return InflateUTF8StringHelper(cx, utf8, InflateUTF8StringToBuffer<CountAndReportInvalids>,
|
||||
outlen);
|
||||
return InflateUTF8StringHelper<CountAndReportInvalids>(cx, utf8, outlen);
|
||||
}
|
||||
|
||||
TwoByteCharsZ
|
||||
JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen)
|
||||
{
|
||||
UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str()));
|
||||
return InflateUTF8StringHelper<CountAndReportInvalids>(cx, chars, outlen);
|
||||
}
|
||||
|
||||
TwoByteCharsZ
|
||||
JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen)
|
||||
{
|
||||
return InflateUTF8StringHelper(cx, utf8, InflateUTF8StringToBuffer<CountAndIgnoreInvalids>,
|
||||
outlen);
|
||||
return InflateUTF8StringHelper<CountAndIgnoreInvalids>(cx, utf8, outlen);
|
||||
}
|
||||
|
||||
TwoByteCharsZ
|
||||
JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen)
|
||||
{
|
||||
UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str()));
|
||||
return InflateUTF8StringHelper<CountAndIgnoreInvalids>(cx, chars, outlen);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
JS::ConstUTF8CharsZ::validate(size_t aLength)
|
||||
{
|
||||
MOZ_ASSERT(data_);
|
||||
UTF8Chars chars(data_, aLength);
|
||||
InflateUTF8StringToBuffer<AssertNoInvalids>(nullptr, chars, nullptr, nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
@ -4845,7 +4845,7 @@ Debugger::setupTraceLogger(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
uint32_t textId = TLStringToTextId(linear);
|
||||
|
||||
if (!TLTextIdIsToggable(textId)) {
|
||||
if (!TLTextIdIsTogglable(textId)) {
|
||||
args.rval().setBoolean(false);
|
||||
return true;
|
||||
}
|
||||
|
@ -1587,14 +1587,20 @@ js::StartOffThreadCompression(ExclusiveContext* cx, SourceCompressionTask* task)
|
||||
bool
|
||||
js::StartPromiseTask(JSContext* cx, UniquePtr<PromiseTask> task)
|
||||
{
|
||||
// If we fail to start, by interface contract, it is because the JSContext
|
||||
// is in the process of shutting down. Since promise handlers are not
|
||||
// necessarily run while shutting down *anyway*, we simply ignore the error.
|
||||
// This is symmetric with the handling of errors in finishAsyncTaskCallback
|
||||
// which, since it is off the JSContext's owner thread, cannot report an
|
||||
// error anyway.
|
||||
if (!cx->startAsyncTaskCallback(cx, task.get())) {
|
||||
MOZ_ASSERT(!cx->isExceptionPending());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Per interface contract, after startAsyncTaskCallback succeeds,
|
||||
// finishAsyncTaskCallback *must* be called on all paths.
|
||||
|
||||
if (!cx->startAsyncTaskCallback(cx, task.get())) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoLockHelperThreadState lock;
|
||||
|
||||
if (!HelperThreadState().promiseTasks(lock).append(task.get())) {
|
||||
|
@ -81,14 +81,14 @@ IsUninitializedLexical(const Value& val)
|
||||
static inline bool
|
||||
IsUninitializedLexicalSlot(HandleObject obj, HandleShape shape)
|
||||
{
|
||||
MOZ_ASSERT(shape);
|
||||
if (obj->is<WithEnvironmentObject>())
|
||||
return false;
|
||||
// We check for IsImplicitDenseOrTypedArrayElement even though the shape
|
||||
// is always a non-indexed property because proxy hooks may return a
|
||||
// "non-native property found" shape, which happens to be encoded in the
|
||||
// same way as the "dense element" shape. See MarkNonNativePropertyFound.
|
||||
if (!shape ||
|
||||
IsImplicitDenseOrTypedArrayElement(shape) ||
|
||||
if (IsImplicitDenseOrTypedArrayElement(shape) ||
|
||||
!shape->hasSlot() ||
|
||||
!shape->hasDefaultGetter() ||
|
||||
!shape->hasDefaultSetter())
|
||||
|
@ -4428,10 +4428,9 @@ js::ThrowMsgOperation(JSContext* cx, const unsigned errorNum)
|
||||
bool
|
||||
js::GetAndClearException(JSContext* cx, MutableHandleValue res)
|
||||
{
|
||||
bool status = cx->getPendingException(res);
|
||||
cx->clearPendingException();
|
||||
if (!status)
|
||||
if (!cx->getPendingException(res))
|
||||
return false;
|
||||
cx->clearPendingException();
|
||||
|
||||
// Allow interrupting deeply nested exception handling.
|
||||
return CheckForInterrupt(cx);
|
||||
|
@ -26,6 +26,7 @@
|
||||
# include "asmjs/WasmSignalHandlers.h"
|
||||
#endif
|
||||
#include "builtin/AtomicsObject.h"
|
||||
#include "builtin/Promise.h"
|
||||
#include "ds/FixedSizeHash.h"
|
||||
#include "frontend/NameCollections.h"
|
||||
#include "gc/GCRuntime.h"
|
||||
@ -66,8 +67,7 @@ class EnterDebuggeeNoExecute;
|
||||
class TraceLoggerThread;
|
||||
#endif
|
||||
|
||||
class PromiseTask;
|
||||
typedef Vector<PromiseTask*, 0, SystemAllocPolicy> PromiseTaskPtrVector;
|
||||
typedef Vector<UniquePtr<PromiseTask>, 0, SystemAllocPolicy> PromiseTaskPtrVector;
|
||||
|
||||
/* Thread Local Storage slot for storing the runtime for a thread. */
|
||||
extern MOZ_THREAD_LOCAL(PerThreadData*) TlsPerThreadData;
|
||||
|
@ -358,15 +358,10 @@ TraceLoggerThread::getOrCreateEventPayload(const char* text)
|
||||
|
||||
AutoTraceLog internal(this, TraceLogger_Internal);
|
||||
|
||||
size_t len = strlen(text);
|
||||
char* str = js_pod_malloc<char>(len + 1);
|
||||
char* str = js_strdup(text);
|
||||
if (!str)
|
||||
return nullptr;
|
||||
|
||||
DebugOnly<size_t> ret = snprintf(str, len + 1, "%s", text);
|
||||
MOZ_ASSERT(ret == len);
|
||||
MOZ_ASSERT(strlen(str) == len);
|
||||
|
||||
uint32_t textId = nextTextId;
|
||||
|
||||
TraceLoggerEventPayload* payload = js_new<TraceLoggerEventPayload>(textId, str);
|
||||
@ -546,7 +541,7 @@ TraceLoggerThread::log(uint32_t id)
|
||||
MOZ_ASSERT(traceLoggerState);
|
||||
|
||||
// We request for 3 items to add, since if we don't have enough room
|
||||
// we record the time it took to make more place. To log this information
|
||||
// we record the time it took to make more space. To log this information
|
||||
// we need 2 extra free entries.
|
||||
if (!events.hasSpaceForAdd(3)) {
|
||||
uint64_t start = rdtsc() - traceLoggerState->startupTime;
|
||||
@ -674,7 +669,7 @@ TraceLoggerThreadState::init()
|
||||
);
|
||||
for (uint32_t i = 1; i < TraceLogger_Last; i++) {
|
||||
TraceLoggerTextId id = TraceLoggerTextId(i);
|
||||
if (!TLTextIdIsToggable(id))
|
||||
if (!TLTextIdIsTogglable(id))
|
||||
continue;
|
||||
printf(" %s\n", TLTextIdString(id));
|
||||
}
|
||||
@ -685,7 +680,7 @@ TraceLoggerThreadState::init()
|
||||
|
||||
for (uint32_t i = 1; i < TraceLogger_Last; i++) {
|
||||
TraceLoggerTextId id = TraceLoggerTextId(i);
|
||||
if (TLTextIdIsToggable(id))
|
||||
if (TLTextIdIsTogglable(id))
|
||||
enabledTextIds[i] = ContainsFlag(env, TLTextIdString(id));
|
||||
else
|
||||
enabledTextIds[i] = true;
|
||||
@ -790,7 +785,7 @@ TraceLoggerThreadState::init()
|
||||
void
|
||||
TraceLoggerThreadState::enableTextId(JSContext* cx, uint32_t textId)
|
||||
{
|
||||
MOZ_ASSERT(TLTextIdIsToggable(textId));
|
||||
MOZ_ASSERT(TLTextIdIsTogglable(textId));
|
||||
|
||||
if (enabledTextIds[textId])
|
||||
return;
|
||||
@ -813,7 +808,7 @@ TraceLoggerThreadState::enableTextId(JSContext* cx, uint32_t textId)
|
||||
void
|
||||
TraceLoggerThreadState::disableTextId(JSContext* cx, uint32_t textId)
|
||||
{
|
||||
MOZ_ASSERT(TLTextIdIsToggable(textId));
|
||||
MOZ_ASSERT(TLTextIdIsTogglable(textId));
|
||||
|
||||
if (!enabledTextIds[textId])
|
||||
return;
|
||||
@ -1016,10 +1011,10 @@ TraceLoggerEvent::~TraceLoggerEvent()
|
||||
TraceLoggerEvent&
|
||||
TraceLoggerEvent::operator=(const TraceLoggerEvent& other)
|
||||
{
|
||||
if (hasPayload())
|
||||
payload()->release();
|
||||
if (other.hasPayload())
|
||||
other.payload()->use();
|
||||
if (hasPayload())
|
||||
payload()->release();
|
||||
|
||||
payload_ = other.payload_;
|
||||
|
||||
|
@ -35,10 +35,9 @@ namespace jit {
|
||||
/*
|
||||
* Tracelogging overview.
|
||||
*
|
||||
* Tracelogging makes it possible to trace the occurrence of a single event and/or
|
||||
* the start and stop of an event. This is implemented to give an as low overhead as
|
||||
* possible so it doesn't interfere with running.
|
||||
*
|
||||
* Tracelogging makes it possible to trace the occurrence of a single event
|
||||
* and/or the start and stop of an event. This is implemented with as low
|
||||
* overhead as possible to not interfere with running.
|
||||
*
|
||||
* Logging something is done in 3 stages.
|
||||
* 1) Get the tracelogger of the current thread.
|
||||
@ -54,6 +53,7 @@ namespace jit {
|
||||
* There are also some predefined events. They are located in
|
||||
* TraceLoggerTextId. They don't require to create an TraceLoggerEvent and
|
||||
* can also be used as an argument to these functions.
|
||||
*
|
||||
* 3) Log the occurrence of a single event:
|
||||
* - TraceLogTimestamp(logger, TraceLoggerTextId);
|
||||
* Note: it is temporarily not supported to provide an TraceLoggerEvent as
|
||||
@ -66,8 +66,8 @@ namespace jit {
|
||||
* - TraceLogStopEvent(logger, TraceLoggerEvent);
|
||||
*
|
||||
* or the start/stop of an event with a RAII class:
|
||||
* - AutoTraceLog logger(logger, TraceLoggerTextId);
|
||||
* - AutoTraceLog logger(logger, TraceLoggerEvent);
|
||||
* - AutoTraceLog atl(logger, TraceLoggerTextId);
|
||||
* - AutoTraceLog atl(logger, TraceLoggerEvent);
|
||||
*/
|
||||
|
||||
class AutoTraceLog;
|
||||
@ -75,11 +75,10 @@ class TraceLoggerEventPayload;
|
||||
class TraceLoggerThread;
|
||||
|
||||
/**
|
||||
* An event that can be used to report start/stop events to TraceLogger.
|
||||
* It prepares the given info, by requesting a TraceLoggerEventPayload for the
|
||||
* given info. (Which contains the string that needs to get reported and an
|
||||
* unique id). It also increases the useCount of this payload, so it cannot
|
||||
* get removed.
|
||||
* An event that can be used to report start/stop events to TraceLogger. It
|
||||
* prepares the given info by requesting a TraceLoggerEventPayload containing
|
||||
* the string to report and an unique id. It also increases the useCount of
|
||||
* this payload, so it cannot get removed.
|
||||
*/
|
||||
class TraceLoggerEvent {
|
||||
private:
|
||||
@ -117,10 +116,10 @@ class TraceLoggerEvent {
|
||||
};
|
||||
|
||||
/**
|
||||
* An internal class holding the to-report string information, together with an
|
||||
* An internal class holding the string information to report, together with an
|
||||
* unique id and a useCount. Whenever this useCount reaches 0, this event
|
||||
* cannot get started/stopped anymore. Though consumers might still request the
|
||||
* to-report string information.
|
||||
* cannot get started/stopped anymore. Consumers may still request the
|
||||
* string information.
|
||||
*/
|
||||
class TraceLoggerEventPayload {
|
||||
uint32_t textId_;
|
||||
@ -180,7 +179,8 @@ class TraceLoggerThread
|
||||
ContinuousSpace<EventEntry> events;
|
||||
|
||||
// Every time the events get flushed, this count is increased by one.
|
||||
// This together with events.lastEntryId(), gives an unique position.
|
||||
// Together with events.lastEntryId(), this gives an unique id for every
|
||||
// event.
|
||||
uint32_t iteration_;
|
||||
|
||||
public:
|
||||
@ -241,8 +241,9 @@ class TraceLoggerThread
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are in a consecutive iteration we are only sure we didn't lose any events,
|
||||
// when the lastSize equals the maximum size 'events' can get.
|
||||
// If we are in the next consecutive iteration we are only sure we
|
||||
// didn't lose any events when the lastSize equals the maximum size
|
||||
// 'events' can get.
|
||||
if (lastIteration == iteration_ - 1 && lastSize == events.maxSize())
|
||||
return false;
|
||||
|
||||
@ -276,7 +277,7 @@ class TraceLoggerThread
|
||||
void stopEvent(const TraceLoggerEvent& event);
|
||||
|
||||
// These functions are actually private and shouldn't be used in normal
|
||||
// code. They are made public so they can get used in assembly.
|
||||
// code. They are made public so they can be used in assembly.
|
||||
void logTimestamp(uint32_t id);
|
||||
void startEvent(uint32_t id);
|
||||
void stopEvent(uint32_t id);
|
||||
|
@ -14,41 +14,104 @@
|
||||
#endif
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "js/UniquePtr.h"
|
||||
#include "threading/LockGuard.h"
|
||||
#include "threading/Thread.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#ifndef TRACE_LOG_DIR
|
||||
#ifndef DEFAULT_TRACE_LOG_DIR
|
||||
# if defined(_WIN32)
|
||||
# define TRACE_LOG_DIR ""
|
||||
# define DEFAULT_TRACE_LOG_DIR "."
|
||||
# else
|
||||
# define TRACE_LOG_DIR "/tmp/"
|
||||
# define DEFAULT_TRACE_LOG_DIR "/tmp/"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
using mozilla::MakeScopeExit;
|
||||
using mozilla::NativeEndian;
|
||||
|
||||
TraceLoggerGraphState* traceLoggerGraphState = nullptr;
|
||||
|
||||
// gcc and clang have these in symcat.h, but MSVC does not.
|
||||
#ifndef STRINGX
|
||||
# define STRINGX(x) #x
|
||||
#endif
|
||||
#ifndef XSTRING
|
||||
# define XSTRING(macro) STRINGX(macro)
|
||||
#endif
|
||||
|
||||
#define MAX_LOGGERS 999
|
||||
|
||||
// Return a filename relative to the output directory. %u and %d substitutions
|
||||
// are allowed, with %u standing for a full 32-bit number and %d standing for
|
||||
// an up to 3-digit number.
|
||||
static js::UniqueChars
|
||||
AllocTraceLogFilename(const char* pattern, ...) {
|
||||
js::UniqueChars filename;
|
||||
|
||||
va_list ap;
|
||||
|
||||
static const char* outdir = getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
|
||||
size_t len = strlen(outdir) + 1; // "+ 1" is for the '/'
|
||||
|
||||
for (const char* p = pattern; *p; p++) {
|
||||
if (*p == '%') {
|
||||
p++;
|
||||
if (*p == 'u')
|
||||
len += sizeof("4294967295") - 1;
|
||||
else if (*p == 'd')
|
||||
len += sizeof(XSTRING(MAX_LOGGERS)) - 1;
|
||||
else
|
||||
MOZ_CRASH("Invalid format");
|
||||
} else {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
len++; // For the terminating NUL.
|
||||
|
||||
filename.reset((char*) js_malloc(len));
|
||||
if (!filename)
|
||||
return nullptr;
|
||||
char* rest = filename.get() + sprintf(filename.get(), "%s/", outdir);
|
||||
|
||||
va_start(ap, pattern);
|
||||
int ret = vsnprintf(rest, len, pattern, ap);
|
||||
va_end(ap);
|
||||
if (ret < 0)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(size_t(ret) <= len - (strlen(outdir) + 1),
|
||||
"overran TL filename buffer; %d given too large a value?");
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool
|
||||
TraceLoggerGraphState::init()
|
||||
{
|
||||
pid_ = (uint32_t) getpid();
|
||||
|
||||
char filename[sizeof TRACE_LOG_DIR "tl-data.4294967295.json"];
|
||||
sprintf(filename, TRACE_LOG_DIR "tl-data.%u.json", pid_);
|
||||
out = fopen(filename, "w");
|
||||
if (!out)
|
||||
js::UniqueChars filename = AllocTraceLogFilename("tl-data.%u.json", pid_);
|
||||
out = fopen(filename.get(), "w");
|
||||
if (!out) {
|
||||
fprintf(stderr, "warning: failed to create TraceLogger output file %s\n", filename.get());
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(out, "[");
|
||||
|
||||
// Write the last tl-data.*.json file to tl-data.json.
|
||||
// Write the latest tl-data.*.json file to tl-data.json.
|
||||
// In most cases that is the wanted file.
|
||||
if (FILE* last = fopen(TRACE_LOG_DIR "tl-data.json", "w")) {
|
||||
fprintf(last, "\"tl-data.%u.json\"", pid_);
|
||||
js::UniqueChars masterFilename = AllocTraceLogFilename("tl-data.json");
|
||||
if (FILE* last = fopen(masterFilename.get(), "w")) {
|
||||
char *basename = strrchr(filename.get(), '/');
|
||||
basename = basename ? basename + 1 : filename.get();
|
||||
fprintf(last, "\"%s\"", basename);
|
||||
fclose(last);
|
||||
}
|
||||
|
||||
@ -78,8 +141,9 @@ TraceLoggerGraphState::nextLoggerId()
|
||||
|
||||
MOZ_ASSERT(initialized);
|
||||
|
||||
if (numLoggers > 999) {
|
||||
fprintf(stderr, "TraceLogging: Can't create more than 999 different loggers.");
|
||||
if (numLoggers > MAX_LOGGERS) {
|
||||
fputs("TraceLogging: Can't create more than " XSTRING(MAX_LOGGERS) " different loggers.",
|
||||
stderr);
|
||||
return uint32_t(-1);
|
||||
}
|
||||
|
||||
@ -92,8 +156,19 @@ TraceLoggerGraphState::nextLoggerId()
|
||||
}
|
||||
|
||||
int written = fprintf(out, "{\"tree\":\"tl-tree.%u.%d.tl\", \"events\":\"tl-event.%u.%d.tl\", "
|
||||
"\"dict\":\"tl-dict.%u.%d.json\", \"treeFormat\":\"64,64,31,1,32\"}",
|
||||
"\"dict\":\"tl-dict.%u.%d.json\", \"treeFormat\":\"64,64,31,1,32\"",
|
||||
pid_, numLoggers, pid_, numLoggers, pid_, numLoggers);
|
||||
|
||||
if (written > 0) {
|
||||
char threadName[16];
|
||||
js::ThisThread::GetName(threadName, sizeof(threadName));
|
||||
if (threadName[0])
|
||||
written = fprintf(out, ", \"threadName\":\"%s\"", threadName);
|
||||
}
|
||||
|
||||
if (written > 0)
|
||||
written = fprintf(out, "}");
|
||||
|
||||
if (written < 0) {
|
||||
fprintf(stderr, "TraceLogging: Error while writing.\n");
|
||||
return uint32_t(-1);
|
||||
@ -132,57 +207,39 @@ js::DestroyTraceLoggerGraphState()
|
||||
bool
|
||||
TraceLoggerGraph::init(uint64_t startTimestamp)
|
||||
{
|
||||
if (!tree.init()) {
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
if (!stack.init()) {
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
auto fail = MakeScopeExit([&] { failed = true; });
|
||||
|
||||
if (!EnsureTraceLoggerGraphState()) {
|
||||
failed = true;
|
||||
if (!tree.init())
|
||||
return false;
|
||||
if (!stack.init())
|
||||
return false;
|
||||
|
||||
if (!EnsureTraceLoggerGraphState())
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t loggerId = traceLoggerGraphState->nextLoggerId();
|
||||
if (loggerId == uint32_t(-1)) {
|
||||
failed = true;
|
||||
if (loggerId == uint32_t(-1))
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t pid = traceLoggerGraphState->pid();
|
||||
|
||||
char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.4294967295.100.json"];
|
||||
sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%u.%d.json", pid, loggerId);
|
||||
dictFile = fopen(dictFilename, "w");
|
||||
if (!dictFile) {
|
||||
failed = true;
|
||||
js::UniqueChars dictFilename = AllocTraceLogFilename("tl-dict.%u.%d.json", pid, loggerId);
|
||||
dictFile = fopen(dictFilename.get(), "w");
|
||||
if (!dictFile)
|
||||
return false;
|
||||
}
|
||||
auto cleanupDict = MakeScopeExit([&] { fclose(dictFile); dictFile = nullptr; });
|
||||
|
||||
char treeFilename[sizeof TRACE_LOG_DIR "tl-tree.4294967295.100.tl"];
|
||||
sprintf(treeFilename, TRACE_LOG_DIR "tl-tree.%u.%d.tl", pid, loggerId);
|
||||
treeFile = fopen(treeFilename, "w+b");
|
||||
if (!treeFile) {
|
||||
fclose(dictFile);
|
||||
dictFile = nullptr;
|
||||
failed = true;
|
||||
js::UniqueChars treeFilename = AllocTraceLogFilename("tl-tree.%u.%d.tl", pid, loggerId);
|
||||
treeFile = fopen(treeFilename.get(), "w+b");
|
||||
if (!treeFile)
|
||||
return false;
|
||||
}
|
||||
auto cleanupTree = MakeScopeExit([&] { fclose(treeFile); treeFile = nullptr; });
|
||||
|
||||
char eventFilename[sizeof TRACE_LOG_DIR "tl-event.4294967295.100.tl"];
|
||||
sprintf(eventFilename, TRACE_LOG_DIR "tl-event.%u.%d.tl", pid, loggerId);
|
||||
eventFile = fopen(eventFilename, "wb");
|
||||
if (!eventFile) {
|
||||
fclose(dictFile);
|
||||
fclose(treeFile);
|
||||
dictFile = nullptr;
|
||||
treeFile = nullptr;
|
||||
failed = true;
|
||||
js::UniqueChars eventFilename = AllocTraceLogFilename("tl-event.%u.%d.tl", pid, loggerId);
|
||||
eventFile = fopen(eventFilename.get(), "wb");
|
||||
if (!eventFile)
|
||||
return false;
|
||||
}
|
||||
auto cleanupEvent = MakeScopeExit([&] { fclose(eventFile); eventFile = nullptr; });
|
||||
|
||||
// Create the top tree node and corresponding first stack item.
|
||||
TreeEntry& treeEntry = tree.pushUninitialized();
|
||||
@ -197,19 +254,16 @@ TraceLoggerGraph::init(uint64_t startTimestamp)
|
||||
stackEntry.setLastChildId(0);
|
||||
stackEntry.setActive(true);
|
||||
|
||||
int written = fprintf(dictFile, "[");
|
||||
if (written < 0) {
|
||||
if (fprintf(dictFile, "[") < 0) {
|
||||
fprintf(stderr, "TraceLogging: Error while writing.\n");
|
||||
fclose(dictFile);
|
||||
fclose(treeFile);
|
||||
fclose(eventFile);
|
||||
dictFile = nullptr;
|
||||
treeFile = nullptr;
|
||||
eventFile = nullptr;
|
||||
failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fail.release();
|
||||
cleanupDict.release();
|
||||
cleanupTree.release();
|
||||
cleanupEvent.release();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -227,8 +281,8 @@ TraceLoggerGraph::~TraceLoggerGraph()
|
||||
|
||||
if (!failed && treeFile) {
|
||||
// Make sure every start entry has a corresponding stop value.
|
||||
// We temporary enable logging for this. Stop doesn't need any extra data,
|
||||
// so is safe to do, even when we encountered OOM.
|
||||
// We temporarily enable logging for this. Stop doesn't need any extra data,
|
||||
// so is safe to do even when we have encountered OOM.
|
||||
enabled = 1;
|
||||
while (stack.size() > 1)
|
||||
stopEvent(0);
|
||||
|
@ -199,14 +199,7 @@ class TraceLoggerGraph
|
||||
};
|
||||
|
||||
public:
|
||||
TraceLoggerGraph()
|
||||
: failed(false)
|
||||
, enabled(false)
|
||||
#ifdef DEBUG
|
||||
, nextTextId(0)
|
||||
#endif
|
||||
, treeOffset(0)
|
||||
{ }
|
||||
TraceLoggerGraph() {}
|
||||
~TraceLoggerGraph();
|
||||
|
||||
bool init(uint64_t timestamp);
|
||||
@ -223,19 +216,19 @@ class TraceLoggerGraph
|
||||
}
|
||||
|
||||
private:
|
||||
bool failed;
|
||||
bool enabled;
|
||||
bool failed = false;
|
||||
bool enabled = false;
|
||||
#ifdef DEBUG
|
||||
uint32_t nextTextId;
|
||||
uint32_t nextTextId = 0;
|
||||
#endif
|
||||
|
||||
FILE* dictFile;
|
||||
FILE* treeFile;
|
||||
FILE* eventFile;
|
||||
FILE* dictFile = nullptr;
|
||||
FILE* treeFile = nullptr;
|
||||
FILE* eventFile = nullptr;
|
||||
|
||||
ContinuousSpace<TreeEntry> tree;
|
||||
ContinuousSpace<StackEntry> stack;
|
||||
uint32_t treeOffset;
|
||||
uint32_t treeOffset = 0;
|
||||
|
||||
// Helper functions that convert a TreeEntry in different endianness
|
||||
// in place.
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "jsalloc.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
// Tree items, meaning they have a start and stop and form a nested tree.
|
||||
#define TRACELOGGER_TREE_ITEMS(_) \
|
||||
_(AnnotateScripts) \
|
||||
_(Baseline) \
|
||||
@ -70,6 +71,7 @@
|
||||
_(IonBuilderRestartLoop) \
|
||||
_(VMSpecific)
|
||||
|
||||
// Log items, with timestamp only.
|
||||
#define TRACELOGGER_LOG_ITEMS(_) \
|
||||
_(Bailout) \
|
||||
_(Invalidation) \
|
||||
@ -107,8 +109,9 @@ TLTextIdString(TraceLoggerTextId id)
|
||||
uint32_t
|
||||
TLStringToTextId(JSLinearString* str);
|
||||
|
||||
// Return whether a given item id can be enabled/disabled.
|
||||
inline bool
|
||||
TLTextIdIsToggable(uint32_t id)
|
||||
TLTextIdIsTogglable(uint32_t id)
|
||||
{
|
||||
if (id == TraceLogger_Error)
|
||||
return false;
|
||||
@ -143,7 +146,7 @@ class ContinuousSpace {
|
||||
uint32_t size_;
|
||||
uint32_t capacity_;
|
||||
|
||||
// The maximum amount of ram memory a continuous space structure can take (in bytes).
|
||||
// The maximum number of bytes of RAM a continuous space structure can take.
|
||||
static const uint32_t LIMIT = 200 * 1024 * 1024;
|
||||
|
||||
public:
|
||||
|
@ -341,7 +341,7 @@ StringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval)
|
||||
/**
|
||||
* As above, but for mozilla::dom::DOMString.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE
|
||||
inline
|
||||
bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
|
||||
JS::MutableHandleValue rval)
|
||||
{
|
||||
|
@ -401,10 +401,15 @@ RestyleManager::RestyleForEmptyChange(Element* aContainer)
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManager::RestyleForAppend(Element* aContainer,
|
||||
RestyleManager::RestyleForAppend(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent)
|
||||
{
|
||||
NS_ASSERTION(aContainer, "must have container for append");
|
||||
// The container cannot be a document, but might be a ShadowRoot.
|
||||
if (!aContainer->IsElement()) {
|
||||
return;
|
||||
}
|
||||
Element* container = aContainer->AsElement();
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
|
||||
@ -414,7 +419,7 @@ RestyleManager::RestyleForAppend(Element* aContainer,
|
||||
}
|
||||
#endif
|
||||
uint32_t selectorFlags =
|
||||
aContainer->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
|
||||
container->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
|
||||
~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
||||
if (selectorFlags == 0)
|
||||
return;
|
||||
@ -422,7 +427,7 @@ RestyleManager::RestyleForAppend(Element* aContainer,
|
||||
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
||||
// see whether we need to restyle the container
|
||||
bool wasEmpty = true; // :empty or :-moz-only-whitespace
|
||||
for (nsIContent* cur = aContainer->GetFirstChild();
|
||||
for (nsIContent* cur = container->GetFirstChild();
|
||||
cur != aFirstNewContent;
|
||||
cur = cur->GetNextSibling()) {
|
||||
// We don't know whether we're testing :empty or :-moz-only-whitespace,
|
||||
@ -435,13 +440,13 @@ RestyleManager::RestyleForAppend(Element* aContainer,
|
||||
}
|
||||
}
|
||||
if (wasEmpty) {
|
||||
RestyleForEmptyChange(aContainer);
|
||||
RestyleForEmptyChange(container);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
|
||||
PostRestyleEvent(aContainer, eRestyle_Subtree, nsChangeHint(0));
|
||||
PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
|
||||
// Restyling the container is the most we can do here, so we're done.
|
||||
return;
|
||||
}
|
||||
@ -485,20 +490,26 @@ RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
|
||||
// The comments are written and variables are named in terms of it being
|
||||
// a ContentInserted notification.
|
||||
void
|
||||
RestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
||||
RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
|
||||
nsIContent* aChild)
|
||||
{
|
||||
// The container might be a document or a ShadowRoot.
|
||||
if (!aContainer->IsElement()) {
|
||||
return;
|
||||
}
|
||||
Element* container = aContainer->AsElement();
|
||||
|
||||
NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
|
||||
"anonymous nodes should not be in child lists");
|
||||
uint32_t selectorFlags =
|
||||
aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
|
||||
container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
|
||||
if (selectorFlags == 0)
|
||||
return;
|
||||
|
||||
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
||||
// see whether we need to restyle the container
|
||||
bool wasEmpty = true; // :empty or :-moz-only-whitespace
|
||||
for (nsIContent* child = aContainer->GetFirstChild();
|
||||
for (nsIContent* child = container->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
if (child == aChild)
|
||||
@ -513,13 +524,13 @@ RestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
||||
}
|
||||
}
|
||||
if (wasEmpty) {
|
||||
RestyleForEmptyChange(aContainer);
|
||||
RestyleForEmptyChange(container);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
|
||||
PostRestyleEvent(aContainer, eRestyle_Subtree, nsChangeHint(0));
|
||||
PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
|
||||
// Restyling the container is the most we can do here, so we're done.
|
||||
return;
|
||||
}
|
||||
@ -532,7 +543,7 @@ RestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
||||
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
||||
// restyle the previously-first element child if it is after this node
|
||||
bool passedChild = false;
|
||||
for (nsIContent* content = aContainer->GetFirstChild();
|
||||
for (nsIContent* content = container->GetFirstChild();
|
||||
content;
|
||||
content = content->GetNextSibling()) {
|
||||
if (content == aChild) {
|
||||
@ -549,7 +560,7 @@ RestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
||||
}
|
||||
// restyle the previously-last element child if it is before this node
|
||||
passedChild = false;
|
||||
for (nsIContent* content = aContainer->GetLastChild();
|
||||
for (nsIContent* content = container->GetLastChild();
|
||||
content;
|
||||
content = content->GetPreviousSibling()) {
|
||||
if (content == aChild) {
|
||||
@ -568,10 +579,16 @@ RestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManager::RestyleForRemove(Element* aContainer,
|
||||
RestyleManager::ContentRemoved(nsINode* aContainer,
|
||||
nsIContent* aOldChild,
|
||||
nsIContent* aFollowingSibling)
|
||||
{
|
||||
// The container might be a document or a ShadowRoot.
|
||||
if (!aContainer->IsElement()) {
|
||||
return;
|
||||
}
|
||||
Element* container = aContainer->AsElement();
|
||||
|
||||
if (aOldChild->IsRootOfAnonymousSubtree()) {
|
||||
// This should be an assert, but this is called incorrectly in
|
||||
// HTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
|
||||
@ -580,14 +597,14 @@ RestyleManager::RestyleForRemove(Element* aContainer,
|
||||
"anonymous nodes should not be in child lists (bug 439258)");
|
||||
}
|
||||
uint32_t selectorFlags =
|
||||
aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
|
||||
container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
|
||||
if (selectorFlags == 0)
|
||||
return;
|
||||
|
||||
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
||||
// see whether we need to restyle the container
|
||||
bool isEmpty = true; // :empty or :-moz-only-whitespace
|
||||
for (nsIContent* child = aContainer->GetFirstChild();
|
||||
for (nsIContent* child = container->GetFirstChild();
|
||||
child;
|
||||
child = child->GetNextSibling()) {
|
||||
// We don't know whether we're testing :empty or :-moz-only-whitespace,
|
||||
@ -600,13 +617,13 @@ RestyleManager::RestyleForRemove(Element* aContainer,
|
||||
}
|
||||
}
|
||||
if (isEmpty) {
|
||||
RestyleForEmptyChange(aContainer);
|
||||
RestyleForEmptyChange(container);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
|
||||
PostRestyleEvent(aContainer, eRestyle_Subtree, nsChangeHint(0));
|
||||
PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
|
||||
// Restyling the container is the most we can do here, so we're done.
|
||||
return;
|
||||
}
|
||||
@ -619,7 +636,7 @@ RestyleManager::RestyleForRemove(Element* aContainer,
|
||||
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
||||
// restyle the now-first element child if it was after aOldChild
|
||||
bool reachedFollowingSibling = false;
|
||||
for (nsIContent* content = aContainer->GetFirstChild();
|
||||
for (nsIContent* content = container->GetFirstChild();
|
||||
content;
|
||||
content = content->GetNextSibling()) {
|
||||
if (content == aFollowingSibling) {
|
||||
@ -636,7 +653,7 @@ RestyleManager::RestyleForRemove(Element* aContainer,
|
||||
}
|
||||
// restyle the now-last element child if it was before aOldChild
|
||||
reachedFollowingSibling = (aFollowingSibling == nullptr);
|
||||
for (nsIContent* content = aContainer->GetLastChild();
|
||||
for (nsIContent* content = container->GetLastChild();
|
||||
content;
|
||||
content = content->GetPreviousSibling()) {
|
||||
if (content->IsElement()) {
|
||||
|
@ -281,23 +281,37 @@ private:
|
||||
void RestyleForEmptyChange(Element* aContainer);
|
||||
|
||||
public:
|
||||
// Restyling for a ContentInserted (notification after insertion) or
|
||||
// for a CharacterDataChanged. |aContainer| must be non-null; when
|
||||
// the container is null, no work is needed.
|
||||
void RestyleForInsertOrChange(Element* aContainer, nsIContent* aChild);
|
||||
// Handle ContentInserted notifications.
|
||||
void ContentInserted(nsINode* aContainer, nsIContent* aChild)
|
||||
{
|
||||
RestyleForInsertOrChange(aContainer, aChild);
|
||||
}
|
||||
|
||||
// This would be the same as RestyleForInsertOrChange if we got the
|
||||
// Handle ContentAppended notifications.
|
||||
void ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent)
|
||||
{
|
||||
RestyleForAppend(aContainer, aFirstNewContent);
|
||||
}
|
||||
|
||||
// Handle ContentRemoved notifications.
|
||||
//
|
||||
// This would be have the same logic as RestyleForInsertOrChange if we got the
|
||||
// notification before the removal. However, we get it after, so we need the
|
||||
// following sibling in addition to the old child. |aContainer| must be
|
||||
// non-null; when the container is null, no work is needed. aFollowingSibling
|
||||
// is the sibling that used to come after aOldChild before the removal.
|
||||
void RestyleForRemove(Element* aContainer,
|
||||
nsIContent* aOldChild,
|
||||
void ContentRemoved(nsINode* aContainer, nsIContent* aOldChild,
|
||||
nsIContent* aFollowingSibling);
|
||||
|
||||
// Same for a ContentAppended. |aContainer| must be non-null; when
|
||||
// Restyling for a ContentInserted (notification after insertion) or
|
||||
// for a CharacterDataChanged. |aContainer| must be non-null; when
|
||||
// the container is null, no work is needed.
|
||||
void RestyleForAppend(Element* aContainer, nsIContent* aFirstNewContent);
|
||||
void RestyleForInsertOrChange(nsINode* aContainer, nsIContent* aChild);
|
||||
|
||||
// Restyling for a ContentAppended (notification after insertion) or
|
||||
// for a CharacterDataChanged. |aContainer| must be non-null; when
|
||||
// the container is null, no work is needed.
|
||||
void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
|
||||
|
||||
// Process any pending restyles. This should be called after
|
||||
// CreateNeededFrames.
|
||||
|
@ -114,13 +114,17 @@ public:
|
||||
inline void PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
|
||||
nsRestyleHint aRestyleHint);
|
||||
inline void ProcessPendingRestyles();
|
||||
inline void RestyleForInsertOrChange(dom::Element* aContainer,
|
||||
inline void ContentInserted(nsINode* aContainer,
|
||||
nsIContent* aChild);
|
||||
inline void RestyleForAppend(dom::Element* aContainer,
|
||||
inline void ContentAppended(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent);
|
||||
inline void RestyleForRemove(dom::Element* aContainer,
|
||||
inline void ContentRemoved(nsINode* aContainer,
|
||||
nsIContent* aOldChild,
|
||||
nsIContent* aFollowingSibling);
|
||||
inline void RestyleForInsertOrChange(nsINode* aContainer,
|
||||
nsIContent* aChild);
|
||||
inline void RestyleForAppend(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent);
|
||||
inline nsresult ContentStateChanged(nsIContent* aContent,
|
||||
EventStates aStateMask);
|
||||
inline void AttributeWillChange(dom::Element* aElement,
|
||||
|
@ -87,27 +87,41 @@ RestyleManagerHandle::Ptr::FlushOverflowChangedTracker()
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManagerHandle::Ptr::RestyleForInsertOrChange(dom::Element* aContainer,
|
||||
RestyleManagerHandle::Ptr::ContentInserted(nsINode* aContainer,
|
||||
nsIContent* aChild)
|
||||
{
|
||||
FORWARD(ContentInserted, (aContainer, aChild));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManagerHandle::Ptr::ContentAppended(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent)
|
||||
{
|
||||
FORWARD(ContentAppended, (aContainer, aFirstNewContent));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManagerHandle::Ptr::ContentRemoved(nsINode* aContainer,
|
||||
nsIContent* aOldChild,
|
||||
nsIContent* aFollowingSibling)
|
||||
{
|
||||
FORWARD(ContentRemoved, (aContainer, aOldChild, aFollowingSibling));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManagerHandle::Ptr::RestyleForInsertOrChange(nsINode* aContainer,
|
||||
nsIContent* aChild)
|
||||
{
|
||||
FORWARD(RestyleForInsertOrChange, (aContainer, aChild));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManagerHandle::Ptr::RestyleForAppend(dom::Element* aContainer,
|
||||
RestyleManagerHandle::Ptr::RestyleForAppend(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent)
|
||||
{
|
||||
FORWARD(RestyleForAppend, (aContainer, aFirstNewContent));
|
||||
}
|
||||
|
||||
void
|
||||
RestyleManagerHandle::Ptr::RestyleForRemove(dom::Element* aContainer,
|
||||
nsIContent* aOldChild,
|
||||
nsIContent* aFollowingSibling)
|
||||
{
|
||||
FORWARD(RestyleForRemove, (aContainer, aOldChild, aFollowingSibling));
|
||||
}
|
||||
|
||||
nsresult
|
||||
RestyleManagerHandle::Ptr::ContentStateChanged(nsIContent* aContent,
|
||||
EventStates aStateMask)
|
||||
|
@ -118,7 +118,7 @@ ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
|
||||
// The frame reconstruction step (if needed) will ask for the descendants'
|
||||
// style correctly. If not needed, we're done too.
|
||||
if (!primaryFrame) {
|
||||
aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
|
||||
aContent->UnsetIsDirtyForServo();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,39 +175,26 @@ ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
|
||||
|
||||
// TODO: There are other continuations we still haven't restyled, mostly
|
||||
// pseudo-elements. We have to deal with those, and with anonymous boxes.
|
||||
aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
|
||||
aContent->UnsetIsDirtyForServo();
|
||||
}
|
||||
|
||||
if (aContent->HasDirtyDescendantsForServo()) {
|
||||
MOZ_ASSERT(primaryFrame,
|
||||
"Frame construction should be scheduled, and it takes the "
|
||||
"correct style for the children, so no need to be here.");
|
||||
FlattenedChildIterator it(aContent);
|
||||
StyleChildrenIterator it(aContent);
|
||||
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
|
||||
RecreateStyleContexts(n, primaryFrame->StyleContext(),
|
||||
aStyleSet, aChangeListToProcess);
|
||||
}
|
||||
aContent->UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MarkParentsAsHavingDirtyDescendants(Element* aElement)
|
||||
{
|
||||
nsINode* cur = aElement;
|
||||
while ((cur = cur->GetParentNode())) {
|
||||
if (cur->HasDirtyDescendantsForServo()) {
|
||||
break;
|
||||
}
|
||||
|
||||
cur->SetHasDirtyDescendantsForServo();
|
||||
aContent->UnsetHasDirtyDescendantsForServo();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MarkChildrenAsDirtyForServo(nsIContent* aContent)
|
||||
{
|
||||
FlattenedChildIterator it(aContent);
|
||||
StyleChildrenIterator it(aContent);
|
||||
|
||||
nsIContent* n = it.GetNextChild();
|
||||
bool hadChildren = bool(n);
|
||||
@ -263,17 +250,17 @@ ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
|
||||
// eRestyle_Subtree.
|
||||
if (aHint & (eRestyle_Self | eRestyle_Subtree)) {
|
||||
aElement->SetIsDirtyForServo();
|
||||
MarkParentsAsHavingDirtyDescendants(aElement);
|
||||
aElement->MarkAncestorsAsHavingDirtyDescendantsForServo();
|
||||
// NB: Servo gives us a eRestyle_SomeDescendants when it expects us to run
|
||||
// selector matching on all the descendants. There's a bug on Servo to align
|
||||
// meanings here (#12710) to avoid this potential source of confusion.
|
||||
} else if (aHint & eRestyle_SomeDescendants) {
|
||||
MarkChildrenAsDirtyForServo(aElement);
|
||||
MarkParentsAsHavingDirtyDescendants(aElement);
|
||||
aElement->MarkAncestorsAsHavingDirtyDescendantsForServo();
|
||||
}
|
||||
|
||||
if (aHint & eRestyle_LaterSiblings) {
|
||||
MarkParentsAsHavingDirtyDescendants(aElement);
|
||||
aElement->MarkAncestorsAsHavingDirtyDescendantsForServo();
|
||||
for (nsIContent* cur = aElement->GetNextSibling(); cur;
|
||||
cur = cur->GetNextSibling()) {
|
||||
cur->SetIsDirtyForServo();
|
||||
@ -322,7 +309,7 @@ ServoRestyleManager::ProcessPendingRestyles()
|
||||
|
||||
if (root->IsDirtyForServo() || root->HasDirtyDescendantsForServo()) {
|
||||
mInStyleRefresh = true;
|
||||
styleSet->RestyleSubtree(root);
|
||||
styleSet->StyleDocument(/* aLeaveDirtyBits = */ true);
|
||||
|
||||
// First do any queued-up frame creation. (see bugs 827239 and 997506).
|
||||
//
|
||||
@ -342,41 +329,86 @@ ServoRestyleManager::ProcessPendingRestyles()
|
||||
}
|
||||
}
|
||||
|
||||
mModifiedElements.Clear();
|
||||
|
||||
// NB: we restyle from the root element, but the document also gets the
|
||||
// HAS_DIRTY_DESCENDANTS flag as part of the loop on PostRestyleEvent, and we
|
||||
// use that to check we have pending restyles.
|
||||
//
|
||||
// Thus, they need to get cleared here.
|
||||
MOZ_ASSERT(!doc->IsDirtyForServo());
|
||||
doc->UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
||||
MOZ_ASSERT(!doc->HasDirtyDescendantsForServo());
|
||||
|
||||
mModifiedElements.Clear();
|
||||
|
||||
IncrementRestyleGeneration();
|
||||
}
|
||||
|
||||
void
|
||||
ServoRestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
||||
ServoRestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
|
||||
nsIContent* aChild)
|
||||
{
|
||||
// XXX Emilio we can do way better.
|
||||
PostRestyleEvent(aContainer, eRestyle_Subtree, nsChangeHint(0));
|
||||
//
|
||||
// XXXbholley: We need the Gecko logic here to correctly restyle for things
|
||||
// like :empty and positional selectors (though we may not need to post
|
||||
// restyle events as agressively as the Gecko path does).
|
||||
//
|
||||
// Bug 1297899 tracks this work.
|
||||
//
|
||||
}
|
||||
|
||||
void
|
||||
ServoRestyleManager::RestyleForAppend(Element* aContainer,
|
||||
ServoRestyleManager::ContentInserted(nsINode* aContainer, nsIContent* aChild)
|
||||
{
|
||||
if (!aContainer->ServoData().get()) {
|
||||
// This can happen with display:none. Bug 1297249 tracks more investigation
|
||||
// and assertions here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Style the new subtree because we will most likely need it during subsequent
|
||||
// frame construction. Bug 1298281 tracks deferring this work in the lazy
|
||||
// frame construction case.
|
||||
StyleSet()->StyleNewSubtree(aChild);
|
||||
|
||||
RestyleForInsertOrChange(aContainer, aChild);
|
||||
}
|
||||
|
||||
void
|
||||
ServoRestyleManager::RestyleForAppend(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent)
|
||||
{
|
||||
// XXX Emilio we can do way better.
|
||||
PostRestyleEvent(aContainer, eRestyle_Subtree, nsChangeHint(0));
|
||||
//
|
||||
// XXXbholley: We need the Gecko logic here to correctly restyle for things
|
||||
// like :empty and positional selectors (though we may not need to post
|
||||
// restyle events as agressively as the Gecko path does).
|
||||
//
|
||||
// Bug 1297899 tracks this work.
|
||||
//
|
||||
}
|
||||
|
||||
void
|
||||
ServoRestyleManager::RestyleForRemove(Element* aContainer,
|
||||
ServoRestyleManager::ContentAppended(nsIContent* aContainer,
|
||||
nsIContent* aFirstNewContent)
|
||||
{
|
||||
if (!aContainer->ServoData().get()) {
|
||||
// This can happen with display:none. Bug 1297249 tracks more investigation
|
||||
// and assertions here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Style the new subtree because we will most likely need it during subsequent
|
||||
// frame construction. Bug 1298281 tracks deferring this work in the lazy
|
||||
// frame construction case.
|
||||
if (aFirstNewContent->GetNextSibling()) {
|
||||
aContainer->SetHasDirtyDescendantsForServo();
|
||||
StyleSet()->StyleNewChildren(aContainer);
|
||||
} else {
|
||||
StyleSet()->StyleNewSubtree(aFirstNewContent);
|
||||
}
|
||||
|
||||
RestyleForAppend(aContainer, aFirstNewContent);
|
||||
}
|
||||
|
||||
void
|
||||
ServoRestyleManager::ContentRemoved(nsINode* aContainer,
|
||||
nsIContent* aOldChild,
|
||||
nsIContent* aFollowingSibling)
|
||||
{
|
||||
NS_WARNING("stylo: ServoRestyleManager::RestyleForRemove not implemented");
|
||||
NS_WARNING("stylo: ServoRestyleManager::ContentRemoved not implemented");
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user