Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2016-08-26 09:37:03 -04:00
commit e1fdfb3b73
242 changed files with 6421 additions and 3275 deletions

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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;

View File

@ -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]

View File

@ -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");
});

View File

@ -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. -->

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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();
}
}

View File

@ -1468,7 +1468,7 @@ nsIDocument::nsIDocument()
mHasScrollLinkedEffect(false),
mUserHasInteracted(false)
{
SetIsDocument();
SetIsInDocument();
PR_INIT_CLIST(&mDOMMediaQueryLists);
}

View File

@ -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);

View File

@ -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

View File

@ -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); }

View File

@ -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;

View File

@ -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*() {

View File

@ -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>

View File

@ -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();

View File

@ -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.

View File

@ -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() {

View File

@ -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;
}

View 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

View File

@ -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

View File

@ -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)

View File

@ -283,6 +283,7 @@ protected:
nsString mDisplayName;
RefPtr<VRDisplayCapabilities> mCapabilities;
RefPtr<VRStageParameters> mStageParameters;
double mDepthNear;
double mDepthFar;

View File

@ -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;

View File

@ -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*

View File

@ -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>>
{

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
};

View File

@ -64,6 +64,8 @@ IPDL_SOURCES = [
'PVsyncBridge.ipdl',
]
LOCAL_INCLUDES += ['/dom/ipc']
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -133,6 +133,11 @@ public:
void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
virtual void Disconnect() override
{
ClientLayer::Disconnect();
}
protected:
ClientLayerManager* ClientManager()
{

View File

@ -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
{

View File

@ -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; }

View File

@ -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
{

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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',

View File

@ -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

View File

@ -2110,6 +2110,9 @@ gfxPlatform::InitAcceleration()
if (XRE_IsParentProcess()) {
gfxVars::SetBrowserTabsRemoteAutostart(BrowserTabsRemoteAutostart());
gfxVars::SetOffscreenFormat(GetOffscreenFormat());
gfxVars::SetRequiresAcceleratedGLContextForCompositorOGL(
RequiresAcceleratedGLContextForCompositorOGL());
}
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();

View File

@ -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);

View File

@ -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

View File

@ -12,9 +12,7 @@
namespace mozilla {
namespace gfx {
class VRDisplayClient;
namespace vr {
class VRLayerChild;
} // namepsace vr
class VRDisplayPresentation final
{

View File

@ -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,6 +75,12 @@ 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) {
@ -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);
}

View File

@ -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 {

View File

@ -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
View 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
View 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 */

View File

@ -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++) {

View File

@ -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
View 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
View File

@ -0,0 +1,2 @@
See https://github.com/ValveSoftware/openvr/

3352
gfx/vr/openvr/openvr.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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.
*

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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>),

View File

@ -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 */

View File

@ -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*) \

View 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;

View File

@ -1,3 +1,5 @@
// |jit-test| error: ReferenceError
load(libdir + "evalInFrame.js");
evalInFrame(1, "a = 43");
let a = 42;

View 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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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),

View File

@ -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;
}

View File

@ -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 {

View File

@ -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';
}

View File

@ -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';
}

View File

@ -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

View File

@ -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;
}

View File

@ -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())) {

View File

@ -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())

View File

@ -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);

View File

@ -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;

View File

@ -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_;

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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:

View File

@ -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)
{

View File

@ -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()) {

View File

@ -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.

View File

@ -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,

View File

@ -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)

View File

@ -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