--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1425,31 +1425,20 @@ void
DocAccessible::CacheChildren()
{
// Search for accessible children starting from the document element since
// some web pages tend to insert elements under it rather than document body.
dom::Element* rootElm = mDocumentNode->GetRootElement();
if (!rootElm)
return;
- // Ignore last HTML:br, copied from HyperTextAccessible.
TreeWalker walker(this, rootElm);
- Accessible* lastChild = nullptr;
- while (Accessible* child = walker.Next()) {
- if (lastChild)
- AppendChild(lastChild);
-
- lastChild = child;
- }
-
- if (lastChild) {
- if (lastChild->IsHTMLBr())
- Document()->UnbindFromDocument(lastChild);
- else
- AppendChild(lastChild);
+ Accessible* child = nullptr;
+ while ((child = walker.Next())) {
+ AppendChild(child);
}
}
////////////////////////////////////////////////////////////////////////////////
// Protected members
void
DocAccessible::NotifyOfLoading(bool aIsReloading)
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1930,41 +1930,16 @@ HyperTextAccessible::RelationByType(Rela
break;
default:
break;
}
return rel;
}
-void
-HyperTextAccessible::CacheChildren()
-{
- // Trailing HTML br element don't play any difference. We don't need to expose
- // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
- // for details).
-
- TreeWalker walker(this, mContent);
- Accessible* child = nullptr;
- Accessible* lastChild = nullptr;
- while ((child = walker.Next())) {
- if (lastChild)
- AppendChild(lastChild);
-
- lastChild = child;
- }
-
- if (lastChild) {
- if (lastChild->IsHTMLBr())
- Document()->UnbindFromDocument(lastChild);
- else
- AppendChild(lastChild);
- }
-}
-
////////////////////////////////////////////////////////////////////////////////
// HyperTextAccessible public static
nsresult
HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentOffset,
uint32_t* aRenderedOffset) const
{
if (!aFrame) {
--- a/accessible/generic/HyperTextAccessible.h
+++ b/accessible/generic/HyperTextAccessible.h
@@ -429,17 +429,16 @@ public:
*/
dom::Selection* DOMSelection() const;
protected:
virtual ~HyperTextAccessible() { }
// Accessible
virtual ENameValueFlag NativeName(nsString& aName) override;
- virtual void CacheChildren() override;
// HyperTextAccessible
/**
* Transform magic offset into text offset.
*/
index_t ConvertMagicOffset(int32_t aOffset) const;
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -121,19 +121,33 @@ function dumpTree(aId, aMsg)
var children = acc.children;
for (var i = 0; i < children.length; i++) {
var child = children.queryElementAt(i, nsIAccessible);
dumpTreeIntl(child, indent + " ");
}
}
+ function dumpDOMTreeIntl(node, indent)
+ {
+ dump(indent + prettyName(node) + "\n");
+
+ var children = node.childNodes;
+ for (var i = 0; i < children.length; i++) {
+ var child = children.item(i);
+ dumpDOMTreeIntl(child, indent + " ");
+ }
+ }
+
dump(aMsg + "\n");
var root = getAccessible(aId);
dumpTreeIntl(root, " ");
+
+ dump("DOM tree:\n");
+ dumpDOMTreeIntl(getNode(aId), " ");
}
/**
* Invokes the given function when document is loaded and focused. Preferable
* to mochitests 'addLoadEvent' function -- additionally ensures state of the
* document accessible is not busy.
*
* @param aFunc the function to invoke
--- a/accessible/tests/mochitest/editabletext/editabletext.js
+++ b/accessible/tests/mochitest/editabletext/editabletext.js
@@ -57,16 +57,17 @@ function editableTextTest(aID)
* setTextContents test.
*/
this.setTextContents = function setTextContents(aValue, aSkipStartOffset)
{
var testID = "setTextContents '" + aValue + "' for " + prettyName(aID);
function setTextContentsInvoke()
{
+ dump(`\nsetTextContents '${aValue}'\n`);
var acc = getAccessible(aID, nsIAccessibleEditableText);
acc.setTextContents(aValue);
}
aSkipStartOffset = aSkipStartOffset || 0;
var insertTripple = aValue ?
[ aSkipStartOffset, aSkipStartOffset + aValue.length, aValue ] : null;
var oldValue = getValue(aID);
@@ -82,16 +83,17 @@ function editableTextTest(aID)
*/
this.insertText = function insertText(aStr, aPos, aResStr, aResPos)
{
var testID = "insertText '" + aStr + "' at " + aPos + " for " +
prettyName(aID);
function insertTextInvoke()
{
+ dump(`\ninsertText '${aStr}' at ${aPos} pos\n`);
var acc = getAccessible(aID, nsIAccessibleEditableText);
acc.insertText(aStr, aPos);
}
var resPos = (aResPos != undefined) ? aResPos : aPos;
this.generateTest(aID, null, [resPos, resPos + aStr.length, aStr],
insertTextInvoke, getValueChecker(aID, aResStr), testID);
}
--- a/dom/animation/CSSPseudoElement.cpp
+++ b/dom/animation/CSSPseudoElement.cpp
@@ -2,16 +2,17 @@
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "mozilla/dom/CSSPseudoElement.h"
#include "mozilla/dom/CSSPseudoElementBinding.h"
#include "mozilla/dom/Element.h"
+#include "mozilla/AnimationComparator.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CSSPseudoElement, mParentElement)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CSSPseudoElement, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CSSPseudoElement, Release)
@@ -46,18 +47,23 @@ JSObject*
CSSPseudoElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return CSSPseudoElementBinding::Wrap(aCx, this, aGivenProto);
}
void
CSSPseudoElement::GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal)
{
- // Bug 1234403: Implement this API.
- NS_NOTREACHED("CSSPseudoElement::GetAnimations() is not implemented yet.");
+ nsIDocument* doc = mParentElement->GetComposedDoc();
+ if (doc) {
+ doc->FlushPendingNotifications(Flush_Style);
+ }
+
+ Element::GetAnimationsUnsorted(mParentElement, mPseudoType, aRetVal);
+ aRetVal.Sort(AnimationPtrComparator<RefPtr<Animation>>());
}
already_AddRefed<Animation>
CSSPseudoElement::Animate(
JSContext* aContext,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError)
--- a/dom/animation/test/css-animations/file_document-get-animations.html
+++ b/dom/animation/test/css-animations/file_document-get-animations.html
@@ -9,20 +9,16 @@
to { top: 100px }
}
@keyframes animBottom {
to { bottom: 100px }
}
@keyframes animRight {
to { right: 100px }
}
-.with-before-animation::before {
- content: " ";
- animation: animLeft 100s;
-}
</style>
<body>
<script>
'use strict';
test(function(t) {
assert_equals(document.getAnimations().length, 0,
'getAnimations returns an empty sequence for a document'
@@ -240,20 +236,41 @@ test(function(t) {
anim.cancel();
anim.play();
assert_equals(document.getAnimations().length, 1,
'CSS animations cancelled and restarted by the API are ' +
'returned');
}, 'CSS Animations cancelled and restarted via the API are returned');
test(function(t) {
- var div = addDiv(t, { class: 'with-before-animation' });
- // FIXME: This should actually return the animation on the pseudo element
- // but we haven't implemented a PseudoElement interface to use as
- // animation's target (bug 1174575) so we simply don't return these animations
- // until we can support them properly.
- assert_equals(document.getAnimations().length, 0,
- 'CSS animations on pseudo elements are not returned');
-}, 'CSS Animations targetting pseudo-elements are not returned');
+ addStyle(t, { '#parent::after': 'animation: animLeft 10s;',
+ '#parent::before': 'animation: animRight 10s;' });
+ // create two divs with these arrangement:
+ // parent
+ // ::before,
+ // ::after
+ // |
+ // child
+ var parent = addDiv(t, { 'id': 'parent' });
+ var child = addDiv(t);
+ parent.appendChild(child);
+ [parent, child].forEach((div) => {
+ div.setAttribute('style', 'animation: animBottom 10s');
+ });
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 4,
+ 'CSS animations on both pseudo-elements and elements ' +
+ 'are returned');
+ assert_equals(anims[0].effect.target, parent,
+ 'The animation targeting the parent element comes first');
+ assert_equals(anims[1].effect.target.type, '::before',
+ 'The animation targeting the ::before element comes second');
+ assert_equals(anims[2].effect.target.type, '::after',
+ 'The animation targeting the ::after element comes third');
+ assert_equals(anims[3].effect.target, child,
+ 'The animation targeting the child element comes last');
+}, 'CSS Animations targetting (pseudo-)elements should have correct order ' +
+ 'after sorting');
done();
</script>
</body>
--- a/dom/animation/test/css-animations/file_effect-target.html
+++ b/dom/animation/test/css-animations/file_effect-target.html
@@ -9,13 +9,46 @@
'use strict';
test(function(t) {
var div = addDiv(t);
div.style.animation = 'anim 100s';
var animation = div.getAnimations()[0];
assert_equals(animation.effect.target, div,
'Animation.target is the animatable div');
-}, 'Returned CSS animations have the correct Animation.target');
+}, 'Returned CSS animations have the correct effect target');
+
+test(function(t) {
+ addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' });
+ var div = addDiv(t, { class: 'after' });
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_equals(anims[0].effect.target, anims[1].effect.target,
+ 'Both animations return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+ addStyle(t, { '.after::after': 'animation: anim 10s;' });
+ var div = addDiv(t, { class: 'after' });
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ var effect = new KeyframeEffectReadOnly(pseudoTarget,
+ { background: ["blue", "red"] },
+ 3000);
+ var newAnim = new Animation(effect, document.timeline);
+ newAnim.play();
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_not_equals(anims[0], newAnim,
+ 'The scriped-generated animation appears last');
+ assert_equals(newAnim.effect.target, pseudoTarget,
+ 'The effect.target of the scripted-generated animation is ' +
+ 'the same as the one from the argument of ' +
+ 'KeyframeEffectReadOnly constructor');
+ assert_equals(anims[0].effect.target, newAnim.effect.target,
+ 'Both animations return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+ 'CSSPseudoElement object as that from the CSS generated animation');
done();
</script>
</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/file_pseudoElement-get-animations.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+@keyframes anim1 { }
+@keyframes anim2 { }
+.before::before {
+ animation: anim1 10s;
+}
+.after-with-mix-anims-trans::after {
+ content: '';
+ animation: anim1 10s, anim2 10s;
+ width: 0px;
+ height: 0px;
+ transition: all 100s;
+}
+.after-change::after {
+ width: 100px;
+ height: 100px;
+}
+</style>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = addDiv(t, { class: 'before' });
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ assert_equals(pseudoTarget.getAnimations().length, 1,
+ 'Expected number of animations are returned');
+ assert_equals(pseudoTarget.getAnimations()[0].animationName, 'anim1',
+ 'CSS animation name matches');
+}, 'getAnimations returns CSSAnimation objects');
+
+test(function(t) {
+ var div = addDiv(t, { class: 'after-with-mix-anims-trans' });
+ // Trigger transitions
+ flushComputedStyle(div);
+ div.classList.add('after-change');
+
+ // Create additional animation on the pseudo-element from script
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ var effect = new KeyframeEffectReadOnly(pseudoTarget,
+ { background: ["blue", "red"] },
+ 3000);
+ var newAnimation = new Animation(effect, document.timeline);
+ newAnimation.id = 'scripted-anim';
+ newAnimation.play();
+
+ // Check order - the script-generated animation should appear later
+ var anims = pseudoTarget.getAnimations();
+ assert_equals(anims.length, 5,
+ 'Got expected number of animations/trnasitions running on ' +
+ '::after pseudo element');
+ assert_equals(anims[0].transitionProperty, 'height',
+ '1st animation is the 1st transition sorted by name');
+ assert_equals(anims[1].transitionProperty, 'width',
+ '2nd animation is the 2nd transition sorted by name ');
+ assert_equals(anims[2].animationName, 'anim1',
+ '3rd animation is the 1st animation in animation-name list');
+ assert_equals(anims[3].animationName, 'anim2',
+ '4rd animation is the 2nd animation in animation-name list');
+ assert_equals(anims[4].id, 'scripted-anim',
+ 'Animation added by script appears last');
+}, 'getAnimations returns css transitions/animations, and script-generated ' +
+ 'animations in the expected order');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/test_pseudoElement-get-animations.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_pseudoElement-get-animations.html");
+ });
+</script>
--- a/dom/animation/test/css-transitions/file_document-get-animations.html
+++ b/dom/animation/test/css-transitions/file_document-get-animations.html
@@ -26,16 +26,59 @@ test(function(t) {
'getAnimations returns two running CSS Transitions');
// Remove both
div.style.transitionProperty = 'none';
assert_equals(document.getAnimations().length, 0,
'getAnimations returns no running CSS Transitions');
}, 'getAnimations for CSS Transitions');
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
+ 'transition: all 100s;',
+ '.init::before': 'content: ""; width: 0px; ' +
+ 'transition: all 10s;',
+ '.change::after': 'width: 100px;',
+ '.change::before': 'width: 100px;' });
+ // create two divs with these arrangement:
+ // parent
+ // ::before,
+ // ::after
+ // |
+ // child
+ var parent = addDiv(t);
+ var child = addDiv(t);
+ parent.appendChild(child);
+
+ parent.style.left = '0px';
+ parent.style.transition = 'left 10s';
+ parent.classList.add('init');
+ child.style.left = '0px';
+ child.style.transition = 'left 10s';
+ flushComputedStyle(parent);
+
+ parent.style.left = '100px';
+ parent.classList.add('change');
+ child.style.left = '100px';
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 4,
+ 'CSS transition on both pseudo-elements and elements ' +
+ 'are returned');
+ assert_equals(anims[0].effect.target, parent,
+ 'The animation targeting the parent element comes first');
+ assert_equals(anims[1].effect.target.type, '::before',
+ 'The animation targeting the ::before element comes second');
+ assert_equals(anims[2].effect.target.type, '::after',
+ 'The animation targeting the ::after element comes third');
+ assert_equals(anims[3].effect.target, child,
+ 'The animation targeting the child element comes last');
+}, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
+ 'after sorting');
+
async_test(function(t) {
var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
flushComputedStyle(div);
div.style.left = '100px';
var animations = div.getAnimations();
assert_equals(animations.length, 1, 'Got transition');
animations[0].finished.then(t.step_func(function() {
--- a/dom/animation/test/css-transitions/file_effect-target.html
+++ b/dom/animation/test/css-transitions/file_effect-target.html
@@ -13,11 +13,54 @@ test(function(t) {
div.style.transition = 'left 100s';
div.style.left = '100px';
var animation = div.getAnimations()[0];
assert_equals(animation.effect.target, div,
'Animation.target is the animatable div');
}, 'Returned CSS transitions have the correct Animation.target');
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; height: 0px; ' +
+ 'transition: all 10s;',
+ '.change::after': 'width: 100px; height: 100px;' });
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got transitions running on ::after pseudo element');
+ assert_equals(anims[0].effect.target, anims[1].effect.target,
+ 'Both transitions return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; transition: all 10s;',
+ '.change::after': 'width: 100px;' });
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ var effect = new KeyframeEffectReadOnly(pseudoTarget,
+ { background: ["blue", "red"] },
+ 3000);
+ var newAnim = new Animation(effect, document.timeline);
+ newAnim.play();
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_not_equals(anims[0], newAnim,
+ 'The scriped-generated animation appears last');
+ assert_equals(newAnim.effect.target, pseudoTarget,
+ 'The effect.target of the scripted-generated animation is ' +
+ 'the same as the one from the argument of ' +
+ 'KeyframeEffectReadOnly constructor');
+ assert_equals(anims[0].effect.target, newAnim.effect.target,
+ 'Both the transition and the scripted-generated animation ' +
+ 'return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+ 'CSSPseudoElement object as that from the CSS generated transition');
+
done();
</script>
</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_pseudoElement-get-animations.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+.init::before {
+ content: '';
+ height: 0px;
+ width: 0px;
+ opacity: 0;
+ transition: all 100s;
+}
+.change::before {
+ height: 100px;
+ width: 100px;
+ opacity: 1;
+}
+</style>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+
+ // Sanity checks
+ assert_equals(document.getAnimations().length, 3,
+ 'Got expected number of animations on document');
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ assert_class_string(pseudoTarget, 'CSSPseudoElement',
+ 'Got pseudo-element target');
+
+ // Check animations returned from the pseudo element are in correct order
+ var anims = pseudoTarget.getAnimations();
+ assert_equals(anims.length, 3,
+ 'Got expected number of animations on pseudo-element');
+ assert_equals(anims[0].transitionProperty, 'height');
+ assert_equals(anims[1].transitionProperty, 'opacity');
+ assert_equals(anims[2].transitionProperty, 'width');
+}, 'getAnimations sorts simultaneous transitions by name');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_pseudoElement-get-animations.html");
+ });
+</script>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -43,16 +43,18 @@ support-files = css-animations/file_cssa
support-files = css-animations/file_document-get-animations.html
[css-animations/test_effect-target.html]
support-files = css-animations/file_effect-target.html
[css-animations/test_element-get-animations.html]
skip-if = buildapp == 'mulet'
support-files = css-animations/file_element-get-animations.html
[css-animations/test_keyframeeffect-getframes.html]
support-files = css-animations/file_keyframeeffect-getframes.html
+[css-animations/test_pseudoElement-get-animations.html]
+support-files = css-animations/file_pseudoElement-get-animations.html
[css-transitions/test_animation-cancel.html]
support-files = css-transitions/file_animation-cancel.html
[css-transitions/test_animation-computed-timing.html]
support-files = css-transitions/file_animation-computed-timing.html
[css-transitions/test_animation-currenttime.html]
support-files = css-transitions/file_animation-currenttime.html
[css-transitions/test_animation-finished.html]
support-files = css-transitions/file_animation-finished.html
@@ -68,16 +70,18 @@ support-files = css-transitions/file_css
support-files = css-transitions/file_document-get-animations.html
[css-transitions/test_effect-target.html]
support-files = css-transitions/file_effect-target.html
[css-transitions/test_element-get-animations.html]
skip-if = buildapp == 'mulet'
support-files = css-transitions/file_element-get-animations.html
[css-transitions/test_keyframeeffect-getframes.html]
support-files = css-transitions/file_keyframeeffect-getframes.html
+[css-transitions/test_pseudoElement-get-animations.html]
+support-files = css-transitions/file_pseudoElement-get-animations.html
[document-timeline/test_document-timeline.html]
support-files = document-timeline/file_document-timeline.html
[document-timeline/test_request_animation_frame.html]
skip-if = buildapp == 'mulet'
[mozilla/test_deferred_start.html]
support-files = mozilla/file_deferred_start.html
skip-if = (toolkit == 'gonk' && debug)
[mozilla/test_hide_and_show.html]
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -25,16 +25,44 @@ function addDiv(t, attrs) {
div.parentNode.removeChild(div);
}
});
}
return div;
}
/**
+ * Appends a style div to the document head.
+ *
+ * @param t The testharness.js Test object. If provided, this will be used
+ * to register a cleanup callback to remove the style element
+ * when the test finishes.
+ *
+ * @param rules A dictionary object with selector names and rules to set on
+ * the style sheet.
+ */
+function addStyle(t, rules) {
+ var extraStyle = document.createElement('style');
+ document.head.appendChild(extraStyle);
+ if (rules) {
+ var sheet = extraStyle.sheet;
+ for (var selector in rules) {
+ sheet.insertRule(selector + '{' + rules[selector] + '}',
+ sheet.cssRules.length);
+ }
+ }
+
+ if (t && typeof t.add_cleanup === 'function') {
+ t.add_cleanup(function() {
+ extraStyle.remove();
+ });
+ }
+}
+
+/**
* Promise wrapper for requestAnimationFrame.
*/
function waitForFrame() {
return new Promise(function(resolve, reject) {
window.requestAnimationFrame(resolve);
});
}
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -63,16 +63,17 @@
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/TextEvents.h"
#include "nsNodeUtils.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "nsDocument.h"
#include "nsAttrValueOrString.h"
#include "nsAttrValueInlines.h"
+#include "nsCSSPseudoElements.h"
#ifdef MOZ_XUL
#include "nsXULElement.h"
#endif /* MOZ_XUL */
#include "nsSVGElement.h"
#include "nsFrameSelection.h"
#ifdef DEBUG
#include "nsRange.h"
#endif
@@ -3356,25 +3357,31 @@ Element::Animate(JSContext* aContext,
void
Element::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
{
nsIDocument* doc = GetComposedDoc();
if (doc) {
doc->FlushPendingNotifications(Flush_Style);
}
- GetAnimationsUnsorted(aAnimations);
+ GetAnimationsUnsorted(this, CSSPseudoElementType::NotPseudo, aAnimations);
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
}
-void
-Element::GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations)
+/* static */ void
+Element::GetAnimationsUnsorted(Element* aElement,
+ CSSPseudoElementType aPseudoType,
+ nsTArray<RefPtr<Animation>>& aAnimations)
{
- EffectSet* effects = EffectSet::GetEffectSet(this,
- CSSPseudoElementType::NotPseudo);
+ MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo ||
+ aPseudoType == CSSPseudoElementType::after ||
+ aPseudoType == CSSPseudoElementType::before,
+ "Unsupported pseudo type");
+
+ EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
if (!effects) {
return;
}
for (KeyframeEffectReadOnly* effect : *effects) {
MOZ_ASSERT(effect && effect->GetAnimation(),
"Only effects associated with an animation should be "
"added to an element's effect set");
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -113,16 +113,17 @@ enum {
};
#undef ELEMENT_FLAG_BIT
// Make sure we have space for our bits
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
namespace mozilla {
+enum class CSSPseudoElementType : uint8_t;
class EventChainPostVisitor;
class EventChainPreVisitor;
class EventChainVisitor;
class EventListenerManager;
class EventStateManager;
namespace dom {
@@ -828,17 +829,19 @@ public:
already_AddRefed<Animation> Animate(
JSContext* aContext,
JS::Handle<JSObject*> aFrames,
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
ErrorResult& aError);
// Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
- void GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations);
+ static void GetAnimationsUnsorted(Element* aElement,
+ CSSPseudoElementType aPseudoType,
+ nsTArray<RefPtr<Animation>>& aAnimations);
NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
void GetOuterHTML(nsAString& aOuterHTML);
void SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError);
void InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
ErrorResult& aError);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -117,25 +117,16 @@
using namespace mozilla;
using namespace mozilla::dom;
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
// NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
// are defined in nsIDOMClassInfo.h.
-#define ARRAY_SCRIPTABLE_FLAGS \
- (DOM_DEFAULT_SCRIPTABLE_FLAGS | \
- nsIXPCScriptable::WANT_GETPROPERTY | \
- nsIXPCScriptable::WANT_ENUMERATE)
-
-#define EVENTTARGET_SCRIPTABLE_FLAGS \
- (DOM_DEFAULT_SCRIPTABLE_FLAGS | \
- nsIXPCScriptable::WANT_ADDPROPERTY)
-
#define DOMCLASSINFO_STANDARD_FLAGS \
(nsIClassInfo::MAIN_THREAD_ONLY | \
nsIClassInfo::DOM_OBJECT | \
nsIClassInfo::SINGLETON_CLASSINFO)
#ifdef DEBUG
#define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -26,16 +26,17 @@
#include "prprf.h"
#include "mozilla/Telemetry.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsILoadContext.h"
#include "nsUnicharUtils.h"
#include "nsContentList.h"
+#include "nsCSSPseudoElements.h"
#include "nsIObserver.h"
#include "nsIBaseWindow.h"
#include "mozilla/css/Loader.h"
#include "mozilla/css/ImageLoader.h"
#include "nsDocShell.h"
#include "nsDocShellLoadTypes.h"
#include "nsIDocShellTreeItem.h"
#include "nsCOMArray.h"
@@ -3135,31 +3136,30 @@ nsDocument::Timeline()
return mDocumentTimeline;
}
void
nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
{
FlushPendingNotifications(Flush_Style);
- // Bug 1174575: Until we implement a suitable PseudoElement interface we
- // don't have anything to return for the |target| attribute of
- // KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
- // Rather than return some half-baked version of these objects (e.g.
- // we a null effect attribute) we simply don't provide access to animations
- // whose effect refers to a pseudo-element until we can support them
- // properly.
for (nsIContent* node = nsINode::GetFirstChild();
node;
node = node->GetNextNode(this)) {
if (!node->IsElement()) {
continue;
}
- node->AsElement()->GetAnimationsUnsorted(aAnimations);
+ Element* element = node->AsElement();
+ Element::GetAnimationsUnsorted(element, CSSPseudoElementType::NotPseudo,
+ aAnimations);
+ Element::GetAnimationsUnsorted(element, CSSPseudoElementType::before,
+ aAnimations);
+ Element::GetAnimationsUnsorted(element, CSSPseudoElementType::after,
+ aAnimations);
}
// Sort animations by priority
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
}
/* Return true if the document is in the focused top-level window, and is an
* ancestor of the focused DOMWindow. */
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -303,16 +303,17 @@ GK_ATOM(digit, "digit")
GK_ATOM(dir, "dir")
GK_ATOM(dirAutoSetBy, "dirAutoSetBy")
GK_ATOM(directionality, "directionality")
GK_ATOM(directory, "directory")
GK_ATOM(disableOutputEscaping, "disable-output-escaping")
GK_ATOM(disabled, "disabled")
GK_ATOM(disablehistory, "disablehistory")
GK_ATOM(display, "display")
+GK_ATOM(displayMode, "display-mode")
GK_ATOM(distinct, "distinct")
GK_ATOM(div, "div")
GK_ATOM(dl, "dl")
GK_ATOM(doctypePublic, "doctype-public")
GK_ATOM(doctypeSystem, "doctype-system")
GK_ATOM(document, "document")
GK_ATOM(download, "download")
GK_ATOM(DOMAttrModified, "DOMAttrModified")
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -365,51 +365,57 @@ Request::Constructor(const GlobalObject&
}
}
requestHeaders->Fill(*headers, aRv);
if (aRv.Failed()) {
return nullptr;
}
- if (aInit.mBody.WasPassed() || temporaryBody) {
+ if ((aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) ||
+ temporaryBody) {
// HEAD and GET are not allowed to have a body.
nsAutoCString method;
request->GetMethod(method);
// method is guaranteed to be uppercase due to step 14.2 above.
if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
aRv.ThrowTypeError<MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD>();
return nullptr;
}
}
if (aInit.mBody.WasPassed()) {
- const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
- nsCOMPtr<nsIInputStream> stream;
- nsAutoCString contentType;
- aRv = ExtractByteStreamFromBody(bodyInit,
- getter_AddRefs(stream), contentType);
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
- }
-
- temporaryBody = stream;
+ const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
+ aInit.mBody.Value();
+ if (!bodyInitNullable.IsNull()) {
+ const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
+ bodyInitNullable.Value();
+ nsCOMPtr<nsIInputStream> stream;
+ nsAutoCString contentType;
+ aRv = ExtractByteStreamFromBody(bodyInit,
+ getter_AddRefs(stream), contentType);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
- if (!contentType.IsVoid() &&
- !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
- requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
- contentType, aRv);
- }
+ temporaryBody = stream;
+
+ if (!contentType.IsVoid() &&
+ !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
+ requestHeaders->Append(NS_LITERAL_CSTRING("Content-Type"),
+ contentType, aRv);
+ }
- if (NS_WARN_IF(aRv.Failed())) {
- return nullptr;
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ request->ClearCreatedByFetchEvent();
+ request->SetBody(temporaryBody);
}
-
- request->ClearCreatedByFetchEvent();
- request->SetBody(temporaryBody);
}
RefPtr<Request> domRequest = new Request(global, request);
domRequest->SetMimeType();
if (aInput.IsRequest()) {
RefPtr<Request> inputReq = &aInput.GetAsRequest();
nsCOMPtr<nsIInputStream> body;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -515,28 +515,31 @@ child:
* content processes always render to a virtual <0, 0> top-left
* point.
*/
async Show(ScreenIntSize size,
ShowInfo info,
TextureFactoryIdentifier textureFactoryIdentifier,
uint64_t layersId,
nullable PRenderFrame renderFrame,
- bool parentIsActive);
+ bool parentIsActive,
+ nsSizeMode sizeMode);
async LoadURL(nsCString uri, BrowserConfiguration config, ShowInfo info);
async OpenURI(URIParams uri, uint32_t flags);
async CacheFileDescriptor(nsString path, FileDescriptor fd);
- async UpdateDimensions(CSSRect rect, CSSSize size, nsSizeMode sizeMode,
+ async UpdateDimensions(CSSRect rect, CSSSize size,
ScreenOrientationInternal orientation,
LayoutDeviceIntPoint chromeDisp) compressall;
+ async SizeModeChanged(nsSizeMode sizeMode);
+
/**
* Sending an activate message moves focus to the child.
*/
async Activate();
async Deactivate();
async ParentActivated(bool aActivated);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1477,17 +1477,17 @@ TabChild::CancelCachedFileDescriptorCall
}
void
TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo)
{
RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier,
- aLayersId, aRenderFrame, mParentIsActive);
+ aLayersId, aRenderFrame, mParentIsActive, nsSizeMode_Normal);
mDidFakeShow = true;
}
void
TabChild::ApplyShowInfo(const ShowInfo& aInfo)
{
if (mDidSetRealShowInfo) {
return;
@@ -1589,20 +1589,22 @@ TabChild::MaybeRequestPreinitCamera()
#endif
bool
TabChild::RecvShow(const ScreenIntSize& aSize,
const ShowInfo& aInfo,
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame,
- const bool& aParentIsActive)
+ const bool& aParentIsActive,
+ const nsSizeMode& aSizeMode)
{
MOZ_ASSERT((!mDidFakeShow && aRenderFrame) || (mDidFakeShow && !aRenderFrame));
+ mPuppetWidget->SetSizeMode(aSizeMode);
if (mDidFakeShow) {
ApplyShowInfo(aInfo);
RecvParentActivated(aParentIsActive);
return true;
}
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
if (!baseWindow) {
@@ -1628,17 +1630,16 @@ TabChild::RecvShow(const ScreenIntSize&
ApplyShowInfo(aInfo);
RecvParentActivated(aParentIsActive);
return res;
}
bool
TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
- const nsSizeMode& sizeMode,
const ScreenOrientationInternal& orientation,
const LayoutDeviceIntPoint& chromeDisp)
{
if (!mRemoteFrame) {
return true;
}
mUnscaledOuterRect = rect;
@@ -1655,25 +1656,40 @@ TabChild::RecvUpdateDimensions(const CSS
// Set the size on the document viewer before we update the widget and
// trigger a reflow. Otherwise the MobileViewportManager reads the stale
// size from the content viewer when it computes a new CSS viewport.
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
true);
- mPuppetWidget->SetSizeMode(sizeMode);
mPuppetWidget->Resize(screenRect.x + chromeDisp.x,
screenRect.y + chromeDisp.y,
screenSize.width, screenSize.height, true);
return true;
}
bool
+TabChild::RecvSizeModeChanged(const nsSizeMode& aSizeMode)
+{
+ mPuppetWidget->SetSizeMode(aSizeMode);
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ nsCOMPtr<nsIPresShell> presShell = document->GetShell();
+ if (presShell) {
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (presContext) {
+ presContext->MediaFeatureValuesChangedAllDocuments(eRestyle_Subtree,
+ NS_STYLE_HINT_REFLOW);
+ }
+ }
+ return true;
+}
+
+bool
TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics)
{
return TabChildBase::UpdateFrameHandler(aFrameMetrics);
}
bool
TabChild::RecvSuppressDisplayport(const bool& aEnabled)
{
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -321,24 +321,26 @@ public:
override;
virtual bool
RecvShow(const ScreenIntSize& aSize,
const ShowInfo& aInfo,
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame,
- const bool& aParentIsActive) override;
+ const bool& aParentIsActive,
+ const nsSizeMode& aSizeMode) override;
virtual bool
RecvUpdateDimensions(const CSSRect& aRect,
const CSSSize& aSize,
- const nsSizeMode& aSizeMode,
const ScreenOrientationInternal& aOrientation,
const LayoutDeviceIntPoint& aChromeDisp) override;
+ virtual bool
+ RecvSizeModeChanged(const nsSizeMode& aSizeMode) override;
virtual bool RecvActivate() override;
virtual bool RecvDeactivate() override;
virtual bool RecvMouseEvent(const nsString& aType,
const float& aX,
const float& aY,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -274,16 +274,17 @@ TabParent::TabParent(nsIContentParent* a
: TabContext(aContext)
, mFrameElement(nullptr)
, mRect(0, 0, 0, 0)
, mDimensions(0, 0)
, mOrientation(0)
, mDPI(0)
, mDefaultScale(0)
, mUpdatedDimensions(false)
+ , mSizeMode(nsSizeMode_Normal)
, mManager(aManager)
, mDocShellIsActive(false)
, mMarkedDestroying(false)
, mIsDestroyed(false)
, mIsDetached(true)
, mAppPackageFileDescriptorSent(false)
, mSendOfflineStatus(true)
, mChromeFlags(aChromeFlags)
@@ -889,29 +890,34 @@ TabParent::Show(const ScreenIntSize& siz
&layersId,
&success);
MOZ_ASSERT(success);
AddTabParentToTable(layersId, this);
Unused << SendPRenderFrameConstructor(renderFrame);
}
}
+ nsCOMPtr<nsISupports> container = mFrameElement->OwnerDoc()->GetContainer();
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
+ nsCOMPtr<nsIWidget> mainWidget;
+ baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+ mSizeMode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
+
Unused << SendShow(size, GetShowInfo(), textureFactoryIdentifier,
- layersId, renderFrame, aParentIsActive);
+ layersId, renderFrame, aParentIsActive, mSizeMode);
}
bool
TabParent::RecvSetDimensions(const uint32_t& aFlags,
const int32_t& aX, const int32_t& aY,
const int32_t& aCx, const int32_t& aCy)
{
MOZ_ASSERT(!(aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER),
"We should never see DIM_FLAGS_SIZE_INNER here!");
- nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(mFrameElement, true);
nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
NS_ENSURE_TRUE(docShell, true);
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(treeOwner);
NS_ENSURE_TRUE(treeOwnerAsWin, true);
@@ -985,22 +991,30 @@ TabParent::UpdateDimensions(const nsIntR
PixelCastJustification::LayoutDeviceIsScreenForTabDims);
LayoutDeviceIntSize devicePixelSize =
ViewAs<LayoutDevicePixel>(mDimensions,
PixelCastJustification::LayoutDeviceIsScreenForTabDims);
CSSRect unscaledRect = devicePixelRect / widgetScale;
CSSSize unscaledSize = devicePixelSize / widgetScale;
Unused << SendUpdateDimensions(unscaledRect, unscaledSize,
- widget->SizeMode(),
orientation, chromeOffset);
}
}
void
+TabParent::SizeModeChanged(const nsSizeMode& aSizeMode)
+{
+ if (!mIsDestroyed && aSizeMode != mSizeMode) {
+ mSizeMode = aSizeMode;
+ Unused << SendSizeModeChanged(aSizeMode);
+ }
+}
+
+void
TabParent::UIResolutionChanged()
{
if (!mIsDestroyed) {
// TryCacheDPIAndScale()'s cache is keyed off of
// mDPI being greater than 0, so this invalidates it.
mDPI = -1;
TryCacheDPIAndScale();
// If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -332,16 +332,18 @@ public:
// XXX/cjones: it's not clear what we gain by hiding these
// message-sending functions under a layer of indirection and
// eating the return values
void Show(const ScreenIntSize& aSize, bool aParentIsActive);
void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
+ void SizeModeChanged(const nsSizeMode& aSizeMode);
+
void UIResolutionChanged();
void ThemeChanged();
void HandleAccessKey(nsTArray<uint32_t>& aCharCodes,
const bool& aIsTrusted,
const int32_t& aModifierMask);
@@ -595,16 +597,17 @@ protected:
ContentCacheInParent mContentCache;
nsIntRect mRect;
ScreenIntSize mDimensions;
ScreenOrientationInternal mOrientation;
float mDPI;
CSSToLayoutDeviceScale mDefaultScale;
bool mUpdatedDimensions;
+ nsSizeMode mSizeMode;
LayoutDeviceIntPoint mChromeOffset;
private:
void DestroyInternal();
already_AddRefed<nsFrameLoader>
GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -188,38 +188,30 @@ private:
class FrameParser {
public:
// Returns the currently parsed frame. Reset via Reset or EndFrameSession.
const Frame& CurrentFrame() const { return mFrame; }
-#ifdef ENABLE_TESTS
- // Returns the previously parsed frame. Reset via Reset.
- const Frame& PrevFrame() const { return mPrevFrame; }
-#endif
-
// Returns the first parsed frame. Reset via Reset.
const Frame& FirstFrame() const { return mFirstFrame; }
// Resets the parser. Don't use between frames as first frame data is reset.
void Reset() {
EndFrameSession();
mFirstFrame.Reset();
}
// Clear the last parsed frame to allow for next frame parsing, i.e.:
// - sets PrevFrame to CurrentFrame
// - resets the CurrentFrame
// - resets ID3Header if no valid header was parsed yet
void EndFrameSession() {
-#ifdef ENABLE_TESTS
- mPrevFrame = mFrame;
-#endif
mFrame.Reset();
}
// Parses contents of given ByteReader for a valid frame header and returns true
// if one was found. After returning, the variable passed to 'aBytesToSkip' holds
// the amount of bytes to be skipped (if any) in order to jump across a large
// ID3v2 tag spanning multiple buffers.
bool Parse(int64_t aOffset, uint8_t* aStart, uint8_t* aEnd) {
@@ -232,19 +224,16 @@ public:
return found;
}
private:
// We keep the first parsed frame around for static info access, the
// previously parsed frame for debugging and the currently parsed frame.
Frame mFirstFrame;
Frame mFrame;
-#ifdef ENABLE_TESTS
- Frame mPrevFrame;
-#endif
};
// Return the AAC Profile Level Indication based upon sample rate and channels
// Information based upon table 1.10 from ISO/IEC 14496-3:2005(E)
static int8_t
ProfileLevelIndication(const Frame& frame)
{
@@ -431,36 +420,16 @@ ADTSTrackDemuxer::Init()
InitAudioSpecificConfig(mParser->FirstFrame(), mInfo->mCodecSpecificConfig);
ADTSLOG("Init mInfo={mRate=%u mChannels=%u mBitDepth=%u mDuration=%" PRId64 "}",
mInfo->mRate, mInfo->mChannels, mInfo->mBitDepth, mInfo->mDuration);
return mSamplesPerSecond && mChannels;
}
-#ifdef ENABLE_TESTS
-const adts::Frame&
-ADTSTrackDemuxer::LastFrame() const
-{
- return mParser->PrevFrame();
-}
-
-RefPtr<MediaRawData>
-ADTSTrackDemuxer::DemuxSample()
-{
- return GetNextFrame(FindNextFrame());
-}
-
-media::TimeUnit
-ADTSTrackDemuxer::SeekPosition() const
-{
- return Duration(mFrameIndex);
-}
-#endif
-
UniquePtr<TrackInfo>
ADTSTrackDemuxer::GetInfo() const
{
return mInfo->Clone();
}
RefPtr<ADTSTrackDemuxer::SeekPromise>
ADTSTrackDemuxer::Seek(media::TimeUnit aTime)
--- a/dom/media/ADTSDemuxer.h
+++ b/dom/media/ADTSDemuxer.h
@@ -54,22 +54,16 @@ public:
// Returns the estimated stream duration, or a 0-duration if unknown.
media::TimeUnit Duration() const;
// Returns the estimated duration up to the given frame number,
// or a 0-duration if unknown.
media::TimeUnit Duration(int64_t aNumFrames) const;
-#ifdef ENABLE_TESTS
- const adts::Frame& LastFrame() const;
- RefPtr<MediaRawData> DemuxSample();
- media::TimeUnit SeekPosition() const;
-#endif
-
// MediaTrackDemuxer interface.
UniquePtr<TrackInfo> GetInfo() const override;
RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
void Reset() override;
RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
media::TimeUnit aTimeThreshold) override;
int64_t GetResourceOffset() const override;
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -152,27 +152,25 @@ media::TimeUnit
MP3TrackDemuxer::SeekPosition() const {
TimeUnit pos = Duration(mFrameIndex);
if (Duration() > TimeUnit()) {
pos = std::min(Duration(), pos);
}
return pos;
}
-#ifdef ENABLE_TESTS
const FrameParser::Frame&
MP3TrackDemuxer::LastFrame() const {
return mParser.PrevFrame();
}
RefPtr<MediaRawData>
MP3TrackDemuxer::DemuxSample() {
return GetNextFrame(FindNextFrame());
}
-#endif
const ID3Parser::ID3Header&
MP3TrackDemuxer::ID3Header() const {
return mParser.ID3Header();
}
const FrameParser::VBRHeader&
MP3TrackDemuxer::VBRInfo() const {
@@ -656,33 +654,29 @@ FrameParser::Reset() {
}
void
FrameParser::EndFrameSession() {
if (!mID3Parser.Header().IsValid()) {
// Reset ID3 tags only if we have not parsed a valid ID3 header yet.
mID3Parser.Reset();
}
-#ifdef ENABLE_TESTS
mPrevFrame = mFrame;
-#endif
mFrame.Reset();
}
const FrameParser::Frame&
FrameParser::CurrentFrame() const {
return mFrame;
}
-#ifdef ENABLE_TESTS
const FrameParser::Frame&
FrameParser::PrevFrame() const {
return mPrevFrame;
}
-#endif
const FrameParser::Frame&
FrameParser::FirstFrame() const {
return mFirstFrame;
}
const ID3Parser::ID3Header&
FrameParser::ID3Header() const {
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/MP3Demuxer.h
@@ -292,20 +292,18 @@ public:
};
// Constructor.
FrameParser();
// Returns the currently parsed frame. Reset via Reset or EndFrameSession.
const Frame& CurrentFrame() const;
-#ifdef ENABLE_TESTS
// Returns the previously parsed frame. Reset via Reset.
const Frame& PrevFrame() const;
-#endif
// Returns the first parsed frame. Reset via Reset.
const Frame& FirstFrame() const;
// Returns the parsed ID3 header. Note: check for validity.
const ID3Parser::ID3Header& ID3Header() const;
// Returns the parsed VBR header info. Note: check for validity by type.
@@ -338,19 +336,17 @@ private:
// VBR header parser.
VBRHeader mVBRHeader;
// We keep the first parsed frame around for static info access, the
// previously parsed frame for debugging and the currently parsed frame.
Frame mFirstFrame;
Frame mFrame;
-#ifdef ENABLE_TESTS
Frame mPrevFrame;
-#endif
};
// The MP3 demuxer used to extract MPEG frames and side information out of
// MPEG streams.
class MP3TrackDemuxer : public MediaTrackDemuxer {
public:
// Constructor, expecting a valid media resource.
explicit MP3TrackDemuxer(MediaResource* aSource);
@@ -367,20 +363,18 @@ public:
// Returns the estimated duration up to the given frame number,
// or a 0-duration if unknown.
media::TimeUnit Duration(int64_t aNumFrames) const;
// Returns the estimated current seek position time.
media::TimeUnit SeekPosition() const;
-#ifdef ENABLE_TESTS
const FrameParser::Frame& LastFrame() const;
RefPtr<MediaRawData> DemuxSample();
-#endif
const ID3Parser::ID3Header& ID3Header() const;
const FrameParser::VBRHeader& VBRInfo() const;
// MediaTrackDemuxer interface.
UniquePtr<TrackInfo> GetInfo() const override;
RefPtr<SeekPromise> Seek(media::TimeUnit aTime) override;
RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
--- a/dom/media/wave/WaveDemuxer.cpp
+++ b/dom/media/wave/WaveDemuxer.cpp
@@ -115,17 +115,20 @@ WAVTrackDemuxer::Init()
aChunkSize += aChunkSize % 2;
if (aChunkName == FRMT_CODE) {
if (!FmtChunkParserInit()) {
return false;
}
} else if (aChunkName == LIST_CODE) {
mHeaderParser.Reset();
- uint32_t endOfListChunk = mOffset + aChunkSize;
+ uint64_t endOfListChunk = static_cast<uint64_t>(mOffset) + aChunkSize;
+ if (endOfListChunk > UINT32_MAX) {
+ return false;
+ }
if (!ListChunkParserInit(aChunkSize)) {
mOffset = endOfListChunk;
}
} else if (aChunkName == DATA_CODE) {
mDataLength = aChunkSize;
if (mFirstChunkOffset != mOffset) {
mFirstChunkOffset = mOffset;
}
@@ -229,19 +232,19 @@ WAVTrackDemuxer::ListChunkParserInit(uin
}
MediaByteRange mRange = { mOffset, mOffset + length };
RefPtr<MediaRawData> mChunkData = GetFileHeader(mRange);
if (!mChunkData) {
return false;
}
- const char* mRawData = reinterpret_cast<const char*>(mChunkData->Data());
+ const char* rawData = reinterpret_cast<const char*>(mChunkData->Data());
- nsCString val(mRawData, length);
+ nsCString val(rawData, length);
if (length > 0 && val[length - 1] == '\0') {
val.SetLength(length - 1);
}
if (length % 2) {
mOffset += 1;
length += length % 2;
}
@@ -401,47 +404,48 @@ int64_t
WAVTrackDemuxer::StreamLength() const
{
return mSource.GetLength();
}
TimeUnit
WAVTrackDemuxer::Duration() const
{
- if (!mDataLength) {
+ if (!mDataLength ||!mChannels || !mSampleFormat) {
return TimeUnit();
}
- int64_t numSamples = mDataLength * 8 / mChannels / mSampleFormat;
+ int64_t numSamples =
+ static_cast<int64_t>(mDataLength) * 8 / mChannels / mSampleFormat;
int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
numUSeconds++;
}
return TimeUnit::FromMicroseconds(numUSeconds);
}
TimeUnit
WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const
{
- if (!mSamplesPerSecond) {
+ if (!mSamplesPerSecond || !mSamplesPerChunk) {
return TimeUnit();
}
const double usPerDataChunk = USECS_PER_S *
static_cast<double>(mSamplesPerChunk) /
mSamplesPerSecond;
return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk);
}
TimeUnit
WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const
{
- if (!mSamplesPerSecond) {
+ if (!mSamplesPerSecond || !mChannels || !mSampleFormat) {
return TimeUnit();
}
int64_t numSamples = aNumBytes * 8 / mChannels / mSampleFormat;
int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond;
if (USECS_PER_S * numSamples % mSamplesPerSecond > mSamplesPerSecond / 2) {
@@ -582,16 +586,19 @@ WAVTrackDemuxer::ChunkIndexFromOffset(in
{
int64_t chunkIndex = (aOffset - mFirstChunkOffset) / DATA_CHUNK_SIZE;
return std::max<int64_t>(0, chunkIndex);
}
int64_t
WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const
{
+ if (!mSamplesPerChunk || !mSamplesPerSecond) {
+ return 0;
+ }
int64_t chunkIndex =
(aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1;
return chunkIndex;
}
void
WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
{
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -261,16 +261,17 @@ nsPluginTag::nsPluginTag(const char* aNa
const char* const* aExtensions,
int32_t aVariants,
int64_t aLastModifiedTime,
bool fromExtension,
bool aArgsAreUTF8)
: nsIInternalPluginTag(aName, aDescription, aFileName, aVersion),
mId(sNextId++),
mContentProcessRunningCount(0),
+ mHadLocalInstance(false),
mLibrary(nullptr),
mIsJavaPlugin(false),
mIsFlashPlugin(false),
mSupportsAsyncInit(false),
mFullPath(aFullPath),
mLastModifiedTime(aLastModifiedTime),
mSandboxLevel(0),
mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
--- a/dom/security/nsCSPService.cpp
+++ b/dom/security/nsCSPService.cpp
@@ -200,21 +200,48 @@ CSPService::ShouldProcess(uint32_t
nsIURI *aContentLocation,
nsIURI *aRequestOrigin,
nsISupports *aRequestContext,
const nsACString &aMimeTypeGuess,
nsISupports *aExtra,
nsIPrincipal *aRequestPrincipal,
int16_t *aDecision)
{
- if (!aContentLocation)
+ if (!aContentLocation) {
return NS_ERROR_FAILURE;
+ }
+
+ if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
+ nsAutoCString location;
+ aContentLocation->GetSpec(location);
+ MOZ_LOG(gCspPRLog, LogLevel::Debug,
+ ("CSPService::ShouldProcess called for %s", location.get()));
+ }
- *aDecision = nsIContentPolicy::ACCEPT;
- return NS_OK;
+ // ShouldProcess is only relevant to TYPE_OBJECT, so let's convert the
+ // internal contentPolicyType to the mapping external one.
+ // If it is not TYPE_OBJECT, we can return at this point.
+ // Note that we should still pass the internal contentPolicyType
+ // (aContentType) to ShouldLoad().
+ uint32_t policyType =
+ nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
+
+ if (policyType != nsIContentPolicy::TYPE_OBJECT) {
+ *aDecision = nsIContentPolicy::ACCEPT;
+ return NS_OK;
+ }
+
+ return ShouldLoad(aContentType,
+ aContentLocation,
+ aRequestOrigin,
+ aRequestContext,
+ aMimeTypeGuess,
+ aExtra,
+ aRequestPrincipal,
+ aDecision);
}
/* nsIChannelEventSink implementation */
NS_IMETHODIMP
CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
nsIChannel *newChannel,
uint32_t flags,
nsIAsyncVerifyRedirectCallback *callback)
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_shouldprocess.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Helper for Test Bug 908933</title>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <object type="application/x-java-test" codebase="test1"></object>
+
+ <object classid="java:test2" codebase="./test2"></object>
+
+ <object data="test3" classid="java:test3" codebase="./test3"></object>
+
+ <applet codebase="test4"></applet>
+
+ <embed src="test5.class" codebase="test5" type="application/x-java-test">
+
+ <embed type="application/x-java-test" codebase="test6">
+
+ <embed src="test7.class">
+
+ <embed src="test8.class" codebase="test8">
+
+ </body>
+</html>
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -76,16 +76,17 @@ support-files =
file_bug910139.xsl
file_bug909029_star.html
file_bug909029_star.html^headers^
file_bug909029_none.html
file_bug909029_none.html^headers^
file_policyuri_regression_from_multipolicy.html
file_policyuri_regression_from_multipolicy.html^headers^
file_policyuri_regression_from_multipolicy_policy
+ file_shouldprocess.html
file_nonce_source.html
file_nonce_source.html^headers^
file_bug941404.html
file_bug941404_xhr.html
file_bug941404_xhr.html^headers^
file_hash_source.html
file_dual_header_testserver.sjs
file_hash_source.html^headers^
@@ -217,15 +218,18 @@ skip-if = buildapp == 'b2g' || toolkit =
[test_upgrade_insecure_cors.html]
skip-if = buildapp == 'b2g' || toolkit == 'gonk' || toolkit == 'android'
[test_report_for_import.html]
[test_blocked_uri_in_reports.html]
[test_service_worker.html]
skip-if = buildapp == 'b2g' #no ssl support
[test_child-src_worker.html]
skip-if = buildapp == 'b2g' #investigate in bug 1222904
+[test_shouldprocess.html]
+# Fennec platform does not support Java applet plugin
+skip-if = toolkit == 'android' #investigate in bug 1250814
[test_child-src_worker_data.html]
[test_child-src_worker-redirect.html]
[test_child-src_iframe.html]
[test_meta_element.html]
[test_meta_header_dual.html]
[test_docwrite_meta.html]
[test_multipartchannel.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_shouldprocess.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=908933
+-->
+<head>
+ <title>Test Bug 908933</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load variations of 'objects' and make sure all the
+ * resource loads are correctly blocked by CSP.
+ * For all the testing we use a CSP with "object-src 'none'"
+ * so that all the loads are either blocked by
+ * shouldProcess or shouldLoad.
+ */
+
+const POLICY = "default-src http://mochi.test:8888; object-src 'none'";
+const TESTFILE = "tests/dom/security/test/csp/file_shouldprocess.html";
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ // Note that the files listed below don't actually exist.
+ // Since loading of them should be blocked by shouldProcess, we don't
+ // really need these files.
+
+ // blocked by shouldProcess
+ "http://mochi.test:8888/tests/dom/security/test/csp/test1",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test2",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test3",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test4",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test5",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test6",
+ // blocked by shouldLoad
+ "http://mochi.test:8888/tests/dom/security/test/csp/test7.class",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test8.class",
+];
+
+function checkResults(aURI) {
+ var index = tests.indexOf(aURI);
+ if (index > -1) {
+ tests.splice(index, 1);
+ ok(true, "ShouldLoad or ShouldProcess blocks TYPE_OBJECT with uri: " + aURI + "!");
+ }
+ else {
+ ok(false, "ShouldLoad or ShouldProcess incorreclty blocks TYPE_OBJECT with uri: " + aURI + "!");
+ }
+ if (tests.length == 0) {
+ window.examiner.remove();
+ SimpleTest.finish();
+ }
+}
+
+// used to watch that shouldProcess blocks TYPE_OBJECT
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "csp-on-violate-policy") {
+ var asciiSpec =
+ SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ checkResults(asciiSpec);
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+window.examiner = new examiner();
+
+function loadFrame() {
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape(TESTFILE);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(POLICY);
+
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ document.body.appendChild(iframe);
+}
+
+SpecialPowers.pushPrefEnv(
+ { "set": [['plugin.java.mime', 'application/x-java-test']] },
+ loadFrame);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -14,17 +14,17 @@ typedef unsigned long nsContentPolicyTyp
Exposed=(Window,Worker)]
interface Request {
readonly attribute ByteString method;
readonly attribute USVString url;
[SameObject] readonly attribute Headers headers;
[Func="mozilla::dom::Request::RequestContextEnabled"]
readonly attribute RequestContext context;
- readonly attribute DOMString referrer;
+ readonly attribute USVString referrer;
readonly attribute RequestMode mode;
readonly attribute RequestCredentials credentials;
[Func="mozilla::dom::Request::RequestCacheEnabled"]
readonly attribute RequestCache cache;
readonly attribute RequestRedirect redirect;
[Throws,
NewObject] Request clone();
@@ -33,17 +33,17 @@ interface Request {
[ChromeOnly]
void setContentPolicyType(nsContentPolicyType context);
};
Request implements Body;
dictionary RequestInit {
ByteString method;
HeadersInit headers;
- BodyInit body;
+ BodyInit? body;
RequestMode mode;
RequestCredentials credentials;
RequestCache cache;
RequestRedirect redirect;
};
// Gecko currently does not ship RequestContext, so please don't use it in IDL
// that is exposed to script.
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1237,17 +1237,17 @@ public:
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
mRegistration->NotifyListenersOnChange();
swm->StoreRegistration(mPrincipal, mRegistration);
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
Done(NS_OK);
// Activate() is invoked out of band of atomic.
- mRegistration->TryToActivate();
+ mRegistration->TryToActivateAsync();
}
private:
const InstallType mType;
};
class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase,
public serviceWorkerScriptCache::CompareCallback
@@ -1849,19 +1849,33 @@ ServiceWorkerManager::AppendPendingOpera
if (!mShuttingDown) {
PendingOperation* opt = mPendingOperations.AppendElement();
opt->mRunnable = aRunnable;
}
}
void
+ServiceWorkerRegistrationInfo::TryToActivateAsync()
+{
+ nsCOMPtr<nsIRunnable> r =
+ NS_NewRunnableMethod(this,
+ &ServiceWorkerRegistrationInfo::TryToActivate);
+ MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+}
+
+/*
+ * TryToActivate should not be called directly, use TryToACtivateAsync instead.
+ */
+void
ServiceWorkerRegistrationInfo::TryToActivate()
{
- if (!IsControllingDocuments() || mWaitingWorker->SkipWaitingFlag()) {
+ if (!IsControllingDocuments() ||
+ // Waiting worker will be removed if the registration is removed
+ (mWaitingWorker && mWaitingWorker->SkipWaitingFlag())) {
Activate();
}
}
void
ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess)
{
mRegistration->FinishActivate(aSuccess);
@@ -3404,17 +3418,17 @@ ServiceWorkerManager::StopControllingADo
} else {
// If the registration has an active worker that is running
// this might be a good time to stop it.
if (aRegistration->mActiveWorker) {
ServiceWorkerPrivate* serviceWorkerPrivate =
aRegistration->mActiveWorker->WorkerPrivate();
serviceWorkerPrivate->NoteStoppedControllingDocuments();
}
- aRegistration->TryToActivate();
+ aRegistration->TryToActivateAsync();
}
}
}
NS_IMETHODIMP
ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
const nsAString& aUrl, nsAString& aScope)
{
@@ -4185,17 +4199,17 @@ ServiceWorkerManager::SetSkipWaitingFlag
if (registration->mInstallingWorker &&
(registration->mInstallingWorker->ID() == aServiceWorkerID)) {
registration->mInstallingWorker->SetSkipWaitingFlag();
} else if (registration->mWaitingWorker &&
(registration->mWaitingWorker->ID() == aServiceWorkerID)) {
registration->mWaitingWorker->SetSkipWaitingFlag();
if (registration->mWaitingWorker->State() == ServiceWorkerState::Installed) {
- registration->TryToActivate();
+ registration->TryToActivateAsync();
}
} else {
NS_WARNING("Failed to set skipWaiting flag, no matching worker.");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -135,16 +135,19 @@ public:
void
Clear();
void
PurgeActiveWorker();
void
+ TryToActivateAsync();
+
+ void
TryToActivate();
void
Activate();
void
FinishActivate(bool aSuccess);
--- a/dom/workers/test/serviceworkers/test_claim_oninstall.html
+++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html
@@ -33,23 +33,28 @@
ok(registration.installing, "Worker should be in installing state");
navigator.serviceWorker.oncontrollerchange = function() {
ok(false, "Claim should not succeed when the worker is not active.");
}
var p = new Promise(function(res, rej) {
registration.installing.onstatechange = function(e) {
+ ok(registration.waiting, "Worker should be in waitinging state");
+
// The worker will become active only if claim will reject inside the
// install handler.
+ registration.waiting.onstatechange = function(e) {
+ ok(registration.active, "Claim should reject if the worker is not active");
+ ok(navigator.serviceWorker.controller === null, "Client is not controlled.");
+ e.target.onstatechange = null;
+ res();
+ }
- ok(registration.active, "Claim should reject if the worker is not active");
- ok(navigator.serviceWorker.controller === null, "Client is not controlled.");
e.target.onstatechange = null;
- res();
}
});
return p;
}
function runTest() {
register()
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -137,17 +137,18 @@ JoinStyleToSkiaJoin(JoinStyle aJoin)
return SkPaint::kDefault_Join;
}
static inline bool
StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions)
{
// Skia renders 0 width strokes with a width of 1 (and in black),
// so we should just skip the draw call entirely.
- if (!aOptions.mLineWidth) {
+ // Skia does not handle non-finite line widths.
+ if (!aOptions.mLineWidth || !IsFinite(aOptions.mLineWidth)) {
return false;
}
aPaint.setStrokeWidth(SkFloatToScalar(aOptions.mLineWidth));
aPaint.setStrokeMiter(SkFloatToScalar(aOptions.mMiterLimit));
aPaint.setStrokeCap(CapStyleToSkiaCap(aOptions.mLineCap));
aPaint.setStrokeJoin(JoinStyleToSkiaJoin(aOptions.mLineJoin));
if (aOptions.mDashLength > 0) {
--- a/gfx/2d/PathSkia.cpp
+++ b/gfx/2d/PathSkia.cpp
@@ -185,17 +185,19 @@ PathSkia::StrokeContainsPoint(const Stro
const Point &aPoint,
const Matrix &aTransform) const
{
if (!mPath.isFinite()) {
return false;
}
SkPaint paint;
- StrokeOptionsToPaint(paint, aStrokeOptions);
+ if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+ return false;
+ }
SkPath strokePath;
paint.getFillPath(mPath, &strokePath);
return SkPathContainsPoint(strokePath, aPoint, aTransform);
}
Rect
@@ -213,18 +215,20 @@ Rect
PathSkia::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
const Matrix &aTransform) const
{
if (!mPath.isFinite()) {
return Rect();
}
SkPaint paint;
- StrokeOptionsToPaint(paint, aStrokeOptions);
-
+ if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
+ return Rect();
+ }
+
SkPath result;
paint.getFillPath(mPath, &result);
Rect bounds = SkRectToRect(result.getBounds());
return aTransform.TransformBounds(bounds);
}
void
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -1,20 +1,25 @@
/* -*- 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 "BufferTexture.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/fallible.h"
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
namespace mozilla {
namespace layers {
class MemoryTextureData : public BufferTextureData
{
public:
static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
@@ -78,26 +83,46 @@ public:
virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
protected:
mozilla::ipc::Shmem mShmem;
};
+static bool UsingX11Compositor()
+{
+#ifdef MOZ_WIDGET_GTK
+ return gfxPlatformGtk::GetPlatform()->UseXRender();
+#endif
+ return false;
+}
+
+static bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+ LayersBackend aLayersBackend)
+{
+ return aLayersBackend != LayersBackend::LAYERS_BASIC
+ || UsingX11Compositor()
+ || aFormat == gfx::SurfaceFormat::UNKNOWN
+ || aFormat == gfx::SurfaceFormat::YUV
+ || aFormat == gfx::SurfaceFormat::NV12;
+}
+
BufferTextureData*
BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ISurfaceAllocator* aAllocator)
{
if (!aAllocator || aAllocator->IsSameProcess()) {
- return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+ return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+ aAllocFlags, aAllocator);
} else {
- return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+ return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+ aAllocFlags, aAllocator);
}
}
BufferTextureData*
BufferTextureData::CreateInternal(ISurfaceAllocator* aAllocator,
const BufferDescriptor& aDesc,
gfx::BackendType aMoz2DBackend,
int32_t aBufferSize,
@@ -174,16 +199,26 @@ BufferTextureData::GetSize() const
}
gfx::SurfaceFormat
BufferTextureData::GetFormat() const
{
return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
}
+
+bool
+BufferTextureData::HasIntermediateBuffer() const
+{
+ if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
+ return true;
+ }
+ return mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
+}
+
bool
BufferTextureData::SupportsMoz2D() const
{
switch (GetFormat()) {
case gfx::SurfaceFormat::YUV:
case gfx::SurfaceFormat::NV12:
case gfx::SurfaceFormat::UNKNOWN:
return false;
@@ -380,17 +415,17 @@ static bool InitBuffer(uint8_t* buf, siz
return true;
}
MemoryTextureData*
MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
- ISurfaceAllocator*)
+ ISurfaceAllocator* aAllocator)
{
// Should have used CreateForYCbCr.
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
if (aSize.width <= 0 || aSize.height <= 0) {
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
return nullptr;
}
@@ -400,19 +435,24 @@ MemoryTextureData::Create(gfx::IntSize a
return nullptr;
}
uint8_t* buf = new (fallible) uint8_t[bufSize];
if (!InitBuffer(buf, bufSize, aAllocFlags)) {
return nullptr;
}
+ auto fwd = aAllocator ? aAllocator->AsCompositableForwarder() : nullptr;
+ bool hasIntermediateBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+ fwd->GetCompositorBackendType())
+ : true;
+
GfxMemoryImageReporter::DidAlloc(buf);
- BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
}
void
MemoryTextureData::Deallocate(ISurfaceAllocator*)
{
MOZ_ASSERT(mBuffer);
@@ -472,17 +512,22 @@ ShmemTextureData::Create(gfx::IntSize aS
return nullptr;
}
uint8_t* buf = shm.get<uint8_t>();
if (!InitBuffer(buf, bufSize, aAllocFlags)) {
return nullptr;
}
- BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+ auto fwd = aAllocator->AsCompositableForwarder();
+ bool hasIntermediateBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+ fwd->GetCompositorBackendType())
+ : true;
+
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
return nullptr;
}
TextureData*
ShmemTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -50,17 +50,17 @@ public:
virtual bool CanExposeMappedData() const override { return true; }
virtual bool BorrowMappedData(MappedTextureData& aMap) override;
virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
virtual bool SupportsMoz2D() const override;
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasIntermediateBuffer() const override;
// use TextureClient's default implementation
virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
// Don't use this.
void SetDesciptor(const BufferDescriptor& aDesc);
protected:
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -107,16 +107,17 @@
*/
class nsIWidget;
namespace mozilla {
namespace gfx {
class Matrix;
class DrawTarget;
+class DataSourceSurface;
} // namespace gfx
namespace layers {
struct Effect;
struct EffectChain;
class Image;
class ImageHostOverlay;
@@ -187,16 +188,20 @@ public:
: mCompositorID(0)
, mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
, mParent(aParent)
, mScreenRotation(ROTATION_0)
{
}
virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
+
virtual bool Initialize() = 0;
virtual void Destroy() = 0;
/**
* Return true if the effect type is supported.
*
* By default Compositor implementations should support all effects but in
* some rare cases it is not possible to support an effect efficiently.
--- a/gfx/layers/TextureDIB.h
+++ b/gfx/layers/TextureDIB.h
@@ -25,17 +25,17 @@ public:
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
virtual bool SupportsMoz2D() const override { return true; }
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasIntermediateBuffer() const override { return true; }
static
DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
ISurfaceAllocator* aAllocator);
protected:
DIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfxWindowsSurface* aSurface)
@@ -75,17 +75,17 @@ public:
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual bool Lock() override;
virtual void Unlock() override;
- virtual bool HasInternalBuffer() const { return true; }
+ virtual bool HasIntermediateBuffer() const { return true; }
virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
protected:
RefPtr<DataTextureSource> mTextureSource;
RefPtr<Compositor> mCompositor;
gfx::SurfaceFormat mFormat;
gfx::IntSize mSize;
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -31,46 +31,63 @@ using namespace mozilla::gfx;
namespace layers {
class DataTextureSourceBasic : public DataTextureSource
, public TextureSourceBasic
{
public:
virtual const char* Name() const override { return "DataTextureSourceBasic"; }
+ explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+ : mSurface(aSurface)
+ , mWrappingExistingData(!!aSurface)
+ {}
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ // If the texture wraps someone else's memory we'd rather not use it as
+ // a DataTextureSource per say (that is call Update on it).
+ return mWrappingExistingData ? nullptr : this;
+ }
+
virtual TextureSourceBasic* AsSourceBasic() override { return this; }
virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
SurfaceFormat GetFormat() const override
{
- return mSurface->GetFormat();
+ return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
}
virtual IntSize GetSize() const override
{
- return mSurface->GetSize();
+ return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
}
virtual bool Update(gfx::DataSourceSurface* aSurface,
nsIntRegion* aDestRegion = nullptr,
gfx::IntPoint* aSrcOffset = nullptr) override
{
+ MOZ_ASSERT(!mWrappingExistingData);
+ if (mWrappingExistingData) {
+ return false;
+ }
mSurface = aSurface;
return true;
}
virtual void DeallocateDeviceData() override
{
mSurface = nullptr;
SetUpdateSerial(0);
}
public:
RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mWrappingExistingData;
};
BasicCompositor::BasicCompositor(nsIWidget *aWidget)
: mWidget(aWidget)
, mDidExternalComposition(false)
{
MOZ_COUNT_CTOR(BasicCompositor);
@@ -177,17 +194,24 @@ BasicCompositor::CreateRenderTargetForWi
}
return rt.forget();
}
already_AddRefed<DataTextureSource>
BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
{
- RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(nullptr);
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
return result.forget();
}
bool
BasicCompositor::SupportsEffect(EffectTypes aEffect)
{
return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
}
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -65,16 +65,19 @@ public:
virtual already_AddRefed<CompositingRenderTarget>
CreateRenderTargetForWindow(const gfx::IntRect& aRect,
SurfaceInitMode aInit,
BufferMode aBufferMode);
virtual already_AddRefed<DataTextureSource>
CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
virtual bool SupportsEffect(EffectTypes aEffect) override;
virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
{
mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
mRenderTarget->BindRenderTarget();
}
virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -28,17 +28,17 @@ public:
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
virtual bool SupportsMoz2D() const override { return true; }
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual void Deallocate(ISurfaceAllocator*) override;
virtual TextureData*
CreateSimilar(ISurfaceAllocator* aAllocator,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -47,15 +47,18 @@ class X11BasicCompositor : public BasicC
{
public:
explicit X11BasicCompositor(nsIWidget *aWidget) : BasicCompositor(aWidget) {}
virtual already_AddRefed<DataTextureSource>
CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
virtual void EndFrame() override;
};
} // namespace layers
} // namespace mozilla
#endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -285,29 +285,16 @@ CompositableClient::DumpTextureClient(st
aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
} else {
aStream << gfxUtils::GetAsDataURI(dSurf).get();
}
}
AutoRemoveTexture::~AutoRemoveTexture()
{
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
if (mCompositable && mTexture && mCompositable->IsConnected()) {
- // remove old buffer from CompositableHost
- RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
- RefPtr<AsyncTransactionTracker> tracker =
- new RemoveTextureFromCompositableTracker(waiter);
- // Hold TextureClient until transaction complete.
- tracker->SetTextureClient(mTexture);
- mTexture->SetRemoveFromCompositableWaiter(waiter);
- // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
- mCompositable->GetForwarder()->RemoveTextureFromCompositableAsync(tracker, mCompositable, mTexture);
- }
-#else
- if (mCompositable && mTexture) {
+ mTexture->RemoveFromCompositable(mCompositable);
mCompositable->RemoveTexture(mTexture);
}
-#endif
}
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -443,41 +443,23 @@ ContentClientDoubleBuffered::DestroyFron
void
ContentClientDoubleBuffered::Updated(const nsIntRegion& aRegionToDraw,
const nsIntRegion& aVisibleRegion,
bool aDidSelfCopy)
{
ContentClientRemoteBuffer::Updated(aRegionToDraw, aVisibleRegion, aDidSelfCopy);
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
if (mFrontClient) {
- // remove old buffer from CompositableHost
- RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
- RefPtr<AsyncTransactionTracker> tracker =
- new RemoveTextureFromCompositableTracker(waiter);
- // Hold TextureClient until transaction complete.
- tracker->SetTextureClient(mFrontClient);
- mFrontClient->SetRemoveFromCompositableWaiter(waiter);
- // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
- GetForwarder()->RemoveTextureFromCompositableAsync(tracker, this, mFrontClient);
+ mFrontClient->RemoveFromCompositable(this);
}
if (mFrontClientOnWhite) {
- // remove old buffer from CompositableHost
- RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
- RefPtr<AsyncTransactionTracker> tracker =
- new RemoveTextureFromCompositableTracker(waiter);
- // Hold TextureClient until transaction complete.
- tracker->SetTextureClient(mFrontClientOnWhite);
- mFrontClientOnWhite->SetRemoveFromCompositableWaiter(waiter);
- // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
- GetForwarder()->RemoveTextureFromCompositableAsync(tracker, this, mFrontClientOnWhite);
+ mFrontClientOnWhite->RemoveFromCompositable(this);
}
-#endif
}
void
ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
{
mFrontUpdatedRegion = aFrontUpdatedRegion;
RefPtr<TextureClient> oldBack = mTextureClient;
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -198,17 +198,17 @@ ClientSingleTiledLayerBuffer::PaintThebe
// The new buffer is now validated, remove the dirty region from it.
mTile.mInvalidBack.SubOut(tileDirtyRegion);
dt = nullptr;
mTile.Flip();
UnlockTile(mTile);
- if (backBuffer->HasInternalBuffer()) {
+ if (backBuffer->HasIntermediateBuffer()) {
// If our new buffer has an internal buffer, we don't want to keep another
// TextureClient around unnecessarily, so discard the back-buffer.
mTile.DiscardBackBuffer();
}
mValidRegion = aNewValidRegion;
mLastPaintSurfaceMode = mode;
mLastPaintContentType = content;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -422,20 +422,20 @@ TextureClient::Unlock()
}
mData->Unlock();
mIsLocked = false;
mOpenMode = OpenMode::OPEN_NONE;
}
bool
-TextureClient::HasInternalBuffer() const
+TextureClient::HasIntermediateBuffer() const
{
MOZ_ASSERT(IsValid());
- return mData->HasInternalBuffer();
+ return mData->HasIntermediateBuffer();
}
gfx::IntSize
TextureClient::GetSize() const
{
MOZ_ASSERT(IsValid());
return mData->GetSize();
}
@@ -947,16 +947,40 @@ bool TextureClient::CopyToTextureClient(
RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
destinationTarget->CopySurface(source,
aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
aPoint ? *aPoint : gfx::IntPoint(0, 0));
return true;
}
void
+TextureClient::RemoveFromCompositable(CompositableClient* aCompositable,
+ AsyncTransactionWaiter* aWaiter)
+{
+ MOZ_ASSERT(aCompositable);
+
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
+ if (mActor && aCompositable->GetIPDLActor()
+ && mData->AsGrallocTextureData()) {
+ // remove old buffer from CompositableHost
+ RefPtr<AsyncTransactionWaiter> waiter = aWaiter ? aWaiter
+ : new AsyncTransactionWaiter();
+ RefPtr<AsyncTransactionTracker> tracker =
+ new RemoveTextureFromCompositableTracker(waiter);
+ // Hold TextureClient until transaction complete.
+ tracker->SetTextureClient(this);
+ mRemoveFromCompositableWaiter = waiter;
+ // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
+ mActor->GetForwarder()->RemoveTextureFromCompositableAsync(tracker, aCompositable, this);
+ }
+#endif
+
+}
+
+void
TextureClient::SetRemoveFromCompositableWaiter(AsyncTransactionWaiter* aWaiter) {
mRemoveFromCompositableWaiter = aWaiter;
}
void
TextureClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
{
aStream << aPrefix;
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -42,16 +42,17 @@ namespace mozilla {
namespace gl {
class SharedSurface_Gralloc;
}
namespace layers {
class AsyncTransactionWaiter;
class CompositableForwarder;
+class GrallocTextureData;
class ISurfaceAllocator;
class CompositableClient;
struct PlanarYCbCrData;
class Image;
class PTextureChild;
class TextureChild;
class TextureData;
struct RawTextureBuffer;
@@ -187,17 +188,17 @@ public:
virtual bool Lock(OpenMode aMode, FenceHandle* aFence) = 0;
virtual void Unlock() = 0;
virtual bool SupportsMoz2D() const { return false; }
virtual bool CanExposeMappedData() const { return false; }
- virtual bool HasInternalBuffer() const = 0;
+ virtual bool HasIntermediateBuffer() const = 0;
virtual bool HasSynchronization() const { return false; }
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
virtual bool BorrowMappedData(MappedTextureData&) { return false; }
virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
@@ -226,16 +227,18 @@ public:
virtual TextureFlags GetTextureFlags() const { return TextureFlags::NO_FLAGS; }
#ifdef XP_WIN
virtual D3D11TextureData* AsD3D11TextureData() {
return nullptr;
}
#endif
+
+ virtual GrallocTextureData* AsGrallocTextureData() { return nullptr; }
};
/**
* TextureClient is a thin abstraction over texture data that need to be shared
* between the content process and the compositor process. It is the
* content-side half of a TextureClient/TextureHost pair. A corresponding
* TextureHost lives on the compositor-side.
*
@@ -405,17 +408,17 @@ public:
*/
bool HasSynchronization() const { return false; }
/**
* Indicates whether the TextureClient implementation is backed by an
* in-memory buffer. The consequence of this is that locking the
* TextureClient does not contend with locking the texture on the host side.
*/
- bool HasInternalBuffer() const;
+ bool HasIntermediateBuffer() const;
/**
* Allocate and deallocate a TextureChild actor.
*
* TextureChild is an implementation detail of TextureClient that is not
* exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
* are for use with the managing IPDL protocols only (so that they can
* implement AllocPextureChild and DeallocPTextureChild).
@@ -572,16 +575,19 @@ public:
ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
/// If you add new code that uses this method, you are probably doing something wrong.
TextureData* GetInternalData() { return mData; }
const TextureData* GetInternalData() const { return mData; }
+ virtual void RemoveFromCompositable(CompositableClient* aCompositable,
+ AsyncTransactionWaiter* aWaiter = nullptr);
+
private:
static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
/**
* Called once, during the destruction of the Texture, on the thread in which
* texture's reference count reaches 0 (could be any thread).
*
* Here goes the shut-down code that uses virtual methods.
--- a/gfx/layers/client/TextureClientSharedSurface.h
+++ b/gfx/layers/client/TextureClientSharedSurface.h
@@ -39,17 +39,17 @@ protected:
public:
~SharedSurfaceTextureData();
virtual bool Lock(OpenMode, FenceHandle*) override { return false; }
virtual void Unlock() override {}
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual gfx::SurfaceFormat GetFormat() const override {
return gfx::SurfaceFormat::UNKNOWN;
}
virtual gfx::IntSize GetSize() const override;
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -564,32 +564,25 @@ TileClient::Dump(std::stringstream& aStr
aStream << " fbow=" << mFrontBufferOnWhite.get();
}
aStream << ")";
}
void
TileClient::Flip()
{
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
- if (mFrontBuffer && mFrontBuffer->GetIPDLActor() &&
- mCompositableClient && mCompositableClient->GetIPDLActor()) {
- // remove old buffer from CompositableHost
- RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
- RefPtr<AsyncTransactionTracker> tracker =
- new RemoveTextureFromCompositableTracker(waiter);
- // Hold TextureClient until transaction complete.
- tracker->SetTextureClient(mFrontBuffer);
- mFrontBuffer->SetRemoveFromCompositableWaiter(waiter);
- // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
- mManager->AsShadowForwarder()->RemoveTextureFromCompositableAsync(tracker,
- mCompositableClient,
- mFrontBuffer);
+ if (mCompositableClient) {
+ if (mFrontBuffer) {
+ mFrontBuffer->RemoveFromCompositable(mCompositableClient);
+ }
+ if (mFrontBufferOnWhite) {
+ mFrontBufferOnWhite->RemoveFromCompositable(mCompositableClient);
+ }
}
-#endif
+
RefPtr<TextureClient> frontBuffer = mFrontBuffer;
RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
mFrontBuffer = mBackBuffer;
mFrontBufferOnWhite = mBackBufferOnWhite;
mBackBuffer.Set(this, frontBuffer);
mBackBufferOnWhite = frontBufferOnWhite;
RefPtr<gfxSharedReadLock> frontLock = mFrontLock;
mFrontLock = mBackLock;
@@ -662,35 +655,24 @@ TileClient::ValidateBackBufferFromFront(
}
}
void
TileClient::DiscardFrontBuffer()
{
if (mFrontBuffer) {
MOZ_ASSERT(mFrontLock);
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
- MOZ_ASSERT(!mFrontBufferOnWhite);
- if (mFrontBuffer->GetIPDLActor() &&
- mCompositableClient && mCompositableClient->GetIPDLActor()) {
- // remove old buffer from CompositableHost
- RefPtr<AsyncTransactionWaiter> waiter = new AsyncTransactionWaiter();
- RefPtr<AsyncTransactionTracker> tracker =
- new RemoveTextureFromCompositableTracker(waiter);
- // Hold TextureClient until transaction complete.
- tracker->SetTextureClient(mFrontBuffer);
- mFrontBuffer->SetRemoveFromCompositableWaiter(waiter);
- // RemoveTextureFromCompositableAsync() expects CompositorChild's presence.
- mManager->AsShadowForwarder()->RemoveTextureFromCompositableAsync(tracker,
- mCompositableClient,
- mFrontBuffer);
+
+ if (mCompositableClient) {
+ mFrontBuffer->RemoveFromCompositable(mCompositableClient);
}
-#endif
+
mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
if (mFrontBufferOnWhite) {
+ mFrontBufferOnWhite->RemoveFromCompositable(mCompositableClient);
mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
}
mFrontLock->ReadUnlock();
if (mFrontBuffer->IsLocked()) {
mFrontBuffer->Unlock();
}
if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
mFrontBufferOnWhite->Unlock();
@@ -738,17 +720,17 @@ TileClient::GetBackBuffer(const nsIntReg
gfxContentType aContent,
SurfaceMode aMode,
nsIntRegion& aAddPaintedRegion,
RefPtr<TextureClient>* aBackBufferOnWhite)
{
// Try to re-use the front-buffer if possible
bool createdTextureClient = false;
if (mFrontBuffer &&
- mFrontBuffer->HasInternalBuffer() &&
+ mFrontBuffer->HasIntermediateBuffer() &&
mFrontLock->GetReadCount() == 1 &&
!(aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA && !mFrontBufferOnWhite)) {
// If we had a backbuffer we no longer care about it since we'll
// re-use the front buffer.
DiscardBackBuffer();
Flip();
} else {
if (!mBackBuffer ||
@@ -1375,17 +1357,17 @@ ClientMultiTiledLayerBuffer::ValidateTil
backBuffer->SetWaste(tileRegion.Area() * mResolution * mResolution);
aTile.Flip();
// Note, we don't call UpdatedTexture. The Updated function is called manually
// by the TiledContentHost before composition.
- if (backBuffer->HasInternalBuffer()) {
+ if (backBuffer->HasIntermediateBuffer()) {
// If our new buffer has an internal buffer, we don't want to keep another
// TextureClient around unnecessarily, so discard the back-buffer.
aTile.DiscardBackBuffer();
}
return true;
}
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -233,16 +233,21 @@ public:
virtual void Unlock() { }
virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::Filter& aFilter) {
return nullptr;
}
virtual int32_t GetLastInputFrameID() const { return -1; }
+ /// Called when shutting down the layer tree.
+ /// This is a good place to clear all potential gpu resources before the widget
+ /// is is destroyed.
+ virtual void CleanupResources() {}
+
protected:
TextureInfo mTextureInfo;
uint64_t mAsyncID;
uint64_t mCompositorID;
RefPtr<Compositor> mCompositor;
Layer* mLayer;
uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
bool mAttached;
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -47,65 +47,46 @@ ImageHost::UseTextureHost(const nsTArray
{
MOZ_ASSERT(!mLocked);
CompositableHost::UseTextureHost(aTextures);
MOZ_ASSERT(aTextures.Length() >= 1);
nsTArray<TimedImage> newImages;
- // Remove all mImages without an mTextureSource to recycle.
- for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
- if (!mImages[i].mTextureSource) {
- mImages.RemoveElementAt(i);
- }
- }
-
- // Create new TimedImage entries and recycle any mTextureSources that match
- // our mFrontBuffers.
for (uint32_t i = 0; i < aTextures.Length(); ++i) {
const TimedTexture& t = aTextures[i];
MOZ_ASSERT(t.mTexture);
if (i + 1 < aTextures.Length() &&
t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
// Ignore frames before a frame that we already composited. We don't
// ever want to display these frames. This could be important if
// the frame producer adjusts timestamps (e.g. to track the audio clock)
// and the new frame times are earlier.
continue;
}
TimedImage& img = *newImages.AppendElement();
- img.mFrontBuffer = t.mTexture;
- for (uint32_t i = 0; i < mImages.Length(); ++i) {
- if (mImages[i].mFrontBuffer == img.mFrontBuffer) {
- img.mTextureSource = mImages[i].mTextureSource;
- mImages.RemoveElementAt(i);
- break;
- }
- }
+ img.mTextureHost = t.mTexture;
img.mTimeStamp = t.mTimeStamp;
img.mPictureRect = t.mPictureRect;
img.mFrameID = t.mFrameID;
img.mProducerID = t.mProducerID;
img.mInputFrameID = t.mInputFrameID;
+ img.mTextureHost->SetCropRect(img.mPictureRect);
+ img.mTextureHost->Updated();
}
- // Recycle any leftover mTextureSources and call PrepareTextureSource on all
- // images.
- for (auto& img : newImages) {
- if (!img.mTextureSource && !mImages.IsEmpty()) {
- img.mTextureSource = mImages.LastElement().mTextureSource;
- mImages.RemoveElementAt(mImages.Length() - 1);
- }
- // SetCropRect() affects only on a specific platform.
- // If it is not implemented, it does nothing.
- img.mFrontBuffer->SetCropRect(img.mPictureRect);
- img.mFrontBuffer->Updated();
- img.mFrontBuffer->PrepareTextureSource(img.mTextureSource);
+
+ mImages.SwapElements(newImages);
+ newImages.Clear();
+
+ // If we only have one image we can upload it right away, otherwise we'll upload
+ // on-demand during composition after we have picked the proper timestamp.
+ if (mImages.Length() == 1) {
+ SetCurrentTextureHost(mImages[0].mTextureHost);
}
- mImages.SwapElements(newImages);
// Video producers generally send replacement images with the same frameID but
// slightly different timestamps in order to sync with the audio clock. This
// means that any CompositeUntil() call we made in Composite() may no longer
// guarantee that we'll composite until the next frame is ready. Fix that here.
if (GetCompositor() && mLastFrameID >= 0) {
for (size_t i = 0; i < mImages.Length(); ++i) {
bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
@@ -115,24 +96,66 @@ ImageHost::UseTextureHost(const nsTArray
TimeDuration::FromMilliseconds(BIAS_TIME_MS));
break;
}
}
}
}
void
+ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
+{
+ if (aTexture == mCurrentTextureHost.get()) {
+ return;
+ }
+
+ bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
+ && mCurrentTextureHost->HasIntermediateBuffer();
+
+ if (swapTextureSources) {
+ auto dataSource = mCurrentTextureSource->AsDataTextureSource();
+ if (dataSource) {
+ // The current textureHost has an internal buffer in the form of the
+ // DataTextureSource. Removing the ownership of the texture source
+ // will enable the next texture host we bind to the texture source to
+ // acquire it instead of creating a new one. This is desirable in
+ // ImageHost because the current texture won't be used again with the
+ // same content. It wouldn't be desirable with ContentHost for instance,
+ // because the latter reuses the texture's valid regions.
+ dataSource->SetOwner(nullptr);
+ }
+
+ RefPtr<TextureSource> tmp = mExtraTextureSource;
+ mExtraTextureSource = mCurrentTextureSource.get();
+ mCurrentTextureSource = tmp;
+ } else {
+ mExtraTextureSource = nullptr;
+ }
+
+ mCurrentTextureHost = aTexture;
+ mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
+}
+
+void
+ImageHost::CleanupResources()
+{
+ mExtraTextureSource = nullptr;
+ mCurrentTextureSource = nullptr;
+ mCurrentTextureHost = nullptr;
+}
+
+void
ImageHost::RemoveTextureHost(TextureHost* aTexture)
{
MOZ_ASSERT(!mLocked);
CompositableHost::RemoveTextureHost(aTexture);
for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
- if (mImages[i].mFrontBuffer == aTexture) {
+ if (mImages[i].mTextureHost == aTexture) {
aTexture->UnbindTextureSource();
mImages.RemoveElementAt(i);
}
}
}
void
ImageHost::UseOverlaySource(OverlaySource aOverlay,
@@ -240,33 +263,35 @@ ImageHost::TimedImage* ImageHost::Choose
int index = ChooseImageIndex();
return index >= 0 ? &mImages[index] : nullptr;
}
TextureHost*
ImageHost::GetAsTextureHost(IntRect* aPictureRect)
{
TimedImage* img = ChooseImage();
+ if (img) {
+ SetCurrentTextureHost(img->mTextureHost);
+ }
if (aPictureRect && img) {
*aPictureRect = img->mPictureRect;
}
- return img ? img->mFrontBuffer.get() : nullptr;
+ return img ? img->mTextureHost.get() : nullptr;
}
void ImageHost::Attach(Layer* aLayer,
Compositor* aCompositor,
AttachFlags aFlags)
{
CompositableHost::Attach(aLayer, aCompositor, aFlags);
for (auto& img : mImages) {
if (GetCompositor()) {
- img.mFrontBuffer->SetCompositor(GetCompositor());
+ img.mTextureHost->SetCompositor(GetCompositor());
}
- img.mFrontBuffer->Updated();
- img.mFrontBuffer->PrepareTextureSource(img.mTextureSource);
+ img.mTextureHost->Updated();
}
}
void
ImageHost::Composite(LayerComposite* aLayer,
EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
@@ -300,44 +325,45 @@ ImageHost::Composite(LayerComposite* aLa
return;
}
if (uint32_t(imageIndex) + 1 < mImages.Length()) {
GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
}
TimedImage* img = &mImages[imageIndex];
+ SetCurrentTextureHost(img->mTextureHost);
// Make sure the front buffer has a compositor
- img->mFrontBuffer->SetCompositor(GetCompositor());
- if (img->mTextureSource) {
- img->mTextureSource->SetCompositor(GetCompositor());
+ mCurrentTextureHost->SetCompositor(GetCompositor());
+ if (mCurrentTextureSource) {
+ mCurrentTextureSource->SetCompositor(GetCompositor());
}
{
AutoLockCompositableHost autoLock(this);
if (autoLock.Failed()) {
NS_WARNING("failed to lock front buffer");
return;
}
- if (!img->mFrontBuffer->BindTextureSource(img->mTextureSource)) {
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
return;
}
- if (!img->mTextureSource) {
+ if (!mCurrentTextureSource) {
// BindTextureSource above should have returned false!
MOZ_ASSERT(false);
return;
}
bool isAlphaPremultiplied =
- !(img->mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
+ !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
RefPtr<TexturedEffect> effect =
- CreateTexturedEffect(img->mFrontBuffer->GetReadFormat(),
- img->mTextureSource.get(), aFilter, isAlphaPremultiplied,
+ CreateTexturedEffect(mCurrentTextureHost->GetFormat(),
+ mCurrentTextureSource.get(), aFilter, isAlphaPremultiplied,
GetRenderState());
if (!effect) {
return;
}
if (!GetCompositor()->SupportsEffect(effect->mType)) {
return;
}
@@ -358,65 +384,65 @@ ImageHost::Composite(LayerComposite* aLa
img->mFrameID, img->mProducerID));
}
mLastFrameID = img->mFrameID;
mLastProducerID = img->mProducerID;
mLastInputFrameID = img->mInputFrameID;
}
aEffectChain.mPrimaryEffect = effect;
gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height);
- BigImageIterator* it = img->mTextureSource->AsBigImageIterator();
+ BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
if (it) {
// This iteration does not work if we have multiple texture sources here
// (e.g. 3 YCbCr textures). There's nothing preventing the different
// planes from having different resolutions or tile sizes. For example, a
// YCbCr frame could have Cb and Cr planes that are half the resolution of
// the Y plane, in such a way that the Y plane overflows the maximum
// texture size and the Cb and Cr planes do not. Then the Y plane would be
// split into multiple tiles and the Cb and Cr planes would just be one
// tile each.
// To handle the general case correctly, we'd have to create a grid of
// intersected tiles over all planes, and then draw each grid tile using
// the corresponding source tiles from all planes, with appropriate
// per-plane per-tile texture coords.
// DrawQuad currently assumes that all planes use the same texture coords.
- MOZ_ASSERT(it->GetTileCount() == 1 || !img->mTextureSource->GetNextSibling(),
+ MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
"Can't handle multi-plane BigImages");
it->BeginBigImageIteration();
do {
IntRect tileRect = it->GetTileRect();
gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
rect = rect.Intersect(pictureRect);
effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
Float(rect.y - tileRect.y) / tileRect.height,
Float(rect.width) / tileRect.width,
Float(rect.height) / tileRect.height);
- if (img->mFrontBuffer->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
effect->mTextureCoords.y = effect->mTextureCoords.YMost();
effect->mTextureCoords.height = -effect->mTextureCoords.height;
}
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
aOpacity, aTransform);
GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
rect, aClipRect, aTransform, mFlashCounter);
} while (it->NextTile());
it->EndBigImageIteration();
// layer border
GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
aClipRect, aTransform, mFlashCounter);
} else {
- IntSize textureSize = img->mTextureSource->GetSize();
+ IntSize textureSize = mCurrentTextureSource->GetSize();
effect->mTextureCoords = Rect(Float(img->mPictureRect.x) / textureSize.width,
Float(img->mPictureRect.y) / textureSize.height,
Float(img->mPictureRect.width) / textureSize.width,
Float(img->mPictureRect.height) / textureSize.height);
- if (img->mFrontBuffer->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
effect->mTextureCoords.y = effect->mTextureCoords.YMost();
effect->mTextureCoords.height = -effect->mTextureCoords.height;
}
GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
aOpacity, aTransform);
GetCompositor()->DrawDiagnostics(diagnosticFlags,
pictureRect, aClipRect,
@@ -436,17 +462,17 @@ ImageHost::Composite(LayerComposite* aLa
mBias);
}
void
ImageHost::SetCompositor(Compositor* aCompositor)
{
if (mCompositor != aCompositor) {
for (auto& img : mImages) {
- img.mFrontBuffer->SetCompositor(aCompositor);
+ img.mTextureHost->SetCompositor(aCompositor);
}
}
if (mImageHostOverlay) {
mImageHostOverlay->SetCompositor(aCompositor);
}
CompositableHost::SetCompositor(aCompositor);
}
@@ -455,17 +481,17 @@ ImageHost::PrintInfo(std::stringstream&
{
aStream << aPrefix;
aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
nsAutoCString pfx(aPrefix);
pfx += " ";
for (auto& img : mImages) {
aStream << "\n";
- img.mFrontBuffer->PrintInfo(aStream, pfx.get());
+ img.mTextureHost->PrintInfo(aStream, pfx.get());
AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
}
if (mImageHostOverlay) {
mImageHostOverlay->PrintInfo(aStream, aPrefix);
}
}
@@ -473,71 +499,75 @@ void
ImageHost::Dump(std::stringstream& aStream,
const char* aPrefix,
bool aDumpHtml)
{
for (auto& img : mImages) {
aStream << aPrefix;
aStream << (aDumpHtml ? "<ul><li>TextureHost: "
: "TextureHost: ");
- DumpTextureHost(aStream, img.mFrontBuffer);
+ DumpTextureHost(aStream, img.mTextureHost);
aStream << (aDumpHtml ? " </li></ul> " : " ");
}
}
LayerRenderState
ImageHost::GetRenderState()
{
if (mImageHostOverlay) {
return mImageHostOverlay->GetRenderState();
}
TimedImage* img = ChooseImage();
if (img) {
- return img->mFrontBuffer->GetRenderState();
+ SetCurrentTextureHost(img->mTextureHost);
+ return img->mTextureHost->GetRenderState();
}
return LayerRenderState();
}
already_AddRefed<gfx::DataSourceSurface>
ImageHost::GetAsSurface()
{
if (mImageHostOverlay) {
return nullptr;
}
TimedImage* img = ChooseImage();
if (img) {
- return img->mFrontBuffer->GetAsSurface();
+ return img->mTextureHost->GetAsSurface();
}
return nullptr;
}
bool
ImageHost::Lock()
{
MOZ_ASSERT(!mLocked);
TimedImage* img = ChooseImage();
if (!img) {
return false;
}
- if (!img->mFrontBuffer->Lock()) {
+
+ SetCurrentTextureHost(img->mTextureHost);
+
+ if (!mCurrentTextureHost->Lock()) {
return false;
}
mLocked = true;
return true;
}
void
ImageHost::Unlock()
{
MOZ_ASSERT(mLocked);
- TimedImage* img = ChooseImage();
- if (img) {
- img->mFrontBuffer->Unlock();
+
+ if (mCurrentTextureHost) {
+ mCurrentTextureHost->Unlock();
}
mLocked = false;
}
IntSize
ImageHost::GetImageSize() const
{
if (mImageHostOverlay) {
@@ -553,26 +583,27 @@ ImageHost::GetImageSize() const
already_AddRefed<TexturedEffect>
ImageHost::GenEffect(const gfx::Filter& aFilter)
{
TimedImage* img = ChooseImage();
if (!img) {
return nullptr;
}
- if (!img->mFrontBuffer->BindTextureSource(img->mTextureSource)) {
+ SetCurrentTextureHost(img->mTextureHost);
+ if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
return nullptr;
}
bool isAlphaPremultiplied = true;
- if (img->mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
+ if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
isAlphaPremultiplied = false;
}
- return CreateTexturedEffect(img->mFrontBuffer->GetReadFormat(),
- img->mTextureSource,
+ return CreateTexturedEffect(mCurrentTextureHost->GetFormat(),
+ mCurrentTextureSource,
aFilter,
isAlphaPremultiplied,
GetRenderState());
}
void
ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
{
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -81,16 +81,20 @@ public:
virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
virtual bool Lock() override;
virtual void Unlock() override;
virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::Filter& aFilter) override;
+ void SetCurrentTextureHost(TextureHost* aTexture);
+
+ virtual void CleanupResources() override;
+
int32_t GetFrameID()
{
const TimedImage* img = ChooseImage();
return img ? img->mFrameID : -1;
}
int32_t GetProducerID()
{
@@ -108,25 +112,31 @@ public:
// Apply a negative bias to frame times to keep them before the vsync time
BIAS_NEGATIVE,
// Apply a positive bias to frame times to keep them after the vsync time
BIAS_POSITIVE,
};
protected:
struct TimedImage {
- CompositableTextureHostRef mFrontBuffer;
- CompositableTextureSourceRef mTextureSource;
+ RefPtr<TextureHost> mTextureHost;
TimeStamp mTimeStamp;
gfx::IntRect mPictureRect;
int32_t mFrameID;
int32_t mProducerID;
int32_t mInputFrameID;
};
+ CompositableTextureHostRef mCurrentTextureHost;
+ CompositableTextureSourceRef mCurrentTextureSource;
+ // When doing texture uploads it's best to alternate between two (or three)
+ // texture sources so that the texture we upload to isn't being used by
+ // the GPU to composite the previous frame.
+ RefPtr<TextureSource> mExtraTextureSource;
+
/**
* ChooseImage is guaranteed to return the same TimedImage every time it's
* called during the same composition, up to the end of Composite() ---
* it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
* mBias is updated at the end of Composite().
*/
const TimedImage* ChooseImage() const;
TimedImage* ChooseImage();
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -152,16 +152,17 @@ ImageLayerComposite::GetCompositableHost
return nullptr;
}
void
ImageLayerComposite::CleanupResources()
{
if (mImageHost) {
+ mImageHost->CleanupResources();
mImageHost->Detach(this);
}
mImageHost = nullptr;
}
gfx::Filter
ImageLayerComposite::GetEffectFilter()
{
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -390,22 +390,24 @@ BufferTextureHost::BufferTextureHost(con
, mNeedsFullUpdate(false)
{
mDescriptor = aDesc;
switch (mDescriptor.type()) {
case BufferDescriptor::TYCbCrDescriptor: {
const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
mSize = ycbcr.ySize();
mFormat = gfx::SurfaceFormat::YUV;
+ mHasIntermediateBuffer = true;
break;
}
case BufferDescriptor::TRGBDescriptor: {
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
mSize = rgb.size();
mFormat = rgb.format();
+ mHasIntermediateBuffer = rgb.hasIntermediateBuffer();
break;
}
default: MOZ_CRASH();
}
if (aFlags & TextureFlags::COMPONENT_ALPHA) {
// One texture of a component alpha texture pair will start out all white.
// This hack allows us to easily make sure that white will be uploaded.
// See bug 1138934
@@ -440,23 +442,37 @@ BufferTextureHost::SetCompositor(Composi
if (mCompositor == aCompositor) {
return;
}
RefPtr<TextureSource> it = mFirstSource;
while (it) {
it->SetCompositor(aCompositor);
it = it->GetNextSibling();
}
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
mFirstSource = nullptr;
mCompositor = aCompositor;
}
void
BufferTextureHost::DeallocateDeviceData()
{
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ return;
+ }
+
+ if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
+ mFirstSource = nullptr;
+ return;
+ }
+
+ mFirstSource->SetOwner(nullptr);
+
RefPtr<TextureSource> it = mFirstSource;
while (it) {
it->DeallocateDeviceData();
it = it->GetNextSibling();
}
}
bool
@@ -473,16 +489,83 @@ BufferTextureHost::Lock()
void
BufferTextureHost::Unlock()
{
MOZ_ASSERT(mLocked);
mLocked = false;
}
bool
+BufferTextureHost::EnsureWrappingTextureSource()
+{
+ MOZ_ASSERT(!mHasIntermediateBuffer);
+ MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV);
+
+ if (mFirstSource) {
+ return true;
+ }
+
+ if (!mCompositor) {
+ return false;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+
+ if (!surf) {
+ return false;
+ }
+
+ mFirstSource = mCompositor->CreateDataTextureSourceAround(surf);
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ mFirstSource->SetOwner(this);
+
+ return true;
+}
+
+void
+BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ if (!mHasIntermediateBuffer) {
+ EnsureWrappingTextureSource();
+ }
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ // We are already attached to a TextureSource, nothing to do except tell
+ // the compositable to use it.
+ aTexture = mFirstSource.get();
+ return;
+ }
+
+ // We don't own it, apparently.
+ mFirstSource = nullptr;
+
+ DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
+ bool compatibleFormats = texture
+ && (mFormat == texture->GetFormat()
+ || (mFormat == gfx::SurfaceFormat::YUV
+ && mCompositor->SupportsEffect(EffectTypes::YCBCR)
+ && texture->GetNextSibling())
+ || (mFormat == gfx::SurfaceFormat::YUV
+ && !mCompositor->SupportsEffect(EffectTypes::YCBCR)
+ && texture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8));
+
+ bool shouldCreateTexture = !compatibleFormats
+ || texture->NumCompositableRefs() > 1
+ || texture->HasOwner()
+ || texture->GetSize() != mSize;
+
+ if (!shouldCreateTexture) {
+ mFirstSource = texture;
+ mFirstSource->SetOwner(this);
+ }
+}
+
+bool
BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
{
MOZ_ASSERT(mLocked);
MOZ_ASSERT(mFirstSource);
aTexture = mFirstSource;
return !!aTexture;
}
@@ -500,19 +583,27 @@ BufferTextureHost::GetFormat() const
return gfx::SurfaceFormat::R8G8B8X8;
}
return mFormat;
}
bool
BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
{
- if (mFirstSource && mFirstSource->GetUpdateSerial() == mUpdateSerial) {
+ auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
+
+ if (serial == mUpdateSerial) {
return true;
}
+
+ if (serial == 0) {
+ // 0 means the source has no valid content
+ aRegion = nullptr;
+ }
+
if (!Upload(aRegion)) {
return false;
}
// We no longer have an invalid region.
mNeedsFullUpdate = false;
mMaybeUpdatedRegion.SetEmpty();
@@ -533,44 +624,50 @@ BufferTextureHost::Upload(nsIntRegion *a
// deserializing it.
return false;
}
if (!mCompositor) {
// This can happen if we send textures to a compositable that isn't yet
// attached to a layer.
return false;
}
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ return true;
+ }
+
if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
NS_WARNING("BufferTextureHost: unsupported format!");
return false;
} else if (mFormat == gfx::SurfaceFormat::YUV) {
const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
if (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
RefPtr<gfx::DataSourceSurface> surf =
ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
if (NS_WARN_IF(!surf)) {
return false;
}
if (!mFirstSource) {
mFirstSource = mCompositor->CreateDataTextureSource(mFlags);
+ mFirstSource->SetOwner(this);
}
mFirstSource->Update(surf, aRegion);
return true;
}
RefPtr<DataTextureSource> srcY;
RefPtr<DataTextureSource> srcU;
RefPtr<DataTextureSource> srcV;
if (!mFirstSource) {
// We don't support BigImages for YCbCr compositing.
srcY = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
srcU = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
srcV = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
mFirstSource = srcY;
+ mFirstSource->SetOwner(this);
srcY->SetNextSibling(srcU);
srcU->SetNextSibling(srcV);
} else {
// mFormat never changes so if this was created as a YCbCr host and already
// contains a source it should already have 3 sources.
// BufferTextureHost only uses DataTextureSources so it is safe to assume
// all 3 sources are DataTextureSource.
MOZ_ASSERT(mFirstSource->GetNextSibling());
@@ -606,16 +703,17 @@ BufferTextureHost::Upload(nsIntRegion *a
NS_WARNING("failed to update the DataTextureSource");
return false;
}
} else {
// non-YCbCr case
nsIntRegion* regionToUpdate = aRegion;
if (!mFirstSource) {
mFirstSource = mCompositor->CreateDataTextureSource(mFlags);
+ mFirstSource->SetOwner(this);
if (mFlags & TextureFlags::COMPONENT_ALPHA) {
// Update the full region the first time for component alpha textures.
regionToUpdate = nullptr;
}
}
RefPtr<gfx::DataSourceSurface> surf =
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -227,17 +227,18 @@ typedef CompositableTextureRef<TextureHo
* Interface for TextureSources that can be updated from a DataSourceSurface.
*
* All backend should implement at least one DataTextureSource.
*/
class DataTextureSource : public TextureSource
{
public:
DataTextureSource()
- : mUpdateSerial(0)
+ : mOwner(0)
+ , mUpdateSerial(0)
{}
virtual const char* Name() const override { return "DataTextureSource"; }
virtual DataTextureSource* AsDataTextureSource() override { return this; }
/**
* Upload a (portion of) surface to the TextureSource.
@@ -272,17 +273,33 @@ public:
* Provide read access to the data as a DataSourceSurface.
*
* This is expected to be very slow and should be used for mostly debugging.
* XXX - implement everywhere and make it pure virtual.
*/
virtual already_AddRefed<gfx::DataSourceSurface> ReadBack() { return nullptr; };
#endif
+ void SetOwner(TextureHost* aOwner)
+ {
+ auto newOwner = (uintptr_t)aOwner;
+ if (newOwner != mOwner) {
+ mOwner = newOwner;
+ SetUpdateSerial(0);
+ }
+ }
+
+ bool IsOwnedBy(TextureHost* aOwner) const { return mOwner == (uintptr_t)aOwner; }
+
+ bool HasOwner() const { return !IsOwnedBy(nullptr); }
+
private:
+ // We store mOwner as an integer rather than as a pointer to make it clear
+ // it is not intended to be dereferenced.
+ uintptr_t mOwner;
uint32_t mUpdateSerial;
};
/**
* TextureHost is a thin abstraction over texture data that need to be shared
* between the content process and the compositor process. It is the
* compositor-side half of a TextureClient/TextureHost pair. A corresponding
* TextureClient lives on the content-side.
@@ -508,17 +525,17 @@ public:
virtual const char *Name() { return "TextureHost"; }
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
/**
* Indicates whether the TextureHost implementation is backed by an
* in-memory buffer. The consequence of this is that locking the
* TextureHost does not contend with locking the texture on the client side.
*/
- virtual bool HasInternalBuffer() const { return false; }
+ virtual bool HasIntermediateBuffer() const { return false; }
void AddCompositableRef() { ++mCompositableCount; }
void ReleaseCompositableRef()
{
--mCompositableCount;
MOZ_ASSERT(mCompositableCount >= 0);
if (mCompositableCount == 0) {
@@ -587,16 +604,18 @@ public:
virtual uint8_t* GetBuffer() = 0;
virtual size_t GetBufferSize() = 0;
virtual bool Lock() override;
virtual void Unlock() override;
+ virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
+
virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override;
virtual void DeallocateDeviceData() override;
virtual void SetCompositor(Compositor* aCompositor) override;
/**
* Return the format that is exposed to the compositor when calling
@@ -606,33 +625,35 @@ public:
* GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
*/
virtual gfx::SurfaceFormat GetFormat() const override;
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasIntermediateBuffer() const override { return mHasIntermediateBuffer; }
protected:
bool Upload(nsIntRegion *aRegion = nullptr);
bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+ bool EnsureWrappingTextureSource();
virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
BufferDescriptor mDescriptor;
RefPtr<Compositor> mCompositor;
RefPtr<DataTextureSource> mFirstSource;
nsIntRegion mMaybeUpdatedRegion;
gfx::IntSize mSize;
gfx::SurfaceFormat mFormat;
uint32_t mUpdateSerial;
bool mLocked;
bool mNeedsFullUpdate;
+ bool mHasIntermediateBuffer;
};
/**
* TextureHost that wraps shared memory.
* the corresponding texture on the client side is ShmemTextureClient.
* This TextureHost is backend-independent.
*/
class ShmemTextureHost : public BufferTextureHost
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -144,17 +144,17 @@ UseTileTexture(CompositableTextureHostRe
if (!aUpdateRect.IsEmpty()) {
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
aTexture->Updated(nullptr);
#else
// We possibly upload the entire texture contents here. This is a purposeful
// decision, as sub-image upload can often be slow and/or unreliable, but
// we may want to reevaluate this in the future.
- // For !HasInternalBuffer() textures, this is likely a no-op.
+ // For !HasIntermediateBuffer() textures, this is likely a no-op.
nsIntRegion region = aUpdateRect;
aTexture->Updated(®ion);
#endif
}
aTexture->PrepareTextureSource(aTextureSource);
}
@@ -362,17 +362,17 @@ TiledLayerBufferComposite::UseTiles(cons
if (tile.mTextureHostOnWhite) {
UseTileTexture(tile.mTextureHostOnWhite,
tile.mTextureSourceOnWhite,
texturedDesc.updateRect(),
aCompositor);
}
- if (tile.mTextureHost->HasInternalBuffer()) {
+ if (tile.mTextureHost->HasIntermediateBuffer()) {
// Now that we did the texture upload (in UseTileTexture), we can release
// the lock.
tile.ReadUnlock();
}
}
mTiles = newTiles;
mTileSize = aTiles.tileSize();
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -25,17 +25,17 @@ class DXGITextureData : public TextureDa
{
public:
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
virtual bool SupportsMoz2D() const override { return true; }
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual bool HasSynchronization() const override { return mHasSynchronization; }
virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
static DXGITextureData*
Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aFlags);
@@ -137,17 +137,17 @@ public:
virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
virtual void Unlock() override {}
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
// TODO - DXGIYCbCrTextureClient returned true but that looks like a mistake
- virtual bool HasInternalBuffer() const override{ return false; }
+ virtual bool HasIntermediateBuffer() const override{ return false; }
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual gfx::SurfaceFormat GetFormat() const override { return gfx::SurfaceFormat::YUV; }
virtual bool SupportsMoz2D() const override { return false; }
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override { return nullptr; }
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -179,17 +179,17 @@ protected:
*/
class D3D9TextureData : public TextureData
{
public:
~D3D9TextureData();
virtual bool SupportsMoz2D() const override { return true; }
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasIntermediateBuffer() const override { return true; }
virtual bool Serialize(SurfaceDescriptor& aOutDescrptor) override;
virtual bool Lock(OpenMode aMode, FenceHandle*) override;
virtual void Unlock() override;
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
@@ -241,17 +241,17 @@ public:
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
virtual void Unlock() override {}
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual void Deallocate(ISurfaceAllocator* aAllocator) override {}
IDirect3DDevice9* GetD3D9Device() { return mDevice; }
IDirect3DTexture9* GetD3D9Texture() { return mTexture; }
HANDLE GetShareHandle() const { return mHandle; }
already_AddRefed<IDirect3DSurface9> GetD3D9Surface() const;
@@ -292,17 +292,17 @@ public:
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
{
return nullptr;
}
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasIntermediateBuffer() const override { return true; }
protected:
TextureHostD3D9(TextureFlags aFlags);
IDirect3DDevice9* GetDevice();
virtual void UpdatedInternal(const nsIntRegion* aRegion) override;
RefPtr<DataTextureSourceD3D9> mTextureSource;
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -167,16 +167,18 @@ public:
{
return mTextureFactoryIdentifier;
}
int32_t GetSerial() { return mSerial; }
SyncObject* GetSyncObject() { return mSyncObject; }
+ virtual CompositableForwarder* AsCompositableForwarder() override { return this; }
+
protected:
TextureFactoryIdentifier mTextureFactoryIdentifier;
nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
RefPtr<SyncObject> mSyncObject;
const int32_t mSerial;
static mozilla::Atomic<int32_t> sSerialCounter;
};
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -142,17 +142,21 @@ ISurfaceAllocator::AllocSurfaceDescripto
mozilla::ipc::Shmem shmem;
if (!AllocUnsafeShmem(size, shmemType, &shmem)) {
return false;
}
bufferDesc = shmem;
}
- *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format), bufferDesc);
+ // Use an intermediate buffer by default. Skipping the intermediate buffer is
+ // only possible in certain configurations so let's keep it simple here for now.
+ const bool hasIntermediateBuffer = true;
+ *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format, hasIntermediateBuffer),
+ bufferDesc);
return true;
}
/* static */ bool
ISurfaceAllocator::IsShmem(SurfaceDescriptor* aSurface)
{
return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -36,16 +36,17 @@ class Shmem;
} // namespace ipc
namespace gfx {
class DataSourceSurface;
} // namespace gfx
namespace layers {
class MaybeMagicGrallocBufferHandle;
+class CompositableForwarder;
enum BufferCapabilities {
DEFAULT_BUFFER_CAPS = 0,
/**
* The allocated buffer must be efficiently mappable as a DataSourceSurface.
*/
MAP_AS_IMAGE_SURFACE = 1 << 0,
/**
@@ -155,16 +156,17 @@ public:
virtual MessageLoop * GetMessageLoop() const
{
return mDefaultMessageLoop;
}
// Returns true if aSurface wraps a Shmem.
static bool IsShmem(SurfaceDescriptor* aSurface);
+ virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
protected:
virtual bool IsOnCompositorSide() const = 0;
virtual ~ISurfaceAllocator();
void ShrinkShmemSectionHeap();
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -96,16 +96,17 @@ struct SurfaceDescriptorSharedGLTexture
struct SurfaceDescriptorGralloc {
MaybeMagicGrallocBufferHandle buffer;
bool isOpaque;
};
struct RGBDescriptor {
IntSize size;
SurfaceFormat format;
+ bool hasIntermediateBuffer;
};
struct YCbCrDescriptor {
IntSize ySize;
IntSize cbCrSize;
uint32_t yOffset;
uint32_t cbOffset;
uint32_t crOffset;
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -410,17 +410,17 @@ ShadowLayerForwarder::UseTextures(Compos
MOZ_ASSERT(t.mTextureClient);
MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
FenceHandle fence = t.mTextureClient->GetAcquireFenceHandle();
textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
fence.IsValid() ? MaybeFence(fence) : MaybeFence(null_t()),
t.mTimeStamp, t.mPictureRect,
t.mFrameID, t.mProducerID, t.mInputFrameID));
if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD)
- && t.mTextureClient->HasInternalBuffer()) {
+ && t.mTextureClient->HasIntermediateBuffer()) {
// We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
// race with updates on the main thread. In this case we want the transaction
// to be synchronous.
mTxn->MarkSyncTransaction();
}
}
mTxn->AddEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -53,17 +53,17 @@ public:
virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
virtual bool CanExposeMappedData() const override { return true; }
virtual bool BorrowMappedData(MappedTextureData& aMap) override;
virtual bool SupportsMoz2D() const override { return true; }
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual bool HasSynchronization() const override { return true; }
virtual void Deallocate(ISurfaceAllocator*) override;
virtual void Forget(ISurfaceAllocator*) override;
static GrallocTextureData* CreateForDrawing(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
@@ -104,16 +104,18 @@ public:
android::sp<android::GraphicBuffer> GetGraphicBuffer() { return mGraphicBuffer; }
virtual void WaitForFence(FenceHandle* aFence) override;
~GrallocTextureData();
virtual TextureFlags GetTextureFlags() const override;
+ virtual GrallocTextureData* AsGrallocTextureData() { return this; }
+
protected:
GrallocTextureData(MaybeMagicGrallocBufferHandle aGrallocHandle,
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend);
gfx::IntSize mSize;
gfx::SurfaceFormat mFormat;
gfx::BackendType mMoz2DBackend;
--- a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
@@ -25,17 +25,17 @@ public:
virtual gfx::SurfaceFormat GetFormat() const override;
virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
virtual void Unlock() override {}
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual void Deallocate(ISurfaceAllocator* aAllocator) override { mSurface = nullptr; }
virtual void Forget(ISurfaceAllocator* aAllocator) override { mSurface = nullptr; }
// For debugging purposes only.
already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
--- a/gfx/layers/opengl/TextureClientOGL.h
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -23,17 +23,17 @@ namespace layers {
class EGLImageTextureData : public TextureData
{
public:
static already_AddRefed<TextureClient>
CreateTextureClient(EGLImageImage* aImage, gfx::IntSize aSize,
ISurfaceAllocator* aAllocator, TextureFlags aFlags);
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual void Deallocate(ISurfaceAllocator*) override { mImage = nullptr; }
virtual void Forget(ISurfaceAllocator*) override { mImage = nullptr; }
@@ -64,17 +64,17 @@ public:
CreateTextureClient(gl::AndroidSurfaceTexture* aSurfTex,
gfx::IntSize aSize,
gl::OriginPos aOriginPos,
ISurfaceAllocator* aAllocator,
TextureFlags aFlags);
~AndroidSurfaceTextureData();
- virtual bool HasInternalBuffer() const override { return false; }
+ virtual bool HasIntermediateBuffer() const override { return false; }
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
// Useless functions.
virtual bool Lock(OpenMode, FenceHandle*) override { return true; }
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -128,19 +128,22 @@ void
nsPNGDecoder::CheckForTransparency(SurfaceFormat aFormat,
const IntRect& aFrameRect)
{
// Check if the image has a transparent color in its palette.
if (aFormat == SurfaceFormat::B8G8R8A8) {
PostHasTransparency();
}
- // PNGs shouldn't have first-frame padding.
- MOZ_ASSERT_IF(mNumFrames == 0,
- IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect));
+ // If the first frame of animated image doesn't draw into the whole image,
+ // then record that it is transparent.
+ if (mNumFrames == 0 && !IntRect(IntPoint(), GetSize()).IsEqualEdges(aFrameRect)) {
+ MOZ_ASSERT(HasAnimation());
+ PostHasTransparency();
+ }
}
// CreateFrame() is used for both simple and animated images
nsresult
nsPNGDecoder::CreateFrame(png_uint_32 aXOffset, png_uint_32 aYOffset,
int32_t aWidth, int32_t aHeight,
gfx::SurfaceFormat aFormat)
{
@@ -592,16 +595,22 @@ nsPNGDecoder::info_callback(png_structp
MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
"for an animated image?");
decoder->mDownscaler.reset();
}
}
#endif
if (decoder->IsMetadataDecode()) {
+ // If we are animated then the first frame rect is either: 1) the whole image
+ // if the IDAT chunk is part of the animation 2) the frame rect of the first
+ // fDAT chunk otherwise. If we are not animated then we want to make sure to
+ // call PostHasTransparency in the metadata decode if we need to. So it's okay
+ // to pass IntRect(0, 0, width, height) here for animated images; they will
+ // call with the proper first frame rect in the full decode.
decoder->CheckForTransparency(decoder->format,
IntRect(0, 0, width, height));
// We have the metadata we're looking for, so we don't need to decode any
// further.
decoder->mSuccessfulEarlyFinish = true;
png_longjmp(decoder->mPNG, 1);
}
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -473,24 +473,24 @@ DecodeExpr(FunctionDecoder& f, ExprType
return DecodeBinaryOperator(f, expected, ExprType::I32);
case Expr::I64Add:
case Expr::I64Sub:
case Expr::I64Mul:
case Expr::I64DivS:
case Expr::I64DivU:
case Expr::I64RemS:
case Expr::I64RemU:
+ return f.fail("NYI: i64");
case Expr::I64And:
case Expr::I64Or:
case Expr::I64Xor:
case Expr::I64Shl:
case Expr::I64ShrS:
case Expr::I64ShrU:
- return f.fail("NYI: i64") &&
- DecodeBinaryOperator(f, expected, ExprType::I64);
+ return DecodeBinaryOperator(f, expected, ExprType::I64);
case Expr::F32Add:
case Expr::F32Sub:
case Expr::F32Mul:
case Expr::F32Div:
case Expr::F32Min:
case Expr::F32Max:
return DecodeBinaryOperator(f, expected, ExprType::F32);
case Expr::F32CopySign:
@@ -520,18 +520,17 @@ DecodeExpr(FunctionDecoder& f, ExprType
case Expr::I64LtS:
case Expr::I64LtU:
case Expr::I64LeS:
case Expr::I64LeU:
case Expr::I64GtS:
case Expr::I64GtU:
case Expr::I64GeS:
case Expr::I64GeU:
- return f.fail("NYI: i64") &&
- DecodeComparisonOperator(f, expected, ExprType::I64);
+ return DecodeComparisonOperator(f, expected, ExprType::I64);
case Expr::F32Eq:
case Expr::F32Ne:
case Expr::F32Lt:
case Expr::F32Le:
case Expr::F32Gt:
case Expr::F32Ge:
return DecodeComparisonOperator(f, expected, ExprType::F32);
case Expr::F64Eq:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -440,21 +440,21 @@ class FunctionCompiler
if (inDeadCode())
return nullptr;
MMod* ins = MMod::NewAsmJS(alloc(), lhs, rhs, type, unsignd);
curBlock_->add(ins);
return ins;
}
template <class T>
- MDefinition* bitwise(MDefinition* lhs, MDefinition* rhs)
+ MDefinition* bitwise(MDefinition* lhs, MDefinition* rhs, MIRType type)
{
if (inDeadCode())
return nullptr;
- T* ins = T::NewAsmJS(alloc(), lhs, rhs);
+ T* ins = T::NewAsmJS(alloc(), lhs, rhs, type);
curBlock_->add(ins);
return ins;
}
template <class T>
MDefinition* bitwise(MDefinition* op)
{
if (inDeadCode())
@@ -781,17 +781,17 @@ class FunctionCompiler
return true;
}
MAsmJSLoadFuncPtr* ptrFun;
if (mg().isAsmJS()) {
MOZ_ASSERT(IsPowerOfTwo(length));
MConstant* mask = MConstant::New(alloc(), Int32Value(length - 1));
curBlock_->add(mask);
- MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask);
+ MBitAnd* maskedIndex = MBitAnd::NewAsmJS(alloc(), index, mask, MIRType_Int32);
curBlock_->add(maskedIndex);
ptrFun = MAsmJSLoadFuncPtr::New(alloc(), maskedIndex, globalDataOffset);
curBlock_->add(ptrFun);
} else {
// For wasm code, as a space optimization, the ModuleGenerator does not allocate a
// table for signatures which do not contain any indirectly-callable functions.
// However, these signatures may still be called (it is not a validation error)
// so we instead have a flag alwaysThrow which throws an exception instead of loading
@@ -2221,16 +2221,40 @@ EmitComparison(FunctionCompiler& f, Expr
case Expr::I32LeS: case Expr::I32LtS: case Expr::I32GeS: case Expr::I32GtS:
case Expr::I32Eq: case Expr::I32Ne:
compareType = MCompare::Compare_Int32; break;
case Expr::I32GeU: case Expr::I32GtU: case Expr::I32LeU: case Expr::I32LtU:
compareType = MCompare::Compare_UInt32; break;
default: MOZ_CRASH("impossibru opcode");
}
break;
+ case Expr::I64Eq:
+ case Expr::I64Ne:
+ case Expr::I64LeS:
+ case Expr::I64LtS:
+ case Expr::I64LeU:
+ case Expr::I64LtU:
+ case Expr::I64GeS:
+ case Expr::I64GtS:
+ case Expr::I64GeU:
+ case Expr::I64GtU:
+ if (!EmitExpr(f, ExprType::I64, &lhs) || !EmitExpr(f, ExprType::I64, &rhs))
+ return false;
+ switch (expr) {
+ case Expr::I64LeS: case Expr::I64LtS: case Expr::I64GeS: case Expr::I64GtS:
+ case Expr::I64Eq: case Expr::I64Ne:
+ compareType = MCompare::Compare_Int64;
+ break;
+ case Expr::I64GeU: case Expr::I64GtU: case Expr::I64LeU: case Expr::I64LtU:
+ compareType = MCompare::Compare_UInt64;
+ break;
+ default:
+ MOZ_CRASH("unexpected opcode");
+ }
+ break;
case Expr::F32Eq:
case Expr::F32Ne:
case Expr::F32Le:
case Expr::F32Lt:
case Expr::F32Ge:
case Expr::F32Gt:
if (!EmitExpr(f, ExprType::F32, &lhs) || !EmitExpr(f, ExprType::F32, &rhs))
return false;
@@ -2247,73 +2271,83 @@ EmitComparison(FunctionCompiler& f, Expr
compareType = MCompare::Compare_Double;
break;
default: MOZ_CRASH("unexpected comparison opcode");
}
JSOp compareOp;
switch (expr) {
case Expr::I32Eq:
+ case Expr::I64Eq:
case Expr::F32Eq:
case Expr::F64Eq:
compareOp = JSOP_EQ;
break;
case Expr::I32Ne:
+ case Expr::I64Ne:
case Expr::F32Ne:
case Expr::F64Ne:
compareOp = JSOP_NE;
break;
case Expr::I32LeS:
case Expr::I32LeU:
+ case Expr::I64LeS:
+ case Expr::I64LeU:
case Expr::F32Le:
case Expr::F64Le:
compareOp = JSOP_LE;
break;
case Expr::I32LtS:
case Expr::I32LtU:
+ case Expr::I64LtS:
+ case Expr::I64LtU:
case Expr::F32Lt:
case Expr::F64Lt:
compareOp = JSOP_LT;
break;
case Expr::I32GeS:
case Expr::I32GeU:
+ case Expr::I64GeS:
+ case Expr::I64GeU:
case Expr::F32Ge:
case Expr::F64Ge:
compareOp = JSOP_GE;
break;
case Expr::I32GtS:
case Expr::I32GtU:
+ case Expr::I64GtS:
+ case Expr::I64GtU:
case Expr::F32Gt:
case Expr::F64Gt:
compareOp = JSOP_GT;
break;
default: MOZ_CRASH("unexpected comparison opcode");
}
*def = f.compare(lhs, rhs, compareOp, compareType);
return true;
}
template<class T>
static bool
-EmitBitwise(FunctionCompiler& f, MDefinition** def)
+EmitBitwise(FunctionCompiler& f, ExprType type, MDefinition** def)
{
MDefinition* lhs;
- if (!EmitExpr(f, ExprType::I32, &lhs))
+ if (!EmitExpr(f, type, &lhs))
return false;
MDefinition* rhs;
- if (!EmitExpr(f, ExprType::I32, &rhs))
+ if (!EmitExpr(f, type, &rhs))
return false;
- *def = f.bitwise<T>(lhs, rhs);
+ MIRType mirType = ToMIRType(type);
+ *def = f.bitwise<T>(lhs, rhs, mirType);
return true;
}
-template<>
-bool
-EmitBitwise<MBitNot>(FunctionCompiler& f, MDefinition** def)
+static bool
+EmitBitwiseNot(FunctionCompiler& f, MDefinition** def)
{
MDefinition* in;
if (!EmitExpr(f, ExprType::I32, &in))
return false;
*def = f.bitwise<MBitNot>(in);
return true;
}
@@ -2781,29 +2815,29 @@ EmitExpr(FunctionCompiler& f, ExprType t
return EmitUnary<MTruncateToInt32>(f, ExprType::F64, def);
case Expr::I32Clz:
return EmitUnary<MClz>(f, ExprType::I32, def);
case Expr::I32Abs:
return EmitUnaryMir<MAbs>(f, ExprType::I32, def);
case Expr::I32Neg:
return EmitUnaryMir<MAsmJSNeg>(f, ExprType::I32, def);
case Expr::I32Or:
- return EmitBitwise<MBitOr>(f, def);
+ return EmitBitwise<MBitOr>(f, ExprType::I32, def);
case Expr::I32And:
- return EmitBitwise<MBitAnd>(f, def);
+ return EmitBitwise<MBitAnd>(f, ExprType::I32, def);
case Expr::I32Xor:
- return EmitBitwise<MBitXor>(f, def);
+ return EmitBitwise<MBitXor>(f, ExprType::I32, def);
case Expr::I32Shl:
- return EmitBitwise<MLsh>(f, def);
+ return EmitBitwise<MLsh>(f, ExprType::I32, def);
case Expr::I32ShrS:
- return EmitBitwise<MRsh>(f, def);
+ return EmitBitwise<MRsh>(f, ExprType::I32, def);
case Expr::I32ShrU:
- return EmitBitwise<MUrsh>(f, def);
+ return EmitBitwise<MUrsh>(f, ExprType::I32, def);
case Expr::I32BitNot:
- return EmitBitwise<MBitNot>(f, def);
+ return EmitBitwiseNot(f, def);
case Expr::I32LoadMem8S:
return EmitLoad(f, Scalar::Int8, def);
case Expr::I32LoadMem8U:
return EmitLoad(f, Scalar::Uint8, def);
case Expr::I32LoadMem16S:
return EmitLoad(f, Scalar::Int16, def);
case Expr::I32LoadMem16U:
return EmitLoad(f, Scalar::Uint16, def);
@@ -2820,16 +2854,26 @@ EmitExpr(FunctionCompiler& f, ExprType t
case Expr::I32LtS:
case Expr::I32LeS:
case Expr::I32GtS:
case Expr::I32GeS:
case Expr::I32LtU:
case Expr::I32LeU:
case Expr::I32GtU:
case Expr::I32GeU:
+ case Expr::I64Eq:
+ case Expr::I64Ne:
+ case Expr::I64LtS:
+ case Expr::I64LeS:
+ case Expr::I64LtU:
+ case Expr::I64LeU:
+ case Expr::I64GtS:
+ case Expr::I64GeS:
+ case Expr::I64GtU:
+ case Expr::I64GeU:
case Expr::F32Eq:
case Expr::F32Ne:
case Expr::F32Lt:
case Expr::F32Le:
case Expr::F32Gt:
case Expr::F32Ge:
case Expr::F64Eq:
case Expr::F64Ne:
@@ -2846,16 +2890,28 @@ EmitExpr(FunctionCompiler& f, ExprType t
return EmitAtomicsLoad(f, def);
case Expr::I32AtomicsStore:
return EmitAtomicsStore(f, def);
case Expr::I32AtomicsBinOp:
return EmitAtomicsBinOp(f, def);
// I64
case Expr::I64Const:
return EmitLiteral(f, ExprType::I64, def);
+ case Expr::I64Or:
+ return EmitBitwise<MBitOr>(f, ExprType::I64, def);
+ case Expr::I64And:
+ return EmitBitwise<MBitAnd>(f, ExprType::I64, def);
+ case Expr::I64Xor:
+ return EmitBitwise<MBitXor>(f, ExprType::I64, def);
+ case Expr::I64Shl:
+ return EmitBitwise<MLsh>(f, ExprType::I64, def);
+ case Expr::I64ShrS:
+ return EmitBitwise<MRsh>(f, ExprType::I64, def);
+ case Expr::I64ShrU:
+ return EmitBitwise<MUrsh>(f, ExprType::I64, def);
// F32
case Expr::F32Const:
return EmitLiteral(f, ExprType::F32, def);
case Expr::F32Add:
return EmitAddOrSub(f, ExprType::F32, IsAdd(true), def);
case Expr::F32Sub:
return EmitAddOrSub(f, ExprType::F32, IsAdd(false), def);
case Expr::F32Mul:
@@ -3022,32 +3078,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
case Expr::I64Popcnt:
case Expr::I64Add:
case Expr::I64Sub:
case Expr::I64Mul:
case Expr::I64DivS:
case Expr::I64DivU:
case Expr::I64RemS:
case Expr::I64RemU:
- case Expr::I64Or:
- case Expr::I64And:
- case Expr::I64Xor:
- case Expr::I64Shl:
- case Expr::I64ShrU:
- case Expr::I64ShrS:
- case Expr::I64Eq:
- case Expr::I64Ne:
- case Expr::I64LtS:
- case Expr::I64LeS:
- case Expr::I64LtU:
- case Expr::I64LeU:
- case Expr::I64GtS:
- case Expr::I64GeS:
- case Expr::I64GtU:
- case Expr::I64GeU:
MOZ_CRASH("NYI");
case Expr::Unreachable:
break;
case Expr::Limit:
MOZ_CRASH("Limit");
}
MOZ_CRASH("unexpected wasm opcode");
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -755,23 +755,25 @@ frontend::CompileScript(ExclusiveContext
if (sourceObjectOut)
*sourceObjectOut = compiler.sourceObjectPtr();
return script;
}
ModuleObject*
frontend::CompileModule(ExclusiveContext* cx, const ReadOnlyCompileOptions& optionsInput,
- SourceBufferHolder& srcBuf, LifoAlloc* alloc)
+ SourceBufferHolder& srcBuf, LifoAlloc* alloc,
+ ScriptSourceObject** sourceObjectOut /* = nullptr */)
{
MOZ_ASSERT(srcBuf.get());
MOZ_ASSERT(cx->isJSContext() == (alloc == nullptr));
if (!alloc)
alloc = &cx->asJSContext()->tempLifoAlloc();
+ MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
CompileOptions options(cx, optionsInput);
options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
options.setIsRunOnce(true);
Rooted<StaticScope*> staticScope(cx, &cx->global()->lexicalScope().staticBlock());
BytecodeCompiler compiler(cx, alloc, options, srcBuf, staticScope,
TraceLogger_ParserCompileModule);
@@ -779,16 +781,20 @@ frontend::CompileModule(ExclusiveContext
if (!module)
return nullptr;
// This happens in GlobalHelperThreadState::finishModuleParseTask() when a
// module is compiled off main thread.
if (cx->isJSContext() && !ModuleObject::FreezeArrayProperties(cx->asJSContext(), module))
return nullptr;
+ // See the comment about sourceObjectOut above.
+ if (sourceObjectOut)
+ *sourceObjectOut = compiler.sourceObjectPtr();
+
return module;
}
bool
frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
{
MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -29,17 +29,18 @@ CompileScript(ExclusiveContext* cx, Lifo
HandleObject scopeChain, Handle<StaticScope*> enclosingStaticScope,
HandleScript evalCaller, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
SourceCompressionTask* extraSct = nullptr,
ScriptSourceObject** sourceObjectOut = nullptr);
ModuleObject*
CompileModule(ExclusiveContext *cx, const ReadOnlyCompileOptions &options,
- SourceBufferHolder &srcBuf, LifoAlloc* alloc = nullptr);
+ SourceBufferHolder &srcBuf, LifoAlloc* alloc = nullptr,
+ ScriptSourceObject** sourceObjectOut = nullptr);
bool
CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
/*
* enclosingStaticScope is a static enclosing scope (e.g. a StaticWithScope).
* Must be null if the enclosing scope is a global.
*/
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -145,16 +145,18 @@ template JSObject* js::Allocate<JSObject
const Class* clasp);
// Attempt to allocate a new GC thing out of the nursery. If there is not enough
// room in the nursery or there is an OOM, this method will return nullptr.
template <AllowGC allowGC>
JSObject*
GCRuntime::tryNewNurseryObject(JSContext* cx, size_t thingSize, size_t nDynamicSlots, const Class* clasp)
{
+ MOZ_ASSERT(isNurseryAllocAllowed());
+ MOZ_ASSERT(!cx->zone()->usedByExclusiveThread);
MOZ_ASSERT(!IsAtomsCompartment(cx->compartment()));
JSObject* obj = nursery.allocateObject(cx, thingSize, nDynamicSlots, clasp);
if (obj)
return obj;
if (allowGC && !rt->mainThread.suppressGC) {
minorGC(cx, JS::gcreason::OUT_OF_NURSERY);
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -703,16 +703,23 @@ class GCRuntime
#ifdef DEBUG
bool isAllocAllowed() { return noGCOrAllocationCheck == 0; }
void disallowAlloc() { ++noGCOrAllocationCheck; }
void allowAlloc() {
MOZ_ASSERT(!isAllocAllowed());
--noGCOrAllocationCheck;
}
+ bool isNurseryAllocAllowed() { return noNurseryAllocationCheck == 0; }
+ void disallowNurseryAlloc() { ++noNurseryAllocationCheck; }
+ void allowNurseryAlloc() {
+ MOZ_ASSERT(!isNurseryAllocAllowed());
+ --noNurseryAllocationCheck;
+ }
+
bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
void enterUnsafeRegion() { ++inUnsafeRegion; }
void leaveUnsafeRegion() {
MOZ_ASSERT(inUnsafeRegion > 0);
--inUnsafeRegion;
}
bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
@@ -1307,16 +1314,17 @@ class GCRuntime
* Some regions of code are hard for the static rooting hazard analysis to
* understand. In those cases, we trade the static analysis for a dynamic
* analysis. When this is non-zero, we should assert if we trigger, or
* might trigger, a GC.
*/
int inUnsafeRegion;
size_t noGCOrAllocationCheck;
+ size_t noNurseryAllocationCheck;
#endif
/* Synchronize GC heap access between main thread and GCHelperState. */
PRLock* lock;
mozilla::DebugOnly<mozilla::Atomic<PRThread*>> lockOwner;
BackgroundAllocTask allocTask;
GCHelperState helperState;
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -4,21 +4,45 @@ assertEq(wasmEvalText('(module (func (re
assertEq(wasmEvalText('(module (func (result i32) (i32.const -2147483648)) (export "" 0))')(), -2147483648);
assertEq(wasmEvalText('(module (func (result i32) (i32.const 4294967295)) (export "" 0))')(), -1);
function testUnary(type, opcode, op, expect) {
assertEq(wasmEvalText('(module (func (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0))) (export "" 0))')(op), expect);
}
function testBinary(type, opcode, lhs, rhs, expect) {
- assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+ if (type === 'i64') {
+ // i64 cannot be imported/exported, so we use a wrapper function.
+ assertEq(wasmEvalText(`(module
+ (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (get_local 1)))
+ (func (result i32) (i64.eq (call 0 (i64.const ${lhs}) (i64.const ${rhs})) (i64.const ${expect})))
+ (export "" 1))`)(), 1);
+ } else {
+ assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+ }
}
function testComparison(type, opcode, lhs, rhs, expect) {
- assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+ if (type === 'i64') {
+ // i64 cannot be imported/exported, so we use a wrapper function.
+ assertEq(wasmEvalText(`(module
+ (func (param i64) (param i64) (result i32) (i64.${opcode} (get_local 0) (get_local 1)))
+ (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
+ (export "" 1))`)(), expect);
+ // Also test if_else, for the compare-and-branch path.
+ assertEq(wasmEvalText(`(module
+ (func (param i64) (param i64) (result i32)
+ (if_else (i64.${opcode} (get_local 0) (get_local 1))
+ (i32.const 1)
+ (i32.const 0)))
+ (func (result i32) (call 0 (i64.const ${lhs}) (i64.const ${rhs})))
+ (export "" 1))`)(), expect);
+ } else {
+ assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result i32) (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
+ }
}
testUnary('i32', 'clz', 40, 26);
//testUnary('i32', 'ctz', 40, 0); // TODO: NYI
//testUnary('i32', 'popcnt', 40, 0); // TODO: NYI
testBinary('i32', 'add', 40, 2, 42);
testBinary('i32', 'sub', 40, 2, 38);
@@ -51,33 +75,64 @@ testComparison('i32', 'ge_u', 40, 40, 1)
//testBinary('i64', 'add', 40, 2, 42); // TODO: NYI
//testBinary('i64', 'sub', 40, 2, 38); // TODO: NYI
//testBinary('i64', 'mul', 40, 2, 80); // TODO: NYI
//testBinary('i64', 'div_s', -40, 2, -20); // TODO: NYI
//testBinary('i64', 'div_u', -40, 2, 2147483628); // TODO: NYI
//testBinary('i64', 'rem_s', 40, -3, 1); // TODO: NYI
//testBinary('i64', 'rem_u', 40, -3, 40); // TODO: NYI
-//testBinary('i64', 'and', 42, 6, 2); // TODO: NYI
-//testBinary('i64', 'or', 42, 6, 46); // TODO: NYI
-//testBinary('i64', 'xor', 42, 2, 40); // TODO: NYI
-//testBinary('i64', 'shl', 40, 2, 160); // TODO: NYI
-//testBinary('i64', 'shr_s', -40, 2, -10); // TODO: NYI
-//testBinary('i64', 'shr_u', -40, 2, 1073741814); // TODO: NYI
+
+if (getBuildConfiguration().x64) {
+ testBinary('i64', 'and', 42, 6, 2);
+ testBinary('i64', 'or', 42, 6, 46);
+ testBinary('i64', 'xor', 42, 2, 40);
+ testBinary('i64', 'and', "0x8765432112345678", "0xffff0000ffff0000", "0x8765000012340000");
+ testBinary('i64', 'or', "0x8765432112345678", "0xffff0000ffff0000", "0xffff4321ffff5678");
+ testBinary('i64', 'xor', "0x8765432112345678", "0xffff0000ffff0000", "0x789a4321edcb5678");
+ testBinary('i64', 'shl', 40, 2, 160);
+ testBinary('i64', 'shr_s', -40, 2, -10);
+ testBinary('i64', 'shr_u', -40, 2, "0x3ffffffffffffff6");
+ testBinary('i64', 'shl', 0xff00ff, 28, "0xff00ff0000000");
+ testBinary('i64', 'shl', 1, 63, "0x8000000000000000");
+ testBinary('i64', 'shl', 1, 64, 1);
+ testBinary('i64', 'shr_s', "0xff00ff0000000", 28, 0xff00ff);
+ testBinary('i64', 'shr_u', "0x8ffff00ff0000000", 56, 0x8f);
-//testComparison('i64', 'eq', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'ne', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'lt_s', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'lt_u', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'le_s', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'le_u', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'gt_s', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'gt_u', 40, 40, 0); // TODO: NYI
-//testComparison('i64', 'ge_s', 40, 40, 1); // TODO: NYI
-//testComparison('i64', 'ge_u', 40, 40, 1); // TODO: NYI
+ testComparison('i64', 'eq', 40, 40, 1);
+ testComparison('i64', 'ne', 40, 40, 0);
+ testComparison('i64', 'lt_s', 40, 40, 0);
+ testComparison('i64', 'lt_u', 40, 40, 0);
+ testComparison('i64', 'le_s', 40, 40, 1);
+ testComparison('i64', 'le_u', 40, 40, 1);
+ testComparison('i64', 'gt_s', 40, 40, 0);
+ testComparison('i64', 'gt_u', 40, 40, 0);
+ testComparison('i64', 'ge_s', 40, 40, 1);
+ testComparison('i64', 'ge_u', 40, 40, 1);
+ testComparison('i64', 'eq', "0x400012345678", "0x400012345678", 1);
+ testComparison('i64', 'ne', "0x400012345678", "0x400012345678", 0);
+ testComparison('i64', 'ne', "0x400012345678", "0x500012345678", 1);
+ testComparison('i64', 'eq', "0xffffffffffffffff", "-1", 1);
+ testComparison('i64', 'lt_s', "0x8000000012345678", "0x1", 1);
+ testComparison('i64', 'lt_u', "0x8000000012345678", "0x1", 0);
+ testComparison('i64', 'le_s', "-1", "0", 1);
+ testComparison('i64', 'le_u', "-1", "-1", 1);
+ testComparison('i64', 'gt_s', "1", "0x8000000000000000", 1);
+ testComparison('i64', 'gt_u', "1", "0x8000000000000000", 0);
+ testComparison('i64', 'ge_s', "1", "0x8000000000000000", 1);
+ testComparison('i64', 'ge_u', "1", "0x8000000000000000", 0);
+} else {
+ // Sleeper test: once i64 works on more platforms, remove this if-else.
+ try {
+ testComparison('i64', 'eq', 40, 40, 1);
+ assertEq(0, 1);
+ } catch(e) {
+ assertEq(e.toString().indexOf("NYI on this platform") >= 0, true);
+ }
+}
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result i32) (i32.clz (get_local 0))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (result f32) (i32.clz (get_local 0))))'), TypeError, mismatchError("i32", "f32"));
assertErrorMessage(() => wasmEvalText('(module (func (param f32) (param i32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param f32) (result i32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("f32", "i32"));
assertErrorMessage(() => wasmEvalText('(module (func (param i32) (param i32) (result f32) (i32.add (get_local 0) (get_local 1))))'), TypeError, mismatchError("i32", "f32"));
--- a/js/src/jit/AlignmentMaskAnalysis.cpp
+++ b/js/src/jit/AlignmentMaskAnalysis.cpp
@@ -60,17 +60,17 @@ AnalyzeAsmHeapAddress(MDefinition* ptr,
return;
uint32_t i = op1->toConstant()->toInt32();
uint32_t m = rhs->toConstant()->toInt32();
if (!IsAlignmentMask(m) || (i & m) != i)
return;
// The pattern was matched! Produce the replacement expression.
- MInstruction* and_ = MBitAnd::NewAsmJS(graph.alloc(), op0, rhs);
+ MInstruction* and_ = MBitAnd::NewAsmJS(graph.alloc(), op0, rhs, MIRType_Int32);
ptr->block()->insertBefore(ptr->toBitAnd(), and_);
MInstruction* add = MAdd::NewAsmJS(graph.alloc(), and_, op1, MIRType_Int32);
ptr->block()->insertBefore(ptr->toBitAnd(), add);
ptr->replaceAllUsesWith(add);
ptr->block()->discard(ptr->toBitAnd());
}
bool
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1890,17 +1890,16 @@ jit::FinishBailoutToBaseline(BaselineBai
case Bailout_NonBooleanInput:
case Bailout_NonObjectInput:
case Bailout_NonStringInput:
case Bailout_NonSymbolInput:
case Bailout_NonSimdBool32x4Input:
case Bailout_NonSimdInt32x4Input:
case Bailout_NonSimdFloat32x4Input:
case Bailout_NonSharedTypedArrayInput:
- case Bailout_InitialState:
case Bailout_Debugger:
case Bailout_UninitializedThis:
case Bailout_BadDerivedConstructorReturn:
// Do nothing.
break;
case Bailout_FirstExecution:
// Do not return directly, as this was not frequent in the first place,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -840,17 +840,17 @@ IonBuilder::build()
// Initialize the arguments object slot to undefined if necessary.
if (info().hasArguments()) {
MInstruction* argsObj = MConstant::New(alloc(), UndefinedValue());
current->add(argsObj);
current->initSlot(info().argsObjSlot(), argsObj);
}
// Emit the start instruction, so we can begin real instructions.
- current->add(MStart::New(alloc(), MStart::StartType_Default));
+ current->add(MStart::New(alloc()));
// Guard against over-recursion. Do this before we start unboxing, since
// this will create an OSI point that will read the incoming argument
// values, which is nice to do before their last real use, to minimize
// register/stack pressure.
MCheckOverRecursed* check = MCheckOverRecursed::New(alloc());
current->add(check);
MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
@@ -7579,17 +7579,17 @@ IonBuilder::newOsrPreheader(MBasicBlock*
ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(info().nlocals() + i);
MOsrValue* osrv = MOsrValue::New(alloc(), entry, offset);
osrBlock->add(osrv);
osrBlock->initSlot(slot, osrv);
}
// Create an MStart to hold the first valid MResumePoint.
- MStart* start = MStart::New(alloc(), MStart::StartType_Osr);
+ MStart* start = MStart::New(alloc());
osrBlock->add(start);
// MOsrValue instructions are infallible, so the first MResumePoint must
// occur after they execute, at the point of the MStart.
if (!resumeAt(start, loopEntry))
return nullptr;
// Link the same MResumePoint from the MStart to each MOsrValue.
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -105,19 +105,16 @@ enum BailoutKind
Bailout_NonSimdBool32x4Input,
Bailout_NonSimdInt32x4Input,
Bailout_NonSimdFloat32x4Input,
// Atomic operations require shared memory, bail out if the typed array
// maps unshared memory.
Bailout_NonSharedTypedArrayInput,
- // For the initial snapshot when entering a function.
- Bailout_InitialState,
-
// We hit a |debugger;| statement.
Bailout_Debugger,
// |this| used uninitialized in a derived constructor
Bailout_UninitializedThis,
// Derived constructors must return object or undefined
Bailout_BadDerivedConstructorReturn,
@@ -217,18 +214,16 @@ BailoutKindString(BailoutKind kind)
case Bailout_NonSimdBool32x4Input:
return "Bailout_NonSimdBool32x4Input";
case Bailout_NonSimdInt32x4Input:
return "Bailout_NonSimdInt32x4Input";
case Bailout_NonSimdFloat32x4Input:
return "Bailout_NonSimdFloat32x4Input";
case Bailout_NonSharedTypedArrayInput:
return "Bailout_NonSharedTypedArrayInput";
- case Bailout_InitialState:
- return "Bailout_InitialState";
case Bailout_Debugger:
return "Bailout_Debugger";
case Bailout_UninitializedThis:
return "Bailout_UninitializedThis";
case Bailout_BadDerivedConstructorReturn:
return "Bailout_BadDerivedConstructorReturn";
case Bailout_FirstExecution:
return "Bailout_FirstExecution";
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -1102,16 +1102,24 @@ class LInstructionHelper : public detail
void setBoxOperand(size_t index, const LBoxAllocation& alloc) {
#ifdef JS_NUNBOX32
operands_[index] = alloc.type();
operands_[index + 1] = alloc.payload();
#else
operands_[index] = alloc.value();
#endif
}
+ void setInt64Operand(size_t index, const LInt64Allocation& alloc) {
+#if JS_BITS_PER_WORD == 32
+ operands_[index] = alloc.low();
+ operands_[index + 1] = alloc.high();
+#else
+ operands_[index] = alloc.value();
+#endif
+ }
};
template<size_t Defs, size_t Temps>
class LVariadicInstruction : public details::LInstructionFixedDefsTempsHelper<Defs, Temps>
{
FixedList<LAllocation> operands_;
public:
@@ -1299,19 +1307,16 @@ class LSnapshot : public TempObject
}
void setBailoutId(BailoutId id) {
MOZ_ASSERT(bailoutId_ == INVALID_BAILOUT_ID);
bailoutId_ = id;
}
BailoutKind bailoutKind() const {
return bailoutKind_;
}
- void setBailoutKind(BailoutKind kind) {
- bailoutKind_ = kind;
- }
void rewriteRecoveredInput(LUse input);
};
struct SafepointSlotEntry {
// Flag indicating whether this is a slot in the stack or argument space.
uint32_t stack:1;
// Byte offset of the slot, as in LStackSlot or LArgument.
@@ -1820,18 +1825,16 @@ class LIRGraph
size_t numConstants() const {
return constantPool_.length();
}
Value* constantPool() {
return &constantPool_[0];
}
void setEntrySnapshot(LSnapshot* snapshot) {
MOZ_ASSERT(!entrySnapshot_);
- MOZ_ASSERT(snapshot->bailoutKind() == Bailout_InitialState);
- snapshot->setBailoutKind(Bailout_ArgumentCheck);
entrySnapshot_ = snapshot;
}
LSnapshot* entrySnapshot() const {
MOZ_ASSERT(entrySnapshot_);
return entrySnapshot_;
}
bool noteNeedsSafepoint(LInstruction* ins);
size_t numNonCallSafepoints() const {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -834,16 +834,29 @@ LIRGenerator::visitTest(MTest* test)
else
rhs = useRegister(right);
LCompareAndBranch* lir = new(alloc()) LCompareAndBranch(comp, op, lhs, rhs,
ifTrue, ifFalse);
add(lir, test);
return;
}
+ // Compare and branch Int64.
+ if (comp->compareType() == MCompare::Compare_Int64 ||
+ comp->compareType() == MCompare::Compare_UInt64)
+ {
+ JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+ LCompare64AndBranch* lir = new(alloc()) LCompare64AndBranch(comp, op,
+ useInt64Register(left),
+ useInt64OrConstant(right),
+ ifTrue, ifFalse);
+ add(lir, test);
+ return;
+ }
+
// Compare and branch doubles.
if (comp->isDoubleComparison()) {
LAllocation lhs = useRegister(left);
LAllocation rhs = useRegister(right);
LCompareDAndBranch* lir = new(alloc()) LCompareDAndBranch(comp, lhs, rhs,
ifTrue, ifFalse);
add(lir, test);
return;
@@ -1064,16 +1077,26 @@ LIRGenerator::visitCompare(MCompare* com
rhs = useAnyOrConstant(right);
} else {
rhs = useRegister(right);
}
define(new(alloc()) LCompare(op, lhs, rhs), comp);
return;
}
+ // Compare Int64.
+ if (comp->compareType() == MCompare::Compare_Int64 ||
+ comp->compareType() == MCompare::Compare_UInt64)
+ {
+ JSOp op = ReorderComparison(comp->jsop(), &left, &right);
+ define(new(alloc()) LCompare64(op, useInt64Register(left), useInt64OrConstant(right)),
+ comp);
+ return;
+ }
+
// Compare doubles.
if (comp->isDoubleComparison()) {
define(new(alloc()) LCompareD(useRegister(left), useRegister(right)), comp);
return;
}
// Compare float32.
if (comp->isFloat32Comparison()) {
@@ -1093,22 +1116,30 @@ LIRGenerator::visitCompare(MCompare* com
}
void
LIRGenerator::lowerBitOp(JSOp op, MInstruction* ins)
{
MDefinition* lhs = ins->getOperand(0);
MDefinition* rhs = ins->getOperand(1);
- if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+ if (lhs->type() == MIRType_Int32) {
+ MOZ_ASSERT(rhs->type() == MIRType_Int32);
ReorderCommutative(&lhs, &rhs, ins);
lowerForALU(new(alloc()) LBitOpI(op), ins, lhs, rhs);
return;
}
+ if (lhs->type() == MIRType_Int64) {
+ MOZ_ASSERT(rhs->type() == MIRType_Int64);
+ ReorderCommutative(&lhs, &rhs, ins);
+ lowerForALUInt64(new(alloc()) LBitOpI64(op), ins, lhs, rhs);
+ return;
+ }
+
LBitOpV* lir = new(alloc()) LBitOpV(op, useBoxAtStart(lhs), useBoxAtStart(rhs));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitTypeOf(MTypeOf* ins)
{
@@ -1192,32 +1223,40 @@ LIRGenerator::visitBitXor(MBitXor* ins)
}
void
LIRGenerator::lowerShiftOp(JSOp op, MShiftInstruction* ins)
{
MDefinition* lhs = ins->getOperand(0);
MDefinition* rhs = ins->getOperand(1);
- if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+ if (lhs->type() == MIRType_Int32) {
+ MOZ_ASSERT(rhs->type() == MIRType_Int32);
+
if (ins->type() == MIRType_Double) {
MOZ_ASSERT(op == JSOP_URSH);
lowerUrshD(ins->toUrsh());
return;
}
LShiftI* lir = new(alloc()) LShiftI(op);
if (op == JSOP_URSH) {
if (ins->toUrsh()->fallible())
assignSnapshot(lir, Bailout_OverflowInvalidate);
}
lowerForShift(lir, ins, lhs, rhs);
return;
}
+ if (lhs->type() == MIRType_Int64) {
+ MOZ_ASSERT(rhs->type() == MIRType_Int64);
+ lowerForShiftInt64(new(alloc()) LShiftI64(op), ins, lhs, rhs);
+ return;
+ }
+
MOZ_ASSERT(ins->specialization() == MIRType_None);
if (op == JSOP_URSH) {
// Result is either int32 or double so we have to use BinaryV.
lowerBinaryV(JSOP_URSH, ins);
return;
}
@@ -1703,22 +1742,23 @@ LIRGenerator::visitFromCharCode(MFromCha
LFromCharCode* lir = new(alloc()) LFromCharCode(useRegister(code));
define(lir, ins);
assignSafepoint(lir, ins);
}
void
LIRGenerator::visitStart(MStart* start)
{
+ LStart* lir = new(alloc()) LStart;
+
// Create a snapshot that captures the initial state of the function.
- LStart* lir = new(alloc()) LStart;
- assignSnapshot(lir, Bailout_InitialState);
-
- if (start->startType() == MStart::StartType_Default && lir->snapshot())
+ assignSnapshot(lir, Bailout_ArgumentCheck);
+ if (start->block()->graph().entryBlock() == start->block())
lirGraph_.setEntrySnapshot(lir->snapshot());
+
add(lir);
}
void
LIRGenerator::visitNop(MNop* nop)
{
}
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1803,16 +1803,17 @@ MCompare::New(TempAllocator& alloc, MDef
return new(alloc) MCompare(left, right, op);
}
MCompare*
MCompare::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op,
CompareType compareType)
{
MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
+ compareType == Compare_Int64 || compareType == Compare_UInt64 ||
compareType == Compare_Double || compareType == Compare_Float32);
MCompare* comp = new(alloc) MCompare(left, right, op);
comp->compareType_ = compareType;
comp->operandMightEmulateUndefined_ = false;
comp->setResultType(MIRType_Int32);
return comp;
}
@@ -2468,25 +2469,27 @@ MBinaryBitwiseInstruction::foldUnnecessa
void
MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
{
if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(0)->mightBeType(MIRType_Symbol) ||
getOperand(1)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Symbol))
{
specialization_ = MIRType_None;
} else {
- specializeAsInt32();
+ specializeAs(MIRType_Int32);
}
}
void
-MBinaryBitwiseInstruction::specializeAsInt32()
-{
- specialization_ = MIRType_Int32;
- MOZ_ASSERT(type() == MIRType_Int32);
+MBinaryBitwiseInstruction::specializeAs(MIRType type)
+{
+ MOZ_ASSERT(type == MIRType_Int32 || type == MIRType_Int64);
+ MOZ_ASSERT(this->type() == type);
+
+ specialization_ = type;
if (isBitOr() || isBitAnd() || isBitXor())
setCommutative();
}
void
MShiftInstruction::infer(BaselineInspector*, jsbytecode*)
{
@@ -3402,94 +3405,94 @@ MTypeOf::cacheInputMaybeCallableOrEmulat
if (!input()->maybeEmulatesUndefined(constraints) && !MaybeCallable(constraints, input()))
markInputNotCallableOrEmulatesUndefined();
}
MBitAnd*
MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
{
- return new(alloc) MBitAnd(left, right);
+ return new(alloc) MBitAnd(left, right, MIRType_Int32);
}
MBitAnd*
-MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
- MBitAnd* ins = new(alloc) MBitAnd(left, right);
- ins->specializeAsInt32();
+MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+ MBitAnd* ins = new(alloc) MBitAnd(left, right, type);
+ ins->specializeAs(type);
return ins;
}
MBitOr*
MBitOr::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
{
- return new(alloc) MBitOr(left, right);
+ return new(alloc) MBitOr(left, right, MIRType_Int32);
}
MBitOr*
-MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
- MBitOr* ins = new(alloc) MBitOr(left, right);
- ins->specializeAsInt32();
+MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+ MBitOr* ins = new(alloc) MBitOr(left, right, type);
+ ins->specializeAs(type);
return ins;
}
MBitXor*
MBitXor::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
{
- return new(alloc) MBitXor(left, right);
+ return new(alloc) MBitXor(left, right, MIRType_Int32);
}
MBitXor*
-MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
- MBitXor* ins = new(alloc) MBitXor(left, right);
- ins->specializeAsInt32();
+MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+ MBitXor* ins = new(alloc) MBitXor(left, right, type);
+ ins->specializeAs(type);
return ins;
}
MLsh*
MLsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
{
- return new(alloc) MLsh(left, right);
+ return new(alloc) MLsh(left, right, MIRType_Int32);
}
MLsh*
-MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
- MLsh* ins = new(alloc) MLsh(left, right);
- ins->specializeAsInt32();
+MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+ MLsh* ins = new(alloc) MLsh(left, right, type);
+ ins->specializeAs(type);
return ins;
}
MRsh*
MRsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
{
- return new(alloc) MRsh(left, right);
+ return new(alloc) MRsh(left, right, MIRType_Int32);
}
MRsh*
-MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
- MRsh* ins = new(alloc) MRsh(left, right);
- ins->specializeAsInt32();
+MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+ MRsh* ins = new(alloc) MRsh(left, right, type);
+ ins->specializeAs(type);
return ins;
}
MUrsh*
MUrsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
{
- return new(alloc) MUrsh(left, right);
+ return new(alloc) MUrsh(left, right, MIRType_Int32);
}
MUrsh*
-MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
-{
- MUrsh* ins = new(alloc) MUrsh(left, right);
- ins->specializeAsInt32();
+MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
+{
+ MUrsh* ins = new(alloc) MUrsh(left, right, type);
+ ins->specializeAs(type);
// Since Ion has no UInt32 type, we use Int32 and we have a special
// exception to the type rules: we can return values in
// (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
// without bailing out. This is necessary because Ion has no UInt32
// type and we can't have bailouts in asm.js code.
ins->bailoutsDisabled_ = true;
@@ -3940,16 +3943,31 @@ MCompare::tryFold(bool* result)
return true;
}
return false;
}
return false;
}
+template <typename T>
+static bool
+FoldComparison(JSOp op, T left, T right)
+{
+ switch (op) {
+ case JSOP_LT: return left < right;
+ case JSOP_LE: return left <= right;
+ case JSOP_GT: return left > right;
+ case JSOP_GE: return left >= right;
+ case JSOP_STRICTEQ: case JSOP_EQ: return left == right;
+ case JSOP_STRICTNE: case JSOP_NE: return left != right;
+ default: MOZ_CRASH("Unexpected op.");
+ }
+}
+
bool
MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result)
{
if (type() != MIRType_Boolean && type() != MIRType_Int32)
return false;
MDefinition* left = getOperand(0);
MDefinition* right = getOperand(1);
@@ -4041,106 +4059,41 @@ MCompare::evaluateConstantOperands(TempA
MConstant* lhs = left->toConstant();
MConstant* rhs = right->toConstant();
// Fold away some String equality comparisons.
if (lhs->type() == MIRType_String && rhs->type() == MIRType_String) {
int32_t comp = 0; // Default to equal.
if (left != right)
comp = CompareAtoms(&lhs->toString()->asAtom(), &rhs->toString()->asAtom());
-
- switch (jsop_) {
- case JSOP_LT:
- *result = (comp < 0);
- break;
- case JSOP_LE:
- *result = (comp <= 0);
- break;
- case JSOP_GT:
- *result = (comp > 0);
- break;
- case JSOP_GE:
- *result = (comp >= 0);
- break;
- case JSOP_STRICTEQ: // Fall through.
- case JSOP_EQ:
- *result = (comp == 0);
- break;
- case JSOP_STRICTNE: // Fall through.
- case JSOP_NE:
- *result = (comp != 0);
- break;
- default:
- MOZ_CRASH("Unexpected op.");
- }
-
+ *result = FoldComparison(jsop_, comp, 0);
return true;
}
if (compareType_ == Compare_UInt32) {
- uint32_t lhsUint = uint32_t(lhs->toInt32());
- uint32_t rhsUint = uint32_t(rhs->toInt32());
-
- switch (jsop_) {
- case JSOP_LT:
- *result = (lhsUint < rhsUint);
- break;
- case JSOP_LE:
- *result = (lhsUint <= rhsUint);
- break;
- case JSOP_GT:
- *result = (lhsUint > rhsUint);
- break;
- case JSOP_GE:
- *result = (lhsUint >= rhsUint);
- break;
- case JSOP_STRICTEQ: // Fall through.
- case JSOP_EQ:
- *result = (lhsUint == rhsUint);
- break;
- case JSOP_STRICTNE: // Fall through.
- case JSOP_NE:
- *result = (lhsUint != rhsUint);
- break;
- default:
- MOZ_CRASH("Unexpected op.");
- }
-
+ *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()), uint32_t(rhs->toInt32()));
+ return true;
+ }
+
+ if (compareType_ == Compare_Int64) {
+ *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
return true;
}
- if (!lhs->isTypeRepresentableAsDouble() || !rhs->isTypeRepresentableAsDouble())
- return false;
-
- switch (jsop_) {
- case JSOP_LT:
- *result = (lhs->numberToDouble() < rhs->numberToDouble());
- break;
- case JSOP_LE:
- *result = (lhs->numberToDouble() <= rhs->numberToDouble());
- break;
- case JSOP_GT:
- *result = (lhs->numberToDouble() > rhs->numberToDouble());
- break;
- case JSOP_GE:
- *result = (lhs->numberToDouble() >= rhs->numberToDouble());
- break;
- case JSOP_STRICTEQ: // Fall through.
- case JSOP_EQ:
- *result = (lhs->numberToDouble() == rhs->numberToDouble());
- break;
- case JSOP_STRICTNE: // Fall through.
- case JSOP_NE:
- *result = (lhs->numberToDouble() != rhs->numberToDouble());
- break;
- default:
- return false;
- }
-
- return true;
+ if (compareType_ == Compare_UInt64) {
+ *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()), uint64_t(rhs->toInt64()));
+ return true;
+ }
+
+ if (lhs->isTypeRepresentableAsDouble() && rhs->isTypeRepresentableAsDouble()) {
+ *result = FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
+ return true;
+ }
+
+ return false;
}
MDefinition*
MCompare::foldsTo(TempAllocator& alloc)
{
bool result;
if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1255,37 +1255,19 @@ class MVariadicT : public T
};
typedef MVariadicT<MInstruction> MVariadicInstruction;
// Generates an LSnapshot without further effect.
class MStart : public MNullaryInstruction
{
public:
- enum StartType {
- StartType_Default,
- StartType_Osr
- };
-
- private:
- StartType startType_;
-
- private:
- explicit MStart(StartType startType)
- : startType_(startType)
- { }
-
- public:
INSTRUCTION_HEADER(Start)
- static MStart* New(TempAllocator& alloc, StartType startType) {
- return new(alloc) MStart(startType);
- }
-
- StartType startType() {
- return startType_;
+ static MStart* New(TempAllocator& alloc) {
+ return new(alloc) MStart();
}
};
// Instruction marking on entrypoint for on-stack replacement.
// OSR may occur at loop headers (at JSOP_TRACE).
// There is at most one MOsrEntry per MIRGraph.
class MOsrEntry : public MNullaryInstruction
{
@@ -4360,16 +4342,22 @@ class MCompare
Compare_Int32,
Compare_Int32MaybeCoerceBoth,
Compare_Int32MaybeCoerceLHS,
Compare_Int32MaybeCoerceRHS,
// Int32 compared as unsigneds
Compare_UInt32,
+ // Int64 compared to Int64.
+ Compare_Int64,
+
+ // Int64 compared as unsigneds.
+ Compare_UInt64,
+
// Double compared to Double
Compare_Double,
Compare_DoubleMaybeCoerceLHS,
Compare_DoubleMaybeCoerceRHS,
// Float compared to Float
Compare_Float32,
@@ -5564,25 +5552,26 @@ class MToId
}
};
class MBinaryBitwiseInstruction
: public MBinaryInstruction,
public BitwisePolicy::Data
{
protected:
- MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right)
+ MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right, MIRType type)
: MBinaryInstruction(left, right), maskMatchesLeftRange(false),
maskMatchesRightRange(false)
{
- setResultType(MIRType_Int32);
- setMovable();
- }
-
- void specializeAsInt32();
+ MOZ_ASSERT(type == MIRType_Int32 || type == MIRType_Int64);
+ setResultType(type);
+ setMovable();
+ }
+
+ void specializeAs(MIRType type);
bool maskMatchesLeftRange;
bool maskMatchesRightRange;
public:
MDefinition* foldsTo(TempAllocator& alloc) override;
MDefinition* foldUnnecessaryBitop();
virtual MDefinition* foldIfZero(size_t operand) = 0;
virtual MDefinition* foldIfNegOne(size_t operand) = 0;
@@ -5605,24 +5594,25 @@ class MBinaryBitwiseInstruction
return AliasSet::None();
}
TruncateKind operandTruncateKind(size_t index) const override;
};
class MBitAnd : public MBinaryBitwiseInstruction
{
- MBitAnd(MDefinition* left, MDefinition* right)
- : MBinaryBitwiseInstruction(left, right)
+ MBitAnd(MDefinition* left, MDefinition* right, MIRType type)
+ : MBinaryBitwiseInstruction(left, right, type)
{ }
public:
INSTRUCTION_HEADER(BitAnd)
static MBitAnd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
- static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+ static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type);
MDefinition* foldIfZero(size_t operand) override {
return getOperand(operand); // 0 & x => 0;
}
MDefinition* foldIfNegOne(size_t operand) override {
return getOperand(1 - operand); // x & -1 => x
}
MDefinition* foldIfEqual() override {
@@ -5639,24 +5629,25 @@ class MBitAnd : public MBinaryBitwiseIns
return specialization_ != MIRType_None;
}
ALLOW_CLONE(MBitAnd)
};
class MBitOr : public MBinaryBitwiseInstruction
{
- MBitOr(MDefinition* left, MDefinition* right)
- : MBinaryBitwiseInstruction(left, right)
+ MBitOr(MDefinition* left, MDefinition* right, MIRType type)
+ : MBinaryBitwiseInstruction(left, right, type)
{ }
public:
INSTRUCTION_HEADER(BitOr)
static MBitOr* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
- static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+ static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type);
MDefinition* foldIfZero(size_t operand) override {
return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th
}
MDefinition* foldIfNegOne(size_t operand) override {
return getOperand(operand); // x | -1 => -1
}
MDefinition* foldIfEqual() override {
@@ -5671,24 +5662,25 @@ class MBitOr : public MBinaryBitwiseInst
return specialization_ != MIRType_None;
}
ALLOW_CLONE(MBitOr)
};
class MBitXor : public MBinaryBitwiseInstruction
{
- MBitXor(MDefinition* left, MDefinition* right)
- : MBinaryBitwiseInstruction(left, right)
+ MBitXor(MDefinition* left, MDefinition* right, MIRType type)
+ : MBinaryBitwiseInstruction(left, right, type)
{ }
public:
INSTRUCTION_HEADER(BitXor)
static MBitXor* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
- static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+ static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type);
MDefinition* foldIfZero(size_t operand) override {
return getOperand(1 - operand); // 0 ^ x => x
}
MDefinition* foldIfNegOne(size_t operand) override {
return this;
}
MDefinition* foldIfEqual() override {
@@ -5706,18 +5698,18 @@ class MBitXor : public MBinaryBitwiseIns
ALLOW_CLONE(MBitXor)
};
class MShiftInstruction
: public MBinaryBitwiseInstruction
{
protected:
- MShiftInstruction(MDefinition* left, MDefinition* right)
- : MBinaryBitwiseInstruction(left, right)
+ MShiftInstruction(MDefinition* left, MDefinition* right, MIRType type)
+ : MBinaryBitwiseInstruction(left, right, type)
{ }
public:
MDefinition* foldIfNegOne(size_t operand) override {
return this;
}
MDefinition* foldIfEqual() override {
return this;
@@ -5725,24 +5717,25 @@ class MShiftInstruction
MDefinition* foldIfAllBitsSet(size_t operand) override {
return this;
}
virtual void infer(BaselineInspector* inspector, jsbytecode* pc) override;
};
class MLsh : public MShiftInstruction
{
- MLsh(MDefinition* left, MDefinition* right)
- : MShiftInstruction(left, right)
+ MLsh(MDefinition* left, MDefinition* right, MIRType type)
+ : MShiftInstruction(left, right, type)
{ }
public:
INSTRUCTION_HEADER(Lsh)
static MLsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
- static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+ static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type);
MDefinition* foldIfZero(size_t operand) override {
// 0 << x => 0
// x << 0 => x
return getOperand(0);
}
void computeRange(TempAllocator& alloc) override;
@@ -5751,24 +5744,25 @@ class MLsh : public MShiftInstruction
return specialization_ != MIRType_None;
}
ALLOW_CLONE(MLsh)
};
class MRsh : public MShiftInstruction
{
- MRsh(MDefinition* left, MDefinition* right)
- : MShiftInstruction(left, right)
+ MRsh(MDefinition* left, MDefinition* right, MIRType type)
+ : MShiftInstruction(left, right, type)
{ }
public:
INSTRUCTION_HEADER(Rsh)
static MRsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
- static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+ static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type);
MDefinition* foldIfZero(size_t operand) override {
// 0 >> x => 0
// x >> 0 => x
return getOperand(0);
}
void computeRange(TempAllocator& alloc) override;
@@ -5779,25 +5773,26 @@ class MRsh : public MShiftInstruction
ALLOW_CLONE(MRsh)
};
class MUrsh : public MShiftInstruction
{
bool bailoutsDisabled_;
- MUrsh(MDefinition* left, MDefinition* right)
- : MShiftInstruction(left, right),
+ MUrsh(MDefinition* left, MDefinition* right, MIRType type)
+ : MShiftInstruction(left, right, type),
bailoutsDisabled_(false)
{ }
public:
INSTRUCTION_HEADER(Ursh)
static MUrsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
- static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
+ static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
+ MIRType type);
MDefinition* foldIfZero(size_t operand) override {
// 0 >>> x => 0
if (operand == 0)
return getOperand(0);
return this;
}
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -598,18 +598,16 @@ MBasicBlock::shimmySlots(int discardDept
slots_[stackPosition_ + i] = slots_[stackPosition_ + i + 1];
--stackPosition_;
}
bool
MBasicBlock::linkOsrValues(MStart* start)
{
- MOZ_ASSERT(start->startType() == MStart::StartType_Osr);
-
MResumePoint* res = start->resumePoint();
for (uint32_t i = 0; i < stackDepth(); i++) {
MDefinition* def = slots_[i];
MInstruction* cloneRp = nullptr;
if (i == info().scopeChainSlot()) {
if (def->isOsrScopeChain())
cloneRp = def->toOsrScopeChain();
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -688,16 +688,18 @@ class MacroAssembler : public MacroAssem
inline void and32(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
inline void and32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
inline void and32(const Address& src, Register dest) PER_SHARED_ARCH;
inline void andPtr(Register src, Register dest) PER_ARCH;
inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
+ inline void or64(Imm64 imm, Register64 dest) PER_ARCH;
+ inline void xor64(Imm64 imm, Register64 dest) PER_ARCH;
inline void or32(Register src, Register dest) PER_SHARED_ARCH;
inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
inline void orPtr(Register src, Register dest) PER_ARCH;
inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -1053,23 +1053,27 @@ IonBuilder::startTrackingOptimizations()
BytecodeSite* site = maybeTrackedOptimizationSite(current->trackedSite()->pc());
if (!site) {
site = current->trackedSite();
site->setOptimizations(new(alloc()) TrackedOptimizations(alloc()));
// OOMs are handled as if optimization tracking were turned off.
if (!trackedOptimizationSites_.append(site))
site = nullptr;
- } else {
+ } else if (site->hasOptimizations()) {
// The same bytecode may be visited multiple times (see
// restartLoop). Only the last time matters, so clear any previous
// tracked optimizations.
site->optimizations()->clear();
}
+ // The case of !site->hasOptimizations() means we had an OOM when
+ // previously attempting to track optimizations. Leave
+ // site->optimizations_ nullptr to leave optimization tracking off.
+
if (site)
current->updateTrackedSite(site);
}
}
void
IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType,
TemporaryTypeSet* typeSet)
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -628,18 +628,23 @@ Range::Range(const MDefinition* def)
}
}
// As a special case, MUrsh is permitted to claim a result type of
// MIRType_Int32 while actually returning values in [0,UINT32_MAX] without
// bailouts. If range analysis hasn't ruled out values in
// (INT32_MAX,UINT32_MAX], set the range to be conservatively correct for
// use as either a uint32 or an int32.
- if (!hasInt32UpperBound() && def->isUrsh() && def->toUrsh()->bailoutsDisabled())
+ if (!hasInt32UpperBound() &&
+ def->isUrsh() &&
+ def->toUrsh()->bailoutsDisabled() &&
+ def->type() != MIRType_Int64)
+ {
lower_ = INT32_MIN;
+ }
assertInvariants();
}
static uint16_t
ExponentImpliedByDouble(double d)
{
// Handle the special values.
@@ -1311,38 +1316,47 @@ void
MClampToUint8::computeRange(TempAllocator& alloc)
{
setRange(Range::NewUInt32Range(alloc, 0, 255));
}
void
MBitAnd::computeRange(TempAllocator& alloc)
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range left(getOperand(0));
Range right(getOperand(1));
left.wrapAroundToInt32();
right.wrapAroundToInt32();
setRange(Range::and_(alloc, &left, &right));
}
void
MBitOr::computeRange(TempAllocator& alloc)
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range left(getOperand(0));
Range right(getOperand(1));
left.wrapAroundToInt32();
right.wrapAroundToInt32();
setRange(Range::or_(alloc, &left, &right));
}
void
MBitXor::computeRange(TempAllocator& alloc)
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range left(getOperand(0));
Range right(getOperand(1));
left.wrapAroundToInt32();
right.wrapAroundToInt32();
setRange(Range::xor_(alloc, &left, &right));
}
@@ -1353,16 +1367,19 @@ MBitNot::computeRange(TempAllocator& all
op.wrapAroundToInt32();
setRange(Range::not_(alloc, &op));
}
void
MLsh::computeRange(TempAllocator& alloc)
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range left(getOperand(0));
Range right(getOperand(1));
left.wrapAroundToInt32();
MConstant* rhsConst = getOperand(1)->maybeConstantValue();
if (rhsConst && rhsConst->type() == MIRType_Int32) {
int32_t c = rhsConst->toInt32();
setRange(Range::lsh(alloc, &left, c));
@@ -1371,16 +1388,19 @@ MLsh::computeRange(TempAllocator& alloc)
right.wrapAroundToShiftCount();
setRange(Range::lsh(alloc, &left, &right));
}
void
MRsh::computeRange(TempAllocator& alloc)
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range left(getOperand(0));
Range right(getOperand(1));
left.wrapAroundToInt32();
MConstant* rhsConst = getOperand(1)->maybeConstantValue();
if (rhsConst && rhsConst->type() == MIRType_Int32) {
int32_t c = rhsConst->toInt32();
setRange(Range::rsh(alloc, &left, c));
@@ -1389,16 +1409,19 @@ MRsh::computeRange(TempAllocator& alloc)
right.wrapAroundToShiftCount();
setRange(Range::rsh(alloc, &left, &right));
}
void
MUrsh::computeRange(TempAllocator& alloc)
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range left(getOperand(0));
Range right(getOperand(1));
// ursh can be thought of as converting its left operand to uint32, or it
// can be thought of as converting its left operand to int32, and then
// reinterpreting the int32 bits as a uint32 value. Both approaches yield
// the same result. Since we lack support for full uint32 ranges, we use
// the second interpretation, though it does cause us to be conservative.
@@ -3318,16 +3341,19 @@ MPowHalf::collectRangeInfoPreTrunc()
operandIsNeverNegativeZero_ = true;
if (!inputRange.canBeNaN())
operandIsNeverNaN_ = true;
}
void
MUrsh::collectRangeInfoPreTrunc()
{
+ if (specialization_ == MIRType_Int64)
+ return;
+
Range lhsRange(lhs()), rhsRange(rhs());
// As in MUrsh::computeRange(), convert the inputs.
lhsRange.wrapAroundToInt32();
rhsRange.wrapAroundToShiftCount();
// If the most significant bit of our result is always going to be zero,
// we can optimize by disabling bailout checks for enforcing an int32 range.
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -159,16 +159,23 @@ LIRGeneratorARM::lowerForALU(LInstructio
// MulI, but only for bail out paths so useAtStart when no bailouts.
ins->setOperand(0, ins->snapshot() ? useRegister(lhs) : useRegisterAtStart(lhs));
ins->setOperand(1, ins->snapshot() ? useRegisterOrConstant(rhs) :
useRegisterOrConstantAtStart(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
void
+LIRGeneratorARM::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ MOZ_CRASH("NYI");
+}
+
+void
LIRGeneratorARM::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input)
{
ins->setOperand(0, useRegisterAtStart(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template<size_t Temps>
void
@@ -226,16 +233,23 @@ void
LIRGeneratorARM::lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
define(ins, mir);
}
void
+LIRGeneratorARM::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ MOZ_CRASH("NYI");
+}
+
+void
LIRGeneratorARM::lowerDivI(MDiv* div)
{
if (div->isUnsigned()) {
lowerUDiv(div);
return;
}
// Division instructions are slow. Division by constant denominators can be
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -42,16 +42,21 @@ class LIRGeneratorARM : public LIRGenera
MDefinition* rhs);
void lowerUrshD(MUrsh* mir);
void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input);
void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
+ void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+ MDefinition* lhs, MDefinition* rhs);
+ void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* src);
template<size_t Temps>
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/arm/MacroAssembler-arm-inl.h
+++ b/js/src/jit/arm/MacroAssembler-arm-inl.h
@@ -81,16 +81,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
void
MacroAssembler::and64(Imm64 imm, Register64 dest)
{
and32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
and32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
}
void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+ or32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+ or32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+ xor32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+ xor32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
MacroAssembler::or32(Register src, Register dest)
{
ma_orr(src, dest);
}
void
MacroAssembler::or32(Imm32 imm, Register dest)
{
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -83,16 +83,30 @@ LIRGeneratorARM64::lowerForFPU(LInstruct
}
template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
template void LIRGeneratorARM64::lowerForFPU(LInstructionHelper<1, 2, 1>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void
+LIRGeneratorARM64::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM64::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ MOZ_CRASH("NYI");
+}
+
+void
LIRGeneratorARM64::lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
MDefinition* lhs, MDefinition* rhs)
{
MOZ_CRASH("lowerForBitAndAndBranch");
}
void
LIRGeneratorARM64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -43,16 +43,21 @@ class LIRGeneratorARM64 : public LIRGene
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
MDefinition* rhs);
void lowerUrshD(MUrsh* mir);
void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
+ void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+ MDefinition* lhs, MDefinition* rhs);
+ void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
template <size_t Temps>
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h
@@ -91,16 +91,34 @@ MacroAssembler::and64(Imm64 imm, Registe
{
vixl::UseScratchRegisterScope temps(this);
const Register scratch = temps.AcquireX().asUnsized();
mov(ImmWord(imm.value), scratch);
andPtr(scratch, dest.reg);
}
void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+ vixl::UseScratchRegisterScope temps(this);
+ const Register scratch = temps.AcquireX().asUnsized();
+ mov(ImmWord(imm.value), scratch);
+ orPtr(scratch, dest.reg);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+ vixl::UseScratchRegisterScope temps(this);
+ const Register scratch = temps.AcquireX().asUnsized();
+ mov(ImmWord(imm.value), scratch);
+ xorPtr(scratch, dest.reg);
+}
+
+void
MacroAssembler::or32(Imm32 imm, Register dest)
{
Orr(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
}
void
MacroAssembler::or32(Register src, Register dest)
{
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -50,16 +50,30 @@ LIRGeneratorMIPSShared::lowerForALU(LIns
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegister(lhs));
ins->setOperand(1, useRegisterOrConstant(rhs));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
void
+LIRGeneratorMIPSShared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ MOZ_CRASH("NYI");
+}
+
+void
LIRGeneratorMIPSShared::lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input)
{
ins->setOperand(0, useRegister(input));
define(ins, mir, LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::REGISTER));
}
template<size_t Temps>
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -32,16 +32,21 @@ class LIRGeneratorMIPSShared : public LI
MDefinition* rhs);
void lowerUrshD(MUrsh* mir);
void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input);
void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
+ void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+ MDefinition* lhs, MDefinition* rhs);
+ void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
void lowerForFPU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* src);
template<size_t Temps>
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs)
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -48,16 +48,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
void
MacroAssembler::and64(Imm64 imm, Register64 dest)
{
and32(Imm32(imm.value & LOW_32_MASK), dest.low);
and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
}
void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+ or32(Imm32(imm.value & LOW_32_MASK), dest.low);
+ or32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+ xor32(Imm32(imm.value & LOW_32_MASK), dest.low);
+ xor32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
+}
+
+void
MacroAssembler::orPtr(Register src, Register dest)
{
ma_or(dest, src);
}
void
MacroAssembler::orPtr(Imm32 imm, Register dest)
{
--- a/js/src/jit/mips64/MacroAssembler-mips64-inl.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64-inl.h
@@ -46,16 +46,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
void
MacroAssembler::and64(Imm64 imm, Register64 dest)
{
ma_li(ScratchRegister, ImmWord(imm.value));
ma_and(dest.reg, ScratchRegister);
}
void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+ ma_li(ScratchRegister, ImmWord(imm.value));
+ ma_or(dest.reg, ScratchRegister);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+ ma_li(ScratchRegister, ImmWord(imm.value));
+ ma_xor(dest.reg, ScratchRegister);
+}
+
+void
MacroAssembler::orPtr(Register src, Register dest)
{
ma_or(dest, src);
}
void
MacroAssembler::orPtr(Imm32 imm, Register dest)
{
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -33,16 +33,20 @@ class LIRGeneratorNone : public LIRGener
void lowerForShift(LInstructionHelper<1, 2, 0>*, MDefinition*, MDefinition*, MDefinition*) {
MOZ_CRASH();
}
void lowerUrshD(MUrsh*) { MOZ_CRASH(); }
template <typename T>
void lowerForALU(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
template <typename T>
void lowerForFPU(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
+ template <typename T>
+ void lowerForALUInt64(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
+ template <typename T>
+ void lowerForShiftInt64(T, MDefinition*, MDefinition*, MDefinition* v = nullptr) { MOZ_CRASH(); }
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs) {
MOZ_CRASH();
}
void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs) {
MOZ_CRASH();
}
--- a/js/src/jit/shared/CodeGenerator-shared-inl.h
+++ b/js/src/jit/shared/CodeGenerator-shared-inl.h
@@ -20,16 +20,26 @@ ToInt32(const LAllocation* a)
{
if (a->isConstantValue())
return a->toConstant()->toInt32();
if (a->isConstantIndex())
return a->toConstantIndex()->index();
MOZ_CRASH("this is not a constant!");
}
+static inline int64_t
+ToInt64(const LAllocation* a)
+{
+ if (a->isConstantValue())
+ return a->toConstant()->toInt64();
+ if (a->isConstantIndex())
+ return a->toConstantIndex()->index();
+ MOZ_CRASH("this is not a constant!");
+}
+
static inline double
ToDouble(const LAllocation* a)
{
return a->toConstant()->numberToDouble();
}
static inline Register
ToRegister(const LAllocation& a)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -2257,16 +2257,86 @@ class LCompare : public LInstructionHelp
MCompare* mir() {
return mir_->toCompare();
}
const char* extraName() const {
return CodeName[jsop_];
}
};
+class LCompare64 : public LInstructionHelper<1, 2 * INT64_PIECES, 0>
+{
+ JSOp jsop_;
+
+ public:
+ LIR_HEADER(Compare64)
+
+ static const size_t Lhs = 0;
+ static const size_t Rhs = INT64_PIECES;
+
+ LCompare64(JSOp jsop, const LInt64Allocation& left, const LInt64Allocation& right)
+ : jsop_(jsop)
+ {
+ setInt64Operand(Lhs, left);
+ setInt64Operand(Rhs, right);
+ }
+
+ JSOp jsop() const {
+ return jsop_;
+ }
+ MCompare* mir() {
+ return mir_->toCompare();
+ }
+ const char* extraName() const {
+ return CodeName[jsop_];
+ }
+};
+
+class LCompare64AndBranch : public LControlInstructionHelper<2, 2 * INT64_PIECES, 0>
+{
+ MCompare* cmpMir_;
+ JSOp jsop_;
+
+ public:
+ LIR_HEADER(Compare64AndBranch)
+
+ static const size_t Lhs = 0;
+ static const size_t Rhs = INT64_PIECES;
+
+ LCompare64AndBranch(MCompare* cmpMir, JSOp jsop,
+ const LInt64Allocation& left, const LInt64Allocation& right,
+ MBasicBlock* ifTrue, MBasicBlock* ifFalse)
+ : cmpMir_(cmpMir), jsop_(jsop)
+ {
+ setInt64Operand(Lhs, left);
+ setInt64Operand(Rhs, right);
+ setSuccessor(0, ifTrue);
+ setSuccessor(1, ifFalse);
+ }
+
+ JSOp jsop() const {
+ return jsop_;
+ }
+ MBasicBlock* ifTrue() const {
+ return getSuccessor(0);
+ }
+ MBasicBlock* ifFalse() const {
+ return getSuccessor(1);
+ }
+ MTest* mir() const {
+ return mir_->toTest();
+ }
+ MCompare* cmpMir() const {
+ return cmpMir_;
+ }
+ const char* extraName() const {
+ return CodeName[jsop_];
+ }
+};
+
// Compares two integral values of the same JS type, either integer or object.
// For objects, both operands are in registers.
class LCompareAndBranch : public LControlInstructionHelper<2, 2, 0>
{
MCompare* cmpMir_;
JSOp jsop_;
public:
@@ -2873,16 +2943,39 @@ class LBitOpI : public LInstructionHelpe
return CodeName[op_];
}
JSOp bitop() const {
return op_;
}
};
+class LBitOpI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+ JSOp op_;
+
+ public:
+ LIR_HEADER(BitOpI64)
+
+ static const size_t Lhs = 0;
+ static const size_t Rhs = INT64_PIECES;
+
+ explicit LBitOpI64(JSOp op)
+ : op_(op)
+ { }
+
+ const char* extraName() const {
+ return CodeName[op_];
+ }
+
+ JSOp bitop() const {
+ return op_;
+ }
+};
+
// Call a VM function to perform a bitwise operation.
class LBitOpV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
{
JSOp jsop_;
public:
LIR_HEADER(BitOpV)
@@ -2926,16 +3019,40 @@ class LShiftI : public LBinaryMath<0>
return mir_->toInstruction();
}
const char* extraName() const {
return CodeName[op_];
}
};
+class LShiftI64 : public LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>
+{
+ JSOp op_;
+
+ public:
+ LIR_HEADER(ShiftI64)
+
+ explicit LShiftI64(JSOp op)
+ : op_(op)
+ { }
+
+ JSOp bitop() {
+ return op_;
+ }
+
+ MInstruction* mir() {
+ return mir_->toInstruction();
+ }
+
+ const char* extraName() const {
+ return CodeName[op_];
+ }
+};
+
class LUrshD : public LBinaryMath<1>
{
public:
LIR_HEADER(UrshD)
LUrshD(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
setOperand(0, lhs);
setOperand(1, rhs);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -92,31 +92,35 @@
_(CreateArgumentsObject) \
_(GetArgumentsObjectArg) \
_(SetArgumentsObjectArg) \
_(ReturnFromCtor) \
_(ComputeThis) \
_(BitNotI) \
_(BitNotV) \
_(BitOpI) \
+ _(BitOpI64) \
_(BitOpV) \
_(ShiftI) \
+ _(ShiftI64) \
_(UrshD) \
_(Return) \
_(Throw) \
_(Phi) \
_(TestIAndBranch) \
_(TestDAndBranch) \
_(TestFAndBranch) \
_(TestVAndBranch) \
_(TestOAndBranch) \
_(FunctionDispatch) \
_(ObjectGroupDispatch) \
_(Compare) \
_(CompareAndBranch) \
+ _(Compare64) \
+ _(Compare64AndBranch) \
_(CompareD) \
_(CompareDAndBranch) \
_(CompareF) \
_(CompareFAndBranch) \
_(CompareS) \
_(CompareStrictS) \
_(CompareB) \
_(CompareBAndBranch) \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -24,16 +24,17 @@ LIRGeneratorShared::emitAtUses(MInstruct
}
LUse
LIRGeneratorShared::use(MDefinition* mir, LUse policy)
{
// It is illegal to call use() on an instruction with two defs.
#if BOX_PIECES > 1
MOZ_ASSERT(mir->type() != MIRType_Value);
+ MOZ_ASSERT(mir->type() != MIRType_Int64);
#endif
ensureDefined(mir);
policy.setVirtualRegister(mir->virtualRegister());
return policy;
}
template <size_t X> void
LIRGeneratorShared::define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
@@ -82,21 +83,51 @@ LIRGeneratorShared::defineReuseInput(LIn
LDefinition def(type, LDefinition::MUST_REUSE_INPUT);
def.setReusedInput(operand);
define(lir, mir, def);
}
template <size_t Ops, size_t Temps> void
+LIRGeneratorShared::defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
+ MDefinition* mir, uint32_t operand)
+{
+ // The input should be used at the start of the instruction, to avoid moves.
+ MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
+ MOZ_ASSERT(!lir->isCall());
+
+ uint32_t vreg = getVirtualRegister();
+
+ LDefinition def1(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
+ def1.setReusedInput(operand);
+ lir->setDef(0, def1);
+ lir->getDef(0)->setVirtualRegister(vreg);
+
+#if JS_BITS_PER_WORD == 32
+ getVirtualRegister();
+ LDefinition def2(LDefinition::GENERAL, LDefinition::MUST_REUSE_INPUT);
+ def2.setReusedInput(operand + 1);
+ lir->setDef(1, def2);
+ lir->getDef(1)->setVirtualRegister(vreg + 1);
+#endif
+
+ lir->setMir(mir);
+ mir->setVirtualRegister(vreg);
+ add(lir);
+
+}
+
+template <size_t Ops, size_t Temps> void
LIRGeneratorShared::defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
LDefinition::Policy policy)
{
// Call instructions should use defineReturn.
MOZ_ASSERT(!lir->isCall());
+ MOZ_ASSERT(mir->type() == MIRType_Value);
uint32_t vreg = getVirtualRegister();
#if defined(JS_NUNBOX32)
lir->setDef(0, LDefinition(vreg + VREG_TYPE_OFFSET, LDefinition::TYPE, policy));
lir->setDef(1, LDefinition(vreg + VREG_DATA_OFFSET, LDefinition::PAYLOAD, policy));
getVirtualRegister();
#elif defined(JS_PUNBOX64)
@@ -109,16 +140,17 @@ LIRGeneratorShared::defineBox(LInstructi
}
template <size_t Ops, size_t Temps> void
LIRGeneratorShared::defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
LDefinition::Policy policy)
{
// Call instructions should use defineReturn.
MOZ_ASSERT(!lir->isCall());
+ MOZ_ASSERT(mir->type() == MIRType_Int64);
uint32_t vreg = getVirtualRegister();
#if JS_BITS_PER_WORD == 32
lir->setDef(0, LDefinition(vreg, LDefinition::GENERAL, policy));
lir->setDef(1, LDefinition(vreg + 1, LDefinition::GENERAL, policy));
getVirtualRegister();
#else
@@ -659,12 +691,58 @@ LIRGeneratorShared::useBoxOrTypedOrConst
#if defined(JS_NUNBOX32)
return LBoxAllocation(useRegister(mir), LAllocation());
#else
return LBoxAllocation(useRegister(mir));
#endif
}
+LInt64Allocation
+LIRGeneratorShared::useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart)
+{
+ MOZ_ASSERT(mir->type() == MIRType_Int64);
+
+ ensureDefined(mir);
+
+ uint32_t vreg = mir->virtualRegister();
+#if JS_BITS_PER_WORD == 32
+ return LInt64Allocation(LUse(vreg + 1, policy, useAtStart),
+ LUse(vreg, policy, useAtStart));
+#else
+ return LInt64Allocation(LUse(vreg, policy, useAtStart));
+#endif
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64(MDefinition* mir, bool useAtStart)
+{
+ // On 32-bit platforms, always load the value in registers.
+#if JS_BITS_PER_WORD == 32
+ return useInt64(mir, LUse::REGISTER, useAtStart);
+#else
+ return useInt64(mir, LUse::ANY, useAtStart);
+#endif
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64Register(MDefinition* mir, bool useAtStart)
+{
+ return useInt64(mir, LUse::REGISTER, useAtStart);
+}
+
+LInt64Allocation
+LIRGeneratorShared::useInt64OrConstant(MDefinition* mir, bool useAtStart)
+{
+ if (mir->isConstant()) {
+#if defined(JS_NUNBOX32)
+ return LInt64Allocation(LAllocation(mir->toConstant()), LAllocation());
+#else
+ return LInt64Allocation(LAllocation(mir->toConstant()));
+#endif
+ }
+ return useInt64(mir, useAtStart);
+}
+
} // namespace jit
} // namespace js
#endif /* jit_shared_Lowering_shared_inl_h */
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -158,24 +158,41 @@ class LIRGeneratorShared : public MDefin
LDefinition::Policy policy = LDefinition::REGISTER);
template <size_t X>
inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
const LDefinition& def);
template <size_t Ops, size_t Temps>
inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, uint32_t operand);
+ template <size_t Ops, size_t Temps>
+ inline void defineInt64ReuseInput(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir,
+ MDefinition* mir, uint32_t operand);
+
// Returns a box allocation for a Value-typed instruction.
inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
bool useAtStart = false);
// Returns a box allocation. The use is either typed, a Value, or
// a constant (if useConstant is true).
inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
+ // Returns an int64 allocation for an Int64-typed instruction.
+ inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
+ inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
+ inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
+ inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
+
+ LInt64Allocation useInt64RegisterAtStart(MDefinition* mir) {
+ return useInt64Register(mir, /* useAtStart = */ true);
+ }
+ LInt64Allocation useInt64OrConstantAtStart(MDefinition* mir) {
+ return useInt64OrConstant(mir, /* useAtStart = */ true);
+ }
+
// Rather than defining a new virtual register, sets |ins| to have the same
// virtual register as |as|.
inline void redefine(MDefinition* ins, MDefinition* as);
// Redefine a sin/cos call to sincos.
inline void redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func);
TempAllocator& alloc() const {
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -557,16 +557,25 @@ class Assembler : public AssemblerX86Sha
masm.shlq_ir(imm.value, dest.encoding());
}
void shrq(Imm32 imm, Register dest) {
masm.shrq_ir(imm.value, dest.encoding());
}
void sarq(Imm32 imm, Register dest) {
masm.sarq_ir(imm.value, dest.encoding());
}
+ void shlq_cl(Register dest) {
+ masm.shlq_CLr(dest.encoding());
+ }
+ void shrq_cl(Register dest) {
+ masm.shrq_CLr(dest.encoding());
+ }
+ void sarq_cl(Register dest) {
+ masm.sarq_CLr(dest.encoding());
+ }
void orq(Imm32 imm, Register dest) {
masm.orq_ir(imm.value, dest.encoding());
}
void orq(Register src, Register dest) {
masm.orq_rr(src.encoding(), dest.encoding());
}
void orq(const Operand& src, Register dest) {
switch (src.kind()) {
@@ -584,16 +593,31 @@ class Assembler : public AssemblerX86Sha
}
}
void xorq(Register src, Register dest) {
masm.xorq_rr(src.encoding(), dest.encoding());
}
void xorq(Imm32 imm, Register dest) {
masm.xorq_ir(imm.value, dest.encoding());
}
+ void xorq(const Operand& src, Register dest) {
+ switch (src.kind()) {
+ case Operand::REG:
+ masm.xorq_rr(src.reg(), dest.encoding());
+ break;
+ case Operand::MEM_REG_DISP:
+ masm.xorq_mr(src.disp(), src.base(), dest.encoding());
+ break;
+ case Operand::MEM_ADDRESS32:
+ masm.xorq_mr(src.address(), dest.encoding());
+ break;
+ default:
+ MOZ_CRASH("unexpected operand kind");
+ }
+ }
void imulq(Register src, Register dest) {
masm.imulq_rr(src.encoding(), dest.encoding());
}
void vcvtsi2sdq(Register src, FloatRegister dest) {
masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
}
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -108,16 +108,28 @@ class BaseAssemblerX64 : public BaseAsse
}
void orq_mr(const void* addr, RegisterID dst)
{
spew("orq %p, %s", addr, GPReg64Name(dst));
m_formatter.oneByteOp64(OP_OR_GvEv, addr, dst);
}
+ void xorq_mr(int32_t offset, RegisterID base, RegisterID dst)
+ {
+ spew("xorq " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+ m_formatter.oneByteOp64(OP_XOR_GvEv, offset, base, dst);
+ }
+
+ void xorq_mr(const void* addr, RegisterID dst)
+ {
+ spew("xorq %p, %s", addr, GPReg64Name(dst));
+ m_formatter.oneByteOp64(OP_XOR_GvEv, addr, dst);
+ }
+
void andq_ir(int32_t imm, RegisterID dst)
{
spew("andq $0x%" PRIx64 ", %s", int64_t(imm), GPReg64Name(dst));
if (CAN_SIGN_EXTEND_8_32(imm)) {
m_formatter.oneByteOp64(OP_GROUP1_EvIb, dst, GROUP1_OP_AND);
m_formatter.immediate8s(imm);
} else {
if (dst == rax)
@@ -222,16 +234,28 @@ class BaseAssemblerX64 : public BaseAsse
}
void sarq_CLr(RegisterID dst)
{
spew("sarq %%cl, %s", GPReg64Name(dst));
m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SAR);
}
+ void shlq_CLr(RegisterID dst)
+ {
+ spew("shlq %%cl, %s", GPReg64Name(dst));
+ m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SHL);
+ }
+
+ void shrq_CLr(RegisterID dst)
+ {
+ spew("shrq %%cl, %s", GPReg64Name(dst));
+ m_formatter.oneByteOp64(OP_GROUP2_EvCL, dst, GROUP2_OP_SHR);
+ }
+
void sarq_ir(int32_t imm, RegisterID dst)
{
MOZ_ASSERT(imm < 64);
spew("sarq $%d, %s", imm, GPReg64Name(dst));
if (imm == 1)
m_formatter.oneByteOp64(OP_GROUP2_Ev1, dst, GROUP2_OP_SAR);
else {
m_formatter.oneByteOp64(OP_GROUP2_EvIb, dst, GROUP2_OP_SAR);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -207,16 +207,126 @@ CodeGeneratorX64::visitCompareBitwiseAnd
MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
masm.cmpPtr(lhs.valueReg(), rhs.valueReg());
emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse());
}
void
+CodeGeneratorX64::visitCompare64(LCompare64* lir)
+{
+ MCompare* mir = lir->mir();
+ MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+ mir->compareType() == MCompare::Compare_UInt64);
+
+ Register lhs = ToRegister(lir->getOperand(0));
+ const LAllocation* rhs = lir->getOperand(1);
+
+ if (rhs->isConstant())
+ masm.cmpPtr(lhs, ImmWord(ToInt64(rhs)));
+ else
+ masm.cmpPtr(lhs, ToOperand(rhs));
+
+ bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+ masm.emitSet(JSOpToCondition(lir->jsop(), isSigned), ToRegister(lir->output()));
+}
+
+void
+CodeGeneratorX64::visitCompare64AndBranch(LCompare64AndBranch* lir)
+{
+ MCompare* mir = lir->cmpMir();
+ MOZ_ASSERT(mir->compareType() == MCompare::Compare_Int64 ||
+ mir->compareType() == MCompare::Compare_UInt64);
+
+ Register lhs = ToRegister(lir->getOperand(0));
+ const LAllocation* rhs = lir->getOperand(1);
+
+ if (rhs->isConstant())
+ masm.cmpPtr(lhs, ImmWord(ToInt64(rhs)));
+ else
+ masm.cmpPtr(lhs, ToOperand(rhs));
+
+ bool isSigned = mir->compareType() == MCompare::Compare_Int64;
+ emitBranch(JSOpToCondition(lir->jsop(), isSigned), lir->ifTrue(), lir->ifFalse());
+}
+
+void
+CodeGeneratorX64::visitBitOpI64(LBitOpI64* lir)
+{
+ Register lhs = ToRegister(lir->getOperand(0));
+ const LAllocation* rhs = lir->getOperand(1);
+
+ switch (lir->bitop()) {
+ case JSOP_BITOR:
+ if (rhs->isConstant())
+ masm.or64(Imm64(ToInt64(rhs)), Register64(lhs));
+ else
+ masm.orq(ToOperand(rhs), lhs);
+ break;
+ case JSOP_BITXOR:
+ if (rhs->isConstant())
+ masm.xor64(Imm64(ToInt64(rhs)), Register64(lhs));
+ else
+ masm.xorq(ToOperand(rhs), lhs);
+ break;
+ case JSOP_BITAND:
+ if (rhs->isConstant())
+ masm.and64(Imm64(ToInt64(rhs)), Register64(lhs));
+ else
+ masm.andq(ToOperand(rhs), lhs);
+ break;
+ default:
+ MOZ_CRASH("unexpected binary opcode");
+ }
+}
+
+void
+CodeGeneratorX64::visitShiftI64(LShiftI64* lir)
+{
+ Register lhs = ToRegister(lir->getOperand(0));
+ const LAllocation* rhs = lir->getOperand(1);
+
+ if (rhs->isConstant()) {
+ int32_t shift = ToInt32(rhs) & 0x3F;
+ switch (lir->bitop()) {
+ case JSOP_LSH:
+ if (shift)
+ masm.shlq(Imm32(shift), lhs);
+ break;
+ case JSOP_RSH:
+ if (shift)
+ masm.sarq(Imm32(shift), lhs);
+ break;
+ case JSOP_URSH:
+ if (shift)
+ masm.shrq(Imm32(shift), lhs);
+ break;
+ default:
+ MOZ_CRASH("Unexpected shift op");
+ }
+ } else {
+ MOZ_ASSERT(ToRegister(rhs) == ecx);
+ switch (lir->bitop()) {
+ case JSOP_LSH:
+ masm.shlq_cl(lhs);
+ break;
+ case JSOP_RSH:
+ masm.sarq_cl(lhs);
+ break;
+ case JSOP_URSH:
+ masm.shrq_cl(lhs);
+ break;
+ default:
+ MOZ_CRASH("Unexpected shift op");
+ }
+ }
+}
+
+void
CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
{
masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
}
void
CodeGeneratorX64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir)
{
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -37,16 +37,20 @@ class CodeGeneratorX64 : public CodeGene
public:
void visitValue(LValue* value);
void visitBox(LBox* box);
void visitUnbox(LUnbox* unbox);
void visitCompareB(LCompareB* lir);
void visitCompareBAndBranch(LCompareBAndBranch* lir);
void visitCompareBitwise(LCompareBitwise* lir);
void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
+ void visitCompare64(LCompare64* lir);
+ void visitCompare64AndBranch(LCompare64AndBranch* lir);
+ void visitBitOpI64(LBitOpI64* lir);
+ void visitShiftI64(LShiftI64* lir);
void visitTruncateDToInt32(LTruncateDToInt32* ins);
void visitTruncateFToInt32(LTruncateFToInt32* ins);
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
void visitAsmJSCall(LAsmJSCall* ins);
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -39,18 +39,47 @@ void
MacroAssembler::andPtr(Imm32 imm, Register dest)
{
andq(imm, dest);
}
void
MacroAssembler::and64(Imm64 imm, Register64 dest)
{
- movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
- andq(ScratchReg, dest.reg);
+ if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+ andq(Imm32(imm.value), dest.reg);
+ } else {
+ ScratchRegisterScope scratch(*this);
+ movq(ImmWord(uintptr_t(imm.value)), scratch);
+ andq(scratch, dest.reg);
+ }
+}
+
+void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+ if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+ orq(Imm32(imm.value), dest.reg);
+ } else {
+ ScratchRegisterScope scratch(*this);
+ movq(ImmWord(uintptr_t(imm.value)), scratch);
+ orq(scratch, dest.reg);
+ }
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+ if (INT32_MIN <= int64_t(imm.value) && int64_t(imm.value) <= INT32_MAX) {
+ xorq(Imm32(imm.value), dest.reg);
+ } else {
+ ScratchRegisterScope scratch(*this);
+ movq(ImmWord(uintptr_t(imm.value)), scratch);
+ xorq(scratch, dest.reg);
+ }
}
void
MacroAssembler::orPtr(Register src, Register dest)
{
orq(src, dest);
}
--- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp
@@ -76,32 +76,66 @@ LIRGeneratorX86Shared::lowerForShift(LIn
ins->setOperand(1, useOrConstantAtStart(rhs));
else
ins->setOperand(1, lhs != rhs ? useFixed(rhs, ecx) : useFixedAtStart(rhs, ecx));
defineReuseInput(ins, mir, 0);
}
void
+LIRGeneratorX86Shared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
+
+ // shift operator should be constant or in register ecx
+ // x86 can't shift a non-ecx register
+ if (rhs->isConstant()) {
+ ins->setOperand(INT64_PIECES, useOrConstantAtStart(rhs));
+ } else {
+ // The operands are int64, but we only care about the lower 32 bits of
+ // the RHS. On 32-bit, the code below will load that part in ecx and
+ // will discard the upper half.
+ ensureDefined(rhs);
+ bool useAtStart = (lhs == rhs);
+ LUse use(ecx, useAtStart);
+ use.setVirtualRegister(rhs->virtualRegister());
+ ins->setOperand(INT64_PIECES, use);
+ }
+
+ defineInt64ReuseInput(ins, mir, 0);
+}
+
+void
LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir,
MDefinition* input)
{
ins->setOperand(0, useRegisterAtStart(input));
defineReuseInput(ins, mir, 0);
}
void
LIRGeneratorX86Shared::lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir,
MDefinition* lhs, MDefinition* rhs)
{
ins->setOperand(0, useRegisterAtStart(lhs));
ins->setOperand(1, lhs != rhs ? useOrConstant(rhs) : useOrConstantAtStart(rhs));
defineReuseInput(ins, mir, 0);
}
+void
+LIRGeneratorX86Shared::lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
+{
+ ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
+ ins->setInt64Operand(INT64_PIECES,
+ lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
+ defineInt64ReuseInput(ins, mir, 0);
+}
+
template<size_t Temps>
void
LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
{
// Without AVX, we'll need to use the x86 encodings where one of the
// inputs must be the same location as the output.
//
// :TODO: (Bug 1132894) Note, we might have to allocate a different
--- a/js/src/jit/x86-shared/Lowering-x86-shared.h
+++ b/js/src/jit/x86-shared/Lowering-x86-shared.h
@@ -26,16 +26,22 @@ class LIRGeneratorX86Shared : public LIR
void visitGuardShape(MGuardShape* ins);
void visitGuardObjectGroup(MGuardObjectGroup* ins);
void visitPowHalf(MPowHalf* ins);
void lowerForShift(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
MDefinition* rhs);
void lowerForALU(LInstructionHelper<1, 1, 0>* ins, MDefinition* mir, MDefinition* input);
void lowerForALU(LInstructionHelper<1, 2, 0>* ins, MDefinition* mir, MDefinition* lhs,
MDefinition* rhs);
+
+ void lowerForALUInt64(LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>* ins, MDefinition* mir,
+ MDefinition* lhs, MDefinition* rhs);
+ void lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, 0>* ins,
+ MDefinition* mir, MDefinition* lhs, MDefinition* rhs);
+
template<size_t Temps>
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir, MDefinition* lhs,
MDefinition* rhs);
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
MDefinition* lhs, MDefinition* rhs);
void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
--- a/js/src/jit/x86/MacroAssembler-x86-inl.h
+++ b/js/src/jit/x86/MacroAssembler-x86-inl.h
@@ -48,16 +48,30 @@ MacroAssembler::andPtr(Imm32 imm, Regist
void
MacroAssembler::and64(Imm64 imm, Register64 dest)
{
andl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
andl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
}
void
+MacroAssembler::or64(Imm64 imm, Register64 dest)
+{
+ orl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+ orl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
+MacroAssembler::xor64(Imm64 imm, Register64 dest)
+{
+ xorl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
+ xorl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
+}
+
+void
MacroAssembler::orPtr(Register src, Register dest)
{
orl(src, dest);
}
void
MacroAssembler::orPtr(Imm32 imm, Register dest)
{
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1213,16 +1213,17 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
#endif
fullCompartmentChecks(false),
mallocBytesUntilGC(0),
mallocGCTriggered(false),
alwaysPreserveCode(false),
#ifdef DEBUG
inUnsafeRegion(0),
noGCOrAllocationCheck(0),
+ noNurseryAllocationCheck(0),
#endif
lock(nullptr),
allocTask(rt, emptyChunks_),
helperState(rt)
{
setGCMode(JSGC_MODE_GLOBAL);
}
@@ -7352,16 +7353,27 @@ void JS::AutoAssertNoAlloc::disallowAllo
gc->disallowAlloc();
}
JS::AutoAssertNoAlloc::~AutoAssertNoAlloc()
{
if (gc)
gc->allowAlloc();
}
+
+AutoAssertNoNurseryAlloc::AutoAssertNoNurseryAlloc(JSRuntime* rt)
+ : gc(rt->gc)
+{
+ gc.disallowNurseryAlloc();
+}
+
+AutoAssertNoNurseryAlloc::~AutoAssertNoNurseryAlloc()
+{
+ gc.allowNurseryAlloc();
+}
#endif
JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject* obj)
: AutoSuppressGCAnalysis()
{
MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapCollecting());
}
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1349,16 +1349,29 @@ class ZoneList
ZoneList(const ZoneList& other) = delete;
ZoneList& operator=(const ZoneList& other) = delete;
};
JSObject*
NewMemoryStatisticsObject(JSContext* cx);
+struct MOZ_RAII AutoAssertNoNurseryAlloc
+{
+#ifdef DEBUG
+ explicit AutoAssertNoNurseryAlloc(JSRuntime* rt);
+ ~AutoAssertNoNurseryAlloc();
+
+ private:
+ gc::GCRuntime& gc;
+#else
+ explicit AutoAssertNoNurseryAlloc(JSRuntime* rt) {}
+#endif
+};
+
} /* namespace gc */
#ifdef DEBUG
/* Use this to avoid assertions when manipulating the wrapper map. */
class MOZ_RAII AutoDisableProxyCheck
{
gc::GCRuntime& gc;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1925,55 +1925,76 @@ UncompressedSourceCache::sizeOfExcluding
}
}
return n;
}
const char16_t*
ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder)
{
- switch (dataType) {
- case DataUncompressed:
- return uncompressedChars();
-
- case DataCompressed: {
- if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(this, holder))
+ struct CharsMatcher
+ {
+ using ReturnType = const char16_t*;
+
+ JSContext* cx;
+ ScriptSource& ss;
+ UncompressedSourceCache::AutoHoldEntry& holder;
+
+ explicit CharsMatcher(JSContext* cx, ScriptSource& ss,
+ UncompressedSourceCache::AutoHoldEntry& holder)
+ : cx(cx)
+ , ss(ss)
+ , holder(holder)
+ { }
+
+ ReturnType match(Uncompressed& u) {
+ return u.chars;
+ }
+
+ ReturnType match(Compressed& c) {
+ if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss, holder))
+ return decompressed;
+
+ const size_t nbytes = sizeof(char16_t) * (ss.length() + 1);
+ char16_t* decompressed = static_cast<char16_t*>(js_malloc(nbytes));
+ if (!decompressed) {
+ JS_ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ if (!DecompressString((const unsigned char*) ss.compressedData(), ss.compressedBytes(),
+ reinterpret_cast<unsigned char*>(decompressed), nbytes)) {
+ JS_ReportOutOfMemory(cx);
+ js_free(decompressed);
+ return nullptr;
+ }
+
+ decompressed[ss.length()] = 0;
+
+ if (!cx->runtime()->uncompressedSourceCache.put(&ss, decompressed, holder)) {
+ JS_ReportOutOfMemory(cx);
+ js_free(decompressed);
+ return nullptr;
+ }
+
return decompressed;
-
- const size_t nbytes = sizeof(char16_t) * (length_ + 1);
- char16_t* decompressed = static_cast<char16_t*>(js_malloc(nbytes));
- if (!decompressed) {
- JS_ReportOutOfMemory(cx);
+ }
+
+ ReturnType match(Parent& p) {
+ return p.parent->chars(cx, holder);
+ }
+
+ ReturnType match(Missing&) {
+ MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
return nullptr;
}
-
- if (!DecompressString((const unsigned char*) compressedData(), compressedBytes(),
- reinterpret_cast<unsigned char*>(decompressed), nbytes)) {
- JS_ReportOutOfMemory(cx);
- js_free(decompressed);
- return nullptr;
- }
-
- decompressed[length_] = 0;
-
- if (!cx->runtime()->uncompressedSourceCache.put(this, decompressed, holder)) {
- JS_ReportOutOfMemory(cx);
- js_free(decompressed);
- return nullptr;
- }
-
- return decompressed;
- }
-
- case DataParent:
- return parent()->chars(cx, holder);
-
- default:
- MOZ_CRASH();
- }
+ };
+
+ CharsMatcher cm(cx, *this, holder);
+ return data.match(cm);
}
JSFlatString*
ScriptSource::substring(JSContext* cx, uint32_t start, uint32_t stop)
{
MOZ_ASSERT(start <= stop);
UncompressedSourceCache::AutoHoldEntry holder;
const char16_t* chars = this->chars(cx, holder);
@@ -1991,80 +2012,74 @@ ScriptSource::substringDontDeflate(JSCon
if (!chars)
return nullptr;
return NewStringCopyNDontDeflate<CanGC>(cx, chars + start, stop - start);
}
void
ScriptSource::setSource(const char16_t* chars, size_t length, bool ownsChars /* = true */)
{
- MOZ_ASSERT(dataType == DataMissing);
-
- dataType = DataUncompressed;
- data.uncompressed.chars = chars;
- data.uncompressed.ownsChars = ownsChars;
-
+ MOZ_ASSERT(data.is<Missing>());
+
+ data = SourceType(Uncompressed(chars, ownsChars));
length_ = length;
}
void
ScriptSource::setCompressedSource(JSRuntime* maybert, void* raw, size_t nbytes, HashNumber hash)
{
- MOZ_ASSERT(dataType == DataMissing || dataType == DataUncompressed);
- if (dataType == DataUncompressed && ownsUncompressedChars())
+ MOZ_ASSERT(data.is<Missing>() || data.is<Uncompressed>());
+
+ if (data.is<Uncompressed>() && data.as<Uncompressed>().ownsChars)
js_free(const_cast<char16_t*>(uncompressedChars()));
- dataType = DataCompressed;
- data.compressed.raw = raw;
- data.compressed.nbytes = nbytes;
- data.compressed.hash = hash;
+ data = SourceType(Compressed(raw, nbytes, hash));
if (maybert)
updateCompressedSourceSet(maybert);
}
void
ScriptSource::updateCompressedSourceSet(JSRuntime* rt)
{
- MOZ_ASSERT(dataType == DataCompressed);
+ MOZ_ASSERT(data.is<Compressed>());
MOZ_ASSERT(!inCompressedSourceSet);
CompressedSourceSet::AddPtr p = rt->compressedSourceSet.lookupForAdd(this);
if (p) {
// There is another ScriptSource with the same compressed data.
// Mark that ScriptSource as the parent and use it for all attempts to
// get the source for this ScriptSource.
ScriptSource* parent = *p;
parent->incref();
js_free(compressedData());
- dataType = DataParent;
- data.parent = parent;
+ data = SourceType(Parent(parent));
} else {
if (rt->compressedSourceSet.add(p, this))
inCompressedSourceSet = true;
}
}
bool
ScriptSource::ensureOwnsSource(ExclusiveContext* cx)
{
- MOZ_ASSERT(dataType == DataUncompressed);
+ MOZ_ASSERT(data.is<Uncompressed>());
if (ownsUncompressedChars())
return true;
char16_t* uncompressed = cx->zone()->pod_malloc<char16_t>(Max<size_t>(length_, 1));
if (!uncompressed) {
ReportOutOfMemory(cx);
return false;
}
PodCopy(uncompressed, uncompressedChars(), length_);
- data.uncompressed.chars = uncompressed;
- data.uncompressed.ownsChars = true;
+ data.as<Uncompressed>().chars = uncompressed;
+ data.as<Uncompressed>().ownsChars = true;
return true;
}
bool
ScriptSource::setSourceCopy(ExclusiveContext* cx, SourceBufferHolder& srcBuf,
bool argumentsNotIncluded, SourceCompressionTask* task)
{
MOZ_ASSERT(!hasSourceData());
@@ -2164,91 +2179,131 @@ SourceCompressionTask::work()
if (void* newCompressed = js_realloc(compressed, compressedBytes))
compressed = newCompressed;
return Success;
}
ScriptSource::~ScriptSource()
{
- MOZ_ASSERT_IF(inCompressedSourceSet, dataType == DataCompressed);
-
- switch (dataType) {
- case DataUncompressed:
- if (ownsUncompressedChars())
- js_free(const_cast<char16_t*>(uncompressedChars()));
- break;
-
- case DataCompressed:
- // Script source references are only manipulated on the main thread,
- // except during off thread parsing when the source may be created
- // and used exclusively by the thread doing the parse. In this case the
- // ScriptSource might be destroyed while off the main thread, but it
- // will not have been added to the runtime's compressed source set
- // until the parse is finished on the main thread.
- if (inCompressedSourceSet)
- TlsPerThreadData.get()->runtimeFromMainThread()->compressedSourceSet.remove(this);
- js_free(compressedData());
- break;
-
- case DataParent:
- parent()->decref();
- break;
-
- default:
- break;
- }
+ struct DestroyMatcher
+ {
+ using ReturnType = void;
+
+ ScriptSource& ss;
+
+ explicit DestroyMatcher(ScriptSource& ss)
+ : ss(ss)
+ { }
+
+ ReturnType match(Uncompressed& u) {
+ if (u.ownsChars)
+ js_free(const_cast<char16_t*>(u.chars));
+ }
+
+ ReturnType match(Compressed& c) {
+ if (ss.inCompressedSourceSet)
+ TlsPerThreadData.get()->runtimeFromMainThread()->compressedSourceSet.remove(&ss);
+ js_free(c.raw);
+ }
+
+ ReturnType match(Parent& p) {
+ p.parent->decref();
+ }
+
+ ReturnType match(Missing&) {
+ // Nothing to do here.
+ }
+ };
+
+ MOZ_ASSERT_IF(inCompressedSourceSet, data.is<Compressed>());
+
+ DestroyMatcher dm(*this);
+ data.match(dm);
}
void
ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ScriptSourceInfo* info) const
{
- if (dataType == DataUncompressed && ownsUncompressedChars())
+ if (data.is<Uncompressed>() && ownsUncompressedChars())
info->uncompressed += mallocSizeOf(uncompressedChars());
- else if (dataType == DataCompressed)
+ else if (data.is<Compressed>())
info->compressed += mallocSizeOf(compressedData());
info->misc += mallocSizeOf(this) +
mallocSizeOf(filename_.get()) +
mallocSizeOf(introducerFilename_.get());
info->numScripts++;
}
template<XDRMode mode>
bool
ScriptSource::performXDR(XDRState<mode>* xdr)
{
+ struct CompressedLengthMatcher
+ {
+ using ReturnType = size_t;
+
+ ReturnType match(Uncompressed&) {
+ return 0;
+ }
+
+ ReturnType match(Compressed& c) {
+ return c.nbytes;
+ }
+
+ ReturnType match(Parent& p) {
+ return p.parent->data.match(*this);
+ }
+
+ ReturnType match(Missing&) {
+ MOZ_CRASH("Missing source data in ScriptSource::performXDR");
+ return 0;
+ }
+ };
+
+ struct RawDataMatcher
+ {
+ using ReturnType = void*;
+
+ ReturnType match(Uncompressed& u) {
+ return (void*) u.chars;
+ }
+
+ ReturnType match(Compressed& c) {
+ return c.raw;
+ }
+
+ ReturnType match(Parent& p) {
+ return p.parent->data.match(*this);
+ }
+
+ ReturnType match(Missing&) {
+ MOZ_CRASH("Missing source data in ScriptSource::performXDR");
+ return nullptr;
+ }
+ };
+
uint8_t hasSource = hasSourceData();
if (!xdr->codeUint8(&hasSource))
return false;
uint8_t retrievable = sourceRetrievable_;
if (!xdr->codeUint8(&retrievable))
return false;
sourceRetrievable_ = retrievable;
if (hasSource && !sourceRetrievable_) {
if (!xdr->codeUint32(&length_))
return false;
uint32_t compressedLength;
if (mode == XDR_ENCODE) {
- switch (dataType) {
- case DataUncompressed:
- compressedLength = 0;
- break;
- case DataCompressed:
- compressedLength = compressedBytes();
- break;
- case DataParent:
- compressedLength = parent()->compressedBytes();
- break;
- default:
- MOZ_CRASH();
- }
+ CompressedLengthMatcher m;
+ compressedLength = data.match(m);
}
if (!xdr->codeUint32(&compressedLength))
return false;
{
uint8_t argumentsNotIncluded;
if (mode == XDR_ENCODE)
argumentsNotIncluded = argumentsNotIncluded_;
@@ -2267,30 +2322,18 @@ ScriptSource::performXDR(XDRState<mode>*
}
if (compressedLength)
setCompressedSource(xdr->cx()->runtime(), p, compressedLength,
CompressedSourceHasher::computeHash(p, compressedLength));
else
setSource((const char16_t*) p, length_);
} else {
- void* p;
- switch (dataType) {
- case DataUncompressed:
- p = (void*) uncompressedChars();
- break;
- case DataCompressed:
- p = compressedData();
- break;
- case DataParent:
- p = parent()->compressedData();
- break;
- default:
- MOZ_CRASH();
- }
+ RawDataMatcher rdm;
+ void* p = data.match(rdm);
if (!xdr->codeBytes(p, byteLen))
return false;
}
}
uint8_t haveSourceMap = hasSourceMapURL();
if (!xdr->codeUint8(&haveSourceMap))
return false;
@@ -2461,19 +2504,19 @@ ScriptSource::setSourceMapURL(ExclusiveC
sourceMapURL_ = DuplicateString(cx, sourceMapURL);
return sourceMapURL_ != nullptr;
}
size_t
ScriptSource::computedSizeOfData() const
{
- if (dataType == DataUncompressed && ownsUncompressedChars())
+ if (data.is<Uncompressed>() && ownsUncompressedChars())
return sizeof(char16_t) * length_;
- if (dataType == DataCompressed)
+ if (data.is<Compressed>())
return compressedBytes();
return 0;
}
/*
* Shared script data management.
*/
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -7,16 +7,17 @@
/* JS script descriptor. */
#ifndef jsscript_h
#define jsscript_h
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/PodOperations.h"
+#include "mozilla/Variant.h"
#include "jsatom.h"
#include "jslock.h"
#include "jsopcode.h"
#include "jstypes.h"
#include "gc/Barrier.h"
#include "gc/Rooting.h"
@@ -601,37 +602,54 @@ class ScriptSource
uint32_t refs;
// Note: while ScriptSources may be compressed off thread, they are only
// modified by the main thread, and all members are always safe to access
// on the main thread.
// Indicate which field in the |data| union is active.
- enum {
- DataMissing,
- DataUncompressed,
- DataCompressed,
- DataParent
- } dataType;
-
- union {
- struct {
- const char16_t* chars;
- bool ownsChars;
- } uncompressed;
-
- struct {
- void* raw;
- size_t nbytes;
- HashNumber hash;
- } compressed;
+
+ struct Missing { };
+
+ struct Uncompressed
+ {
+ Uncompressed(const char16_t* chars, bool ownsChars)
+ : chars(chars)
+ , ownsChars(ownsChars)
+ { }
+
+ const char16_t* chars;
+ bool ownsChars;
+ };
+
+ struct Compressed
+ {
+ Compressed(void* raw, size_t nbytes, HashNumber hash)
+ : raw(raw)
+ , nbytes(nbytes)
+ , hash(hash)
+ { }
+
+ void* raw;
+ size_t nbytes;
+ HashNumber hash;
+ };
+
+ struct Parent
+ {
+ explicit Parent(ScriptSource* parent)
+ : parent(parent)
+ { }
ScriptSource* parent;
- } data;
+ };
+
+ using SourceType = mozilla::Variant<Missing, Uncompressed, Compressed, Parent>;
+ SourceType data;
uint32_t length_;
// The filename of this script.
UniqueChars filename_;
UniqueTwoByteChars displayURL_;
UniqueTwoByteChars sourceMapURL_;
@@ -675,17 +693,17 @@ class ScriptSource
bool hasIntroductionOffset_:1;
// Whether this is in the runtime's set of compressed ScriptSources.
bool inCompressedSourceSet:1;
public:
explicit ScriptSource()
: refs(0),
- dataType(DataMissing),
+ data(SourceType(Missing())),
length_(0),
filename_(nullptr),
displayURL_(nullptr),
sourceMapURL_(nullptr),
mutedErrors_(false),
introductionOffset_(0),
introducerFilename_(nullptr),
introductionType_(nullptr),
@@ -704,60 +722,54 @@ class ScriptSource
}
bool initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options);
bool setSourceCopy(ExclusiveContext* cx,
JS::SourceBufferHolder& srcBuf,
bool argumentsNotIncluded,
SourceCompressionTask* tok);
void setSourceRetrievable() { sourceRetrievable_ = true; }
bool sourceRetrievable() const { return sourceRetrievable_; }
- bool hasSourceData() const { return dataType != DataMissing; }
- bool hasCompressedSource() const { return dataType == DataCompressed; }
+ bool hasSourceData() const { return !data.is<Missing>(); }
+ bool hasCompressedSource() const { return data.is<Compressed>(); }
size_t length() const {
MOZ_ASSERT(hasSourceData());
return length_;
}
bool argumentsNotIncluded() const {
MOZ_ASSERT(hasSourceData());
return argumentsNotIncluded_;
}
const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp);
JSFlatString* substring(JSContext* cx, uint32_t start, uint32_t stop);
JSFlatString* substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop);
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ScriptSourceInfo* info) const;
const char16_t* uncompressedChars() const {
- MOZ_ASSERT(dataType == DataUncompressed);
- return data.uncompressed.chars;
+ return data.as<Uncompressed>().chars;
}
bool ownsUncompressedChars() const {
- MOZ_ASSERT(dataType == DataUncompressed);
- return data.uncompressed.ownsChars;
+ return data.as<Uncompressed>().ownsChars;
}
void* compressedData() const {
- MOZ_ASSERT(dataType == DataCompressed);
- return data.compressed.raw;
+ return data.as<Compressed>().raw;
}
size_t compressedBytes() const {
- MOZ_ASSERT(dataType == DataCompressed);
- return data.compressed.nbytes;
+ return data.as<Compressed>().nbytes;
}
HashNumber compressedHash() const {
- MOZ_ASSERT(dataType == DataCompressed);
- return data.compressed.hash;
+ return data.as<Compressed>().hash;
}
ScriptSource* parent() const {
- MOZ_ASSERT(dataType == DataParent);
- return data.parent;
+ return data.as<Parent>().parent;
}
void setSource(const char16_t* chars, size_t length, bool ownsChars = true);
void setCompressedSource(JSRuntime* maybert, void* raw, size_t nbytes, HashNumber hash);
void updateCompressedSourceSet(JSRuntime* rt);
bool ensureOwnsSource(ExclusiveContext* cx);
// XDR handling
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -280,17 +280,18 @@ ModuleParseTask::ModuleParseTask(Exclusi
callbackData)
{
}
void
ModuleParseTask::parse()
{
SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
- ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, &alloc);
+ ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, &alloc,
+ sourceObject.address());
if (module)
script = module->script();
}
void
js::CancelOffThreadParses(JSRuntime* rt)
{
AutoLockHelperThreadState lock;
@@ -455,16 +456,17 @@ QueueOffThreadParseTask(JSContext* cx, P
bool
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
{
// Suppress GC so that calls below do not trigger a new incremental GC
// which could require barriers on the atoms compartment.
gc::AutoSuppressGC nogc(cx);
+ gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
if (!global)
return false;
ScopedJSDeletePtr<ExclusiveContext> helpercx(
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
ExclusiveContext::Context_Exclusive));
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -76,24 +76,20 @@ interface nsIXPCScriptable : nsISupports
void preCreate(in nsISupports nativeObj, in JSContextPtr cx,
in JSObjectPtr globalObj, out JSObjectPtr parentObj);
boolean addProperty(in nsIXPConnectWrappedNative wrapper,
in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
in jsval val);
- // The returnCode should be set to NS_SUCCESS_I_DID_SOMETHING if
- // this method does something.
boolean getProperty(in nsIXPConnectWrappedNative wrapper,
in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
in JSValPtr vp);
- // The returnCode should be set to NS_SUCCESS_I_DID_SOMETHING if
- // this method does something.
boolean setProperty(in nsIXPConnectWrappedNative wrapper,
in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
in JSValPtr vp);
boolean enumerate(in nsIXPConnectWrappedNative wrapper,
in JSContextPtr cx, in JSObjectPtr obj);
boolean newEnumerate(in nsIXPConnectWrappedNative wrapper,
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2046,16 +2046,49 @@ nsPresContext::PostRebuildAllStyleDataEv
{
if (!mShell) {
// We must have been torn down. Nothing to do here.
return;
}
RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
}
+struct MediaFeatureHints
+{
+ nsRestyleHint restyleHint;
+ nsChangeHint changeHint;
+};
+
+static bool
+MediaFeatureValuesChangedAllDocumentsCallback(nsIDocument* aDocument, void* aHints)
+{
+ MediaFeatureHints* hints = static_cast<MediaFeatureHints*>(aHints);
+ if (nsIPresShell* shell = aDocument->GetShell()) {
+ if (nsPresContext* pc = shell->GetPresContext()) {
+ pc->MediaFeatureValuesChangedAllDocuments(hints->restyleHint,
+ hints->changeHint);
+ }
+ }
+ return true;
+}
+
+void
+nsPresContext::MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
+ nsChangeHint aChangeHint)
+{
+ MediaFeatureValuesChanged(aRestyleHint, aChangeHint);
+ MediaFeatureHints hints = {
+ aRestyleHint,
+ aChangeHint
+ };
+
+ mDocument->EnumerateSubDocuments(MediaFeatureValuesChangedAllDocumentsCallback,
+ &hints);
+}
+
void
nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint)
{
mPendingMediaFeatureValuesChanged = false;
// MediumFeaturesChanged updates the applied rules, so it always gets called.
if (mShell) {
@@ -2152,16 +2185,35 @@ nsPresContext::HandleMediaFeatureValuesC
{
// Null-check mShell in case the shell has been destroyed (and the
// event is the only thing holding the pres context alive).
if (mPendingMediaFeatureValuesChanged && mShell) {
MediaFeatureValuesChanged(nsRestyleHint(0));
}
}
+static void
+NotifyTabSizeModeChanged(TabParent* aTab, void* aArg)
+{
+ nsSizeMode* sizeMode = static_cast<nsSizeMode*>(aArg);
+ aTab->SizeModeChanged(*sizeMode);
+}
+
+void
+nsPresContext::SizeModeChanged(nsSizeMode aSizeMode)
+{
+ if (HasCachedStyleData()) {
+ nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
+ NotifyTabSizeModeChanged,
+ &aSizeMode);
+ MediaFeatureValuesChangedAllDocuments(eRestyle_Subtree,
+ NS_STYLE_HINT_REFLOW);
+ }
+}
+
nsCompatibility
nsPresContext::CompatibilityMode() const
{
return Document()->GetCompatibilityMode();
}
void
nsPresContext::SetPaginatedScrolling(bool aPaginated)
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -16,16 +16,17 @@
#include "nsIPresShell.h"
#include "nsRect.h"
#include "nsFont.h"
#include "gfxFontConstants.h"
#include "nsIAtom.h"
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsCRT.h"
+#include "nsIWidgetListener.h"
#include "FramePropertyTable.h"
#include "nsGkAtoms.h"
#include "nsCycleCollectionParticipant.h"
#include "nsChangeHint.h"
#include <algorithm>
// This also pulls in gfxTypes.h, which we cannot include directly.
#include "gfxRect.h"
#include "nsTArray.h"
@@ -274,24 +275,39 @@ public:
* rerunning of selector matching
*
* For aChangeHint, see RestyleManager::RebuildAllStyleData. (Passing
* a nonzero aChangeHint forces rebuilding style data even if
* nsRestyleHint(0) is passed.)
*/
void MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint = nsChangeHint(0));
+ /**
+ * Calls MediaFeatureValuesChanged for this pres context and all descendant
+ * subdocuments that have a pres context. This should be used for media
+ * features that must be updated in all subdocuments e.g. display-mode.
+ */
+ void MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
+ nsChangeHint aChangeHint = nsChangeHint(0));
+
void PostMediaFeatureValuesChangedEvent();
void HandleMediaFeatureValuesChangedEvent();
void FlushPendingMediaFeatureValuesChanged() {
if (mPendingMediaFeatureValuesChanged)
MediaFeatureValuesChanged(nsRestyleHint(0));
}
/**
+ * Updates the size mode on all remote children and recursively notifies this
+ * document and all subdocuments (including remote children) that a media
+ * feature value has changed.
+ */
+ void SizeModeChanged(nsSizeMode aSizeMode);
+
+ /**
* Access compatibility mode for this context. This is the same as
* our document's compatibility mode.
*/
nsCompatibility CompatibilityMode() const;
/**
* Notify the context that the document's compatibility mode has changed
*/
--- a/layout/generic/nsBRFrame.cpp
+++ b/layout/generic/nsBRFrame.cpp
@@ -262,12 +262,19 @@ BRFrame::AccessibleType()
nsIContent *parent = mContent->GetParent();
if (parent && parent->IsRootOfNativeAnonymousSubtree() &&
parent->GetChildCount() == 1) {
// This <br> is the only node in a text control, therefore it is the hacky
// "bogus node" used when there is no text in the control
return a11y::eNoType;
}
+ // Trailing HTML br element don't play any difference. We don't need to expose
+ // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
+ // for details).
+ if (!mContent->GetNextSibling() && !GetNextSibling()) {
+ return a11y::eNoType;
+ }
+
return a11y::eHTMLBRType;
}
#endif
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/LICENSE
@@ -0,0 +1,32 @@
+Tests in this directory have been imported from
+https://github.com/MathML/MathMLinHTML5-tests
+
+
+Copyright (c) 2016 MathML Association
+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.
new file mode 100644
index 0000000000000000000000000000000000000000..0f2ae3bc1261a3930b8e23a1efe8e0b547fe8c70
GIT binary patch
literal 1208
zc$_QucXRU(3GruOVA;XI%>V?P4GfG7Sb?*fn*sv^a|n=mj)8$u>gT`dU2bk6zCgVy
zKru-mmMK3LTj1*$;sF$M0P^#J>LQrfv>*Bh>l=aCKruTYPT=Pf_>i2NSO7GS1;{rC
z$~iIcG9SxGO-uo@Ie=nXKy1dcbdzTWP!K3)0^~~ru?mwv>-~(}k_w<58-RR}S`PLL
z3=Fx66+rVC1Au%sFlGv1$V<#k1&Yl8;w+$grjT0&(gpd&B|v*^fNI2mSmA#ggK@>2
z<b;%j1cpatyT5*a^fPy#QGxM(hAx8`t3AoWM!c*tJU$9s46zbipBNaRYX2Pi(W=<>
zla-mpz-R%J8bg!~GsuZR>zN>G66+3HG&W9cY+Tu$$<NH}%-@n$b0Fcw2^Nvmgr_MD
zjBact7Y-}{!XK?4xt&GYq!#cnD>G=yaSDO-AD!r*b;v>F*#0B-7kpm|rYX;GN>Sr>
z4HsS6y-1q-owLN*1rnZ0FGQ<)-&J>i<GA~c%hqYX&#}DdjDtLhb;h6fecxNj{6I?o
zLqNUk8JTL156^D2e^5Hkd#75yZ#KuD$&bbI{)8XTW$BG}t2(Q_PHxSrB!2EEeYJNU
zUM^(zzqk0YnpoZOLheKLWp|$avsOE?;do~x&oz%grv=?DTe(~&1iYC1tfO*L(Y!)a
zw}TT6Pu-|>zQ;H@r~cGgD_vJ}P8XHX_%n}<=AJ*YLu1pCBTG+8Y+DrW^EOhvs8mJ#
z%=-PSB`)liQe4b`<I|E)=2vo8T-?U=G-<=B$4k8Aj=2Xf*|jQBVJXk&!(r1`dYW0s
ziga23*nR!HSa9{)OXn<gx8$bwCp8NG|DW$LH$3q~#?k+O3TCA3E}Hc;TX)es5#^7s
z-3-HcokhdeSG-h@4w@YtUCq5ZeB1o5cQ;<CJQ(8Sb#-;t{(^(?W-Ct0LE<VQA*~@n
zlFf}xU<2bPiGwv_Y^(}rnOYR~9s2)Y;M0HS6<%y^W^59|>Iy3MR~Q*06WK0<^`$X1
z|3Ay)=Fl~fk(Yr%Bb0F`*u0d4#KeS@lq+7^UOsv!&z!&PyXJ(>IghhG=QRw?4Gb>m
zYMu4eJ$b_C+&OPuJu{Xr0|O&N1EY-p57K`qp8et6_Oe~)Ge74vIk{tb3JFsaE6(X3
zKA@m*_=8tKTcdMZphrie<Pyi;gYy?2vfRI56GzIHCbvz?nkF_boY|D0<k_~qzbUz}
zu&yXg=>)6G?v@I5W=3UZVdce>ioV}_zqMNO`@6q8Q}sW`ZHyPXFaK|H!tLMHdtZFN
zH9xxU`linx|E>~zDfaf+Lnemw57Ad);giD9<Xx7(<KUNvva=IR7<^xFurt8(!h@uW
z11pXT97s4^)y~MXL|LMPS$)A4HNmXqh6V=51T*yn4E7($V`~-^QQmyfV6g`C;rpu{
zw(&BvH%m+iW4zRpAS`iEVCr(FBQ`uvJVHE07IT>cHJL&dGNpZNJlooFC_{yVp=mP@
L7X$MP1~3KyHR#Cl
new file mode 100644
index 0000000000000000000000000000000000000000..03e9b3688596fd63753844ee5baa11c5c3e9a9fd
GIT binary patch
literal 1264
zc$_QucXRU(3GruOVEMqn%>V>kR~Q%>umWc{Hw6X;<{BXLAp-+r@H?R&$!=~TzCgVR
zKru-mmMK3LTj1*$;sF!`smlSXi(q0?5%dq%Hv+MNVs=2Bz|SS{Avrg(0BD{LkZ%r@
zb7J6SK9-T1m;z*T0L8R`*o<ZACeI9@AW+N%$d?9U6()by`x&_<6+k;)0Qn%b9PAeu
z7;+OUfaWm<0QqcS%oM<omzbLh6q^CWlNlIT<(IKfpIne%TmrNgXb*!J5G(v|V=%6m
zlbn!}kihWBe)sg5M?c@LNqvx3$0(Axqom@vT8b2xg~SX85r(W_Zes=psM<e=ezYof
z{bXfkF)&)dq{a|s!whmF(0V3_n#8(;7LAQl8yi=4XYw;MJM*`s)f`ATae_r8HQ{MW
z1EU*T$%O+8fbd7_M{Z}4HmL<X%*qU!a-2dS{a1s|`aL#~m}A1)$^A&M#&J=5Na4?k
z4to`r$OTlp#w@<#tK%ed)6K+x!}D4my^QnK_kY`+ush-x^O;GZvEymY<MW?;kJ~fw
zee7b{{a)g9hwzz?`i}(tCeIgKzC+&D-R79h-rfp+_m|#Be$VJXf5PV&yUg9TRgCI4
z4!PMJQk&^A=TZ14^+$%=7Vj5)@3_QX>E3a(zrE5DB`;Pk3S8LMet^@<M<V@%Vxi*}
zr-L(R6`s6kDl=QryM2#ZUZeEouVLX^#nqp>oGGeQ3~OB<?p^qHj%Mk@t%|vaw{l(E
zcQrM_*-*;H=rWtD^SN2d?)kjNwiBQ4SKRz)NB5m#E14*_f59_kW3pD+7lg6SH!9k&
z(bY2I#I;qX{kwGcY|MVUYuSsr@1;XUWj8;soR|J>W#GPRPn}oKnSAEKW}khhZ!5_x
z;lEgvE2@|8W!tFzZN1f}yRU?%r+9zc&feOZ`S;Kbug;q$;^B{!!w(l#&ODh@QE>lT
z>D|C{L3!y>ES{2i1?7D^g0BU}KG`{kZ}z`+)_?iK4oY)^<1`^5tsy~@&5cc91LG%&
zgEeAotO{qDS`_vj`u|_x(|_j`UTkh=Y!bri3M%$j7#SiH*)D_ir7<-BKg;9h&^3{f
zmw`belyN6Gjie+bCMKk$Z1L6h^3gka=KN(}&$Fk`pFQKPb5h^yq>kPR&ow%FE6;fP
z`s*-s&61Lkcp*0R*^+4~DQfA@mx_r?Gr3Ag%#@Ij+*1F+_|Js2KZ=LHI8Xc8&f?9_
zpKLC$;go^ahtmcI948F4xK9}roG_^3HZ*89Sz)MRc&7NA{tBH_KIb(8FL+!FF)%VP
z3I5=#bMnL2zyck;bN=UiSS-7Qjt4$5Q~)OSq?C}LclUnpEz^H|Z?FCKH>vluzj;6U
zeYjpIr@DT<_}{qS#i!p#mD%l!H&wd3=&nr+!>1F)><kRhFiT-*@-EBYaq!DS+1Uvu
z48AWoU~<5G^dPC?z>4Dn2NDifwKtj+FnMJQEi98_W=@v*=w`gX;ld%=6=?<%j)q3k
zyvAKd27mT)rQ9|sFz8^+6jeCOVb&<f6IiL`aOSYW5ru;a?UyYDrYbpzY9)xt--zkZ
R=y7WT7B)Oa%)pWYgaK!++Moac
new file mode 100644
index 0000000000000000000000000000000000000000..df6aab016ae7fdde75544ae0af1256cc015303eb
GIT binary patch
literal 1280
zc$_QucXRU(3GruOU}a$7W&i?i4hBXBtiajLO@V=dxdX_2%D})FlRSfcvYT6oF9QQh
z0Z>d5h-J!;#TNKFhIjzQdVu^Kpt=YqHuazW!TLrZHc-qCh!gm^1U@9^CIa;_TLAgy
zKshG{Ugl#Nsfj5-HV05l3y95FmTvOQ015)dOn`i8AXZ`WXT6`1TT%hE;|GurQp>@9
zfq@}6u>xowV*rrP2F6SQ40(yUsX(zAKs*oVN1uW<JpBdv#U(&{L2ecUVuk;048|36
zk`q!A5*QxM>d~}*^fPy#QGxM(hAx8`t3AoWM!c*tJU$9s4B=H=KzpES{~Y?!s@V0D
zm6^rBXaSQNLzE3O$caGfnILKs>ke8pHcoA9T-lw;&&=%1-;!2yAmPLb7LnA1rzs7L
zZfqqN4lDq|AFUs`okiND7Vt1DGib_j3W4-r4Knn5VjxmGlgGoPLwSc|lhWdfE{_X6
z0+&QGmMwLR()6+s&UUid)Eh8sCsX(W_gh8z8^4xSH@>wvxUb?2s{@nZ>5rek|2}{I
zA6r4se2)$P{Wf-39OPLq*dL&BUU2yiX<JvDV>)+TY8obgDL>Laqqly-%$|OW-JFrk
zejA#5D_YEEy3KhM{z?6jVcFt(L3xKI`hxOF)Bhbc<=OUP<)V#l%>0eUAr{XqT=_bi
zGCC(|^B!kS4=>1R(~*5NYX|eXv)i^;-K{7Lo7<hK$GuUfc5_UJ_H&=5-s^cYwcmvr
zeb?1@<~@4x!Murj8d61B=VbP7?fK#|@BcI(cllXb^LU!w(?q^lZt02SZ+y(W{7~C!
ztsPS*Hc!=Eeb6Ip^|?E**L^v5?N;vXP38OYr)GaSb^X~!?N2u6-#D+XS-jeJ`JBl;
z4~p;ZyZ*!`W-b5X)WlGY=Pi?%roV}gZrRJ;$-PPS_ig9HcS`E=-b8i2R1u&4sQKuk
zradQ8%=R38|NleY%8Zpe%JrL8@tEGdubDTs^p3-mopX4W|NrLxkDqa><~>OKCM2Xa
zBuKKku?cKo{3LO(MvRSB;Ve^&!oEZQ{|kKj@4Ui`&CQHWLReiv#r_H-Lu4Y`Ww5?9
zhUWigdE6YjCNlCeFldA_?gX2cl8~5~kdjj3q3z|Pck;~n%f6mxPoF<~##`s4zSl_|
zy%U~mbo5r9@zgzc!bjWlr2cthV*`T=x>{#Fbx)q~Id{%mSI>;4%fP_M(7-6;|AX`&
zif4Z~x4mrF`OMEbO-}Auo<hRZ#4AsxCng9?NW3CACGo+8L>|GU1h&k8WRKK{M|0u>
zBB#up7r1D_#5F5{2|Q!jgV_<29==}uAR=bY{5dl@io{#rExuCiaNxv&BPUiYxwH3s
z_1m~(d#mg7Zl2x~{$~1-@9qCpZhZR}r~hm3x98LDZ+%l!vp>sam+S7z9SjGo%Iz6o
z;g-VC<Xx7(<KUNvva=IR7<^xFurt8()q|vp11pXT97s4^)!t}Qz~q%Jw6IKynK@ae
zPW2HhhuLTDH)#eEj)q3kyvAKd27mT)rQ9|sFz8^+6jeCOVb&<f6IiL`aOSYW5ru;a
j?UyqJrYbpzY9)xt--zkZ=y7Xeh^gk0V}1cF0e~0)P}lDc
new file mode 100644
index 0000000000000000000000000000000000000000..c5ba7a6a78edfa3d4e2a240c87c4092b843379ae
GIT binary patch
literal 1232
zc$_QucXRU(3GruOV7b7+%>V?Pe;61UumWc{Hw6X;<`f|FDgy(f{_@YVEpBchzCgV;
zKru-mmMK3LTj1*$;sF$k0P=Hz>LQrf6g2&V^^HJmpqL#HC-8F#d`QksEC8A(0_2+m
z<(wFJnU7_pCIZc8<N%6k0kIj&(oLQjKtZ6G36L)h#41ewtoJi=ODceNoB;AcYB|_1
z04+<b0Gh`b0OYfQF;f6TUSe)4P;3SeR{_;$U&;S*s35<%1Zb}hP>mQ6EBtR`Fs_)B
zoRE@`z>p-nHnsoJ&)j`R1;+asx(s5h_9P1%@v_SB_$Y8Octmm4F)%>Y{yFrcRk7<Q
zD>I9M(E=tlhA10mkQ0H{GeOiO)*ZBHY@FKIxUxHwpPAX2za_2aK*EU=EF!51Pg5Eg
z-PlSl99RH^KUzO>JBzeQE#P5RX3&)56awi#J85IyAp?Q7|0)evmc9xr{Tw8!#d?!N
z@nG8oiB%WYo^eqW;O+<%Vr$#yD8In};_(;4Ut)KC>o5EsmZh2$`LAZ*@Aox)4c>bW
z9)7vo^~sIqBkjrZ<@Y8(W|&z2b_e6j{nmMpw00EUY2t}{@zPXpNA-q4)9Q!?1#cc-
z%(mkD@XFRYFX5rSthwSJ|KqwkP1QaL8;(!ZlFCk5p>V;iiSN3cc9_J3w2sP2g}pnZ
z7e6uha_QQ-la>aCzmpeUk5s+o>HcMjR7Ye?>Sm9g)0b4N)@@mMyK86CvAW}}5lT<9
zHY`@%-qmK8ed*rKUu%}gDZXXTc^MzRc;%*tnOUy`&#|vo@_QBX(CF-0zvqRiU8Q@}
zzb>7USa@Su=(5Sa^QJ9b#9AMFp#QU9F0<C|Yd?c-f6+5#ahvo1?y6w1R>i%d&%ZY(
zXMM^#^(y${YcE5g{kfXXR}4ZD{|e7}+I?O#FyK+hgbhZg%KlvuKXZA}l0_4ON?&u&
zeXP5Wq1)=IHze*764DwHB-z~91U4{!k~ml+#>T2}mZ?Qy-=Y8i1wQ?EUg5>&X2vEV
ztgfJ9e}$1DGLh{vSYH}L^Z&CvZVp`&8F?8PG(s77g3U`wNK8yfNip%!_VUp?dFK3O
zU(d6r&!0Wxt#eY}%U_3KlMfFMPf52|ageu>(d5magSxwU8<lu^e0X|zZrT5ktn)Db
zHzDzt;^{xmO-tL`H%fCvq)PnzaN59t<AlK<@hOQ9CM1T4Cnd0D1|)MNXB3~)U!il#
z=e$PX1&?bX21W)Z!5@5ePJZ|rSfHbK&i|Ybi)Bj)`@}bl3c!S%loAs3?%wacW%`fr
z?X}<jCiR~7H}6Nk57!IjRM)Q;{~Pza`1JdzGP`~8rb>4g-L;8fh-66l$N&qc6ow}6
zviuzfzdV$konXS?`+|d=0iH)5Bvl+(aa`a)!r`j+Mw0?2uWX@(Wm3${$ub|^jQ0cO
z;~O&BngvCaH(xYZtigQv{%VJ9yv*#)5);B0FZCn{OB@uKx}52V4UZF#5KocCT;@Pc
erjUh9X&)QUwssuKP~l)Gc+KMmw2%1(1Oot;VAGBO
new file mode 100644
index 0000000000000000000000000000000000000000..2c2b3d81b1e99dde81af6760d2b17d037145bc55
GIT binary patch
literal 1240
zc$_QucXRU(3GruOV7bA-%>V>k1`Lc0Sb?*fn*sv^a}JPslYxQJ!hDx}yqjByFHo-s
zP)rhtWy+7m7Wg`b0QoE_Kt50{V+0eMjH7?Bz7dEG6te^31b!}o56QWS1wiv;fP8bH
zoD%~t^RbN7M4&!K4xpG85Sy_q-Q<}86a<Qy0Qu5Dtit5adOst#qylKi1t1@!mV+J0
z%1x{Qn#ULb<g<Y>QvgF=Vs0u>Yz7dw0M*ZUB<0*)kY8K^v^NB(Mhu7*{<kq0SIkLH
zNJ&Uwc+@Yar1bHRS-j+qnehy+5_+=tHge8vW8z~|6JTX94dDWY4OH!)LqA#-yMD4V
zvltjHU{Yg<vS9`}5okRVL``DdL5s%5sf~>*yEFNjnVtDt(rOMQoH)TElA7=|rGe3n
zt>nUi1wi<t^&_{lNSo9G9%f|*O*u{>kp8oi{Jjo4h#cFmvh~5T9p%Q-3%OR#WaHA^
zq0y?h<ZAB@{>)1}xyGuJnz5BE`40qRswDIunEo-EE9c;7=se^8ncw&3R~j=|RW!FB
zE^nNf%x`EX^Lxi9u|DQIzpwX2cl`A~u5Q7dC$n9U{e4K)+TeA0Yr_xA6@~QG+EmCM
zXN=n`eq8s-zs-+@J09FAl&&mj=QP=N!?^2^5GxPME|0>KJNr3=m@mn2>asqU3{Soy
zGfOM=yXa09wW{6guWT|{G&`=VPK7;b-i#zW;m5nKt@P@-G{sa@{fq6d)(C~iYc@Xg
z%T&(F%6b+YB7MuWY|D`~%Ow@p`bS(o9pT$0x_0JTFDu?pTAs689x7>nb(`9GF*J6?
z9i1=hdKPPNtzKT5#x-@_sWt0V%Rg98ty$i%GPiSM@lo!RZ%n^PxFsC<pFgYogsS0^
zs89BGEIy@~%XUSjExO-*#N*F`lV2ts4rn|3`^WwMhZ6&5ooPSS(yQ_|R;H@u_?lIc
zAubc&n5zH2e*7oXW8crbpg2rONNY%tWOHK^*ueNn;$V#!8>_-urWS>LhyMQ;`1Id-
zg%_Kf8JmQ#x`K-R6-I{0M7GP|xKCqf{(qLo&7o@|BQFDkMkwP>aGFR-NK8yfNip%%
z_VUp?dFK3OU(d6r&!0Wxt#eXe>)Z(+ZO@bX=Z(z_3@+$uo%Pf`dBW%1Id5G(GnOs`
z10zENqm2I#(tjwP{o&m9vR&shKj$<#xnp?>2~!hOo=#6p5SWnoL|{tdg9(Wy0!ayM
znE}ZiDK84o>95c^<#S#m@Pfy+5CbCvli&}&IwwDT4J^>nJLiAShh=9gkNL#Myb8cX
zos<$1^zPp8y=D53@9nkU{wDRF_BZcGzYo_7<y6<N7yldgyZH3`s4}~K@uo_57u~gq
zVR+4zuF3#O?+FPh3{Bo;`8y7Nc_=$O!Gyv01qV9=Jik0hsyMLXxWIvg!&U8#CIw7h
z*+L7;q?nnLW%4rL$*!n*CVt~=f<aSKikVazFo*o87Co^o@j-$B^Cc~ZS%NuC$`Xq{
tg*41)ckFO%a^ze7SwYLSK`Z2dj@^cx0)Y~}ER22Ad7_wK0CP4F0{|iX%}xLS
new file mode 100644
index 0000000000000000000000000000000000000000..a899cc9d2c8357a879176be5507cd5a6696d454c
GIT binary patch
literal 1256
zc$_QucXRU(3GruOV0pp7%>V>kTNoG_umWc{Hw6X;<`N+D0Rsc0Z<d6wk(*nHFHmm?
zP)rhtWy+7m7Wg`bcmTx;fc#vbx(FsV{geK|`bHo&P|Oa96Zp9VJ|yQR0@X090Qu%X
zIVT2Q=3^PDi77xf2T)84h|O4*Zt~0k3IfGUfP85nR$=mIy`Pa=QUSCB=ne*uS`PLL
zK+6&<faWm<0QqcS%oM<omzbLh6q^CWoeT`DjICFLP8Z}C1O31p15_gh#0vl07>q0C
zBqyXKBrqg}U;M-Q@sC-&<c^u~46YJ-viCM}&TM1iV^b4gWiWip`ICVGs`k&JAFYaA
zKUtYs42%{qsWC*^FoT>3w4Mo~Cb90IMPuXC#>SQ1nf%Pm&ipNDH3t$-oL~`2O?aBp
z!05(Sa^b)NApFt#k=t3MO=<xTvoeFG9H$US|J9(4euoVNYBRZnxRX-0v`lz0L2I2r
zQy#}k=B(@Q6lWY!>Q%li`-%0;A133Z^oP|yc;aF{GxI1US=Q~VF8`h*-{8DQ@!&h_
zqehQdifi0#ntbLd`E8U773??kKd*f6aNsYQk9N-<CmT)v$XLEzG3x-QP0kVJJF4n}
z%PV~EDa#%1w^O>;?BRRl_lK9<c?lO5ri=A*nSSP+v+zjEYaUA#y^b)A7b|z_?cCk=
z*dXO)!u2D%1<AYay;}9{(V96Ok$U<^R(<%IrMYgd@lw4#W=m{0gdM!`en(GnH*d&>
z>RUSRUKU@udf}3A<v(d7t#{T%rpbCM^CTpH-hLn}l9g7sB%HO^sOUpNcag^Yt+#tO
zwVHp6+r3V+>iKWCP}l7j-=@xcKJ%{Gsq4FHa;6qf68qD(M}9?A%jwUFTi2{P)a*Oc
zcf-Xa^J6}*+jK<q=A_TN8#frNjhBelG@Sh`Nw4mo49~RinP=L*{c7L4h3`_#6m2Ib
z$++C|`UvgUGoqjDoWpngZ+-e7rrUBt>lq+%nUIjykRZwC#wM_V@sq^C8ZkCjg|kd8
z3i}TI|1a?Azw-(&Ha9ai31M{w75giU43UX!m%;kd7@Gf|<#BW9n#jn@z@QPzxD#w%
zN<v~{LQ2XOKW#4`y_09oUp}jS)>qd{=cK;ZNgcfto@;dUR-W<n_18IZ_RMAj1A_~?
zT4z0VPoD5Ocg|Z^&y1zZz`)4Rz$oMYgY+MYXMZ@iy=>R{%+EPZPVQKqLc-L<DUYTn
zCJ0POoFY0U@xg?|649gtw#<NJkAxXd=fnp@PMJ9`aM6N^YgPi&amKO-vm+)we7*QV
zM9iG|b7pcB3H|h2d?482z=;D#POMmRXYcpww{gezR@dj<JiRCU&GaMR+yATF`1UVO
z|JUAc&!^qr`lhC4f0oNG*WHyn7&JTz)<VK7g`vs2EPuzrFArsBCzvq!zTjYIfajeD
zNfifH92Yo{aJZ_y(WHRMD_dw`nG`c~vP|KzQ`SHa?>~^o)+{KZy!oQRVh!fQ_g6b?
z<7H-VmY5L6c&R5rSmL0-)a6V^Y<Qe_gm{W9=CTB8GKDN;O8eM&wzcC>h6)FRl`{7?
Kpnc3QAQ%AXo#oj8
new file mode 100644
index 0000000000000000000000000000000000000000..d3fa259f9a8219187920e40ee33e8332118a789e
GIT binary patch
literal 1264
zc$_QucXRU(3GruOVEMqn%>V>k2N)O`umWc{Hw6X;<_aM5Ap-+rz`vqbrfzN_zCgV(
zKru-mmMK3LTj1*$;sF!`smlebi(q1ti1ZKEHv+MNVs=2Bz|SS{Avrg(0BD{DkZ%r@
zb7J6SK9-T1m;z*T0L8R`*o<ZACeI9@AW+N%$d?9U6()by`x&_<K=WB%0Qn%b9PAf>
zmL*mcfZPS-vw<;F07G74ZYof01`zi$Ft7^jc-4NqAiuZ-Xm0{gjTjIs{BL70u9%aY
zkdlzV@F;iLew9Z*bN3k)81HB3GKjI-lPqk+%PPa;qrk=BKb>nX0|Qj;pF=-d6}x`2
zGP4*MEnre(h_Yb@IT2_*6GTm7-9d}S#;J{sE4wrKnVFsWTheL{B%C<GB9fZ$G^K&j
zjjiOufdxSLqxB=Vvq+oN0v={%22D9mA&~y7L5_Z}4Fqa4xrC&T2<~A#s1!PfRj8y<
zAV_@0-9uJhcaJGunfNxO>!0Y3L;Ic9f0%x!HTc7$UE)7H6p}3K=2aTk%`<EWelsEA
z|J-D`!X`~SCAozj_muoT+87D;8~VOivO5&{%luKY&vEe-wa3Em<eaZG2<Id!%O%c~
zvCu3#xxeH1jlO%zc1<4tU21xAcO7QtiTZM8(MC63{zmVR9gm_E+K-rCWKy*?y>40l
zy+Ec-NA}IE3TB%b+qPH5Ru@Lbb}CK3o)q}2%253KxtU(m<D@Tz*{n`^qp0`z%?B>g
zH}lN8RcrfpNkvsDov(LpTg-NrXP#j01@oRB!PpPKAFTgyXpYv3DHEHg>aKQl*!D7R
zs_x#6-fwR$do%aF^wX@ppJJn*RL;A(E^yzqtEb&8)iV!%*Z3o!xH-T{KI}BxstemV
zFLT~0`scIpd%E|X3A&d2{G7&{KN=@r_Y#>LbEiw+r1jz_waGU>oU41jxQgplOo)z?
zn`G7Y^@{VRl=|p>it%fc{(maFj%l)>gc<`RJ`)ns8WJSg+}H#*Fn*FaSR=;9s&JO6
zMPc8e|NjL({dZpB#pY(lCLye@pkjZ8ks&gX?J`(j8bkB{vpjANT@x9385lG|8Fzxs
zOG!vfOh`%D;;HTBqj&Pm`O9au&-&_m>73N}I;o>~!gGy|-pVtcy5~;#Xlv>k7#SE`
z(A7HYseAH-&$)Bnx_V|TT?Ph5h6Y9%{~x6PP(1s?x$R}U&S!qkX>xMM@)Q!LCQf-e
zJuyLGLSl%(l*9)U62Ax}C9q`%BzvUHcswUQAacsgd4Y=-OkA@Pn3yw`J(wLa>EY|e
z4<cgb%%3xpqez%FZ}E|6hXW@L967OK$(_C5tKY^Q+gn|qck}d~@Hf+sd~g4+a^u^-
zIQ?IHzdfIJf9so?n*CWWyIgly?qImcS7ZwhvlNCV@3Q<I2fsX&ot<F9;QNAuodKSQ
z9wb#9SaDq7K*Hgw_C}KeCa-Lvg=JFA%*irkn%}r*B%RUUF#BKv%h3}#CTBEG9r*BF
zYeMef0|yn@mV`E#DBoalGjMsjlEIL_S)iGvS;p_Wb4V9M=!%BOnuKD-MFvuwObiP@
M^W*{TV}1d_00V~NBLDyZ
new file mode 100644
index 0000000000000000000000000000000000000000..cceffbb5fa7c242e0ea371a9cde8d5420a8b35b7
GIT binary patch
literal 1220
zc$_QucXRU(3GruOU^&9T%>V?PcNiEMumWc{Hw6X;<`^LJDgy(fW`U8Gw3}OqFHo-u
zP)rhtWy+7m7Wg`bcmTx$fczYwx(FsV<Bk5o`bHo&P|Oa96Zp9VJ|yQR768rT0rJg(
za!w4q%*QfP6H|a}4xpG85Sy_q-Q<}86a<Qa?2rax6()by`x&_<6+k=o0Qn%b9PAeu
z7;+OUfaWoR>}3OErT~V##N1S%*bE>p2CDbHt}$1<AiuZ-Xs-)UjTjIs{BL70u9%aY
zkdlzV@F??I(ey_@bN3k)81HB3GKjI-lPqk+%PPa;qrk=B63hj32~_Q$LqA#-yMD4V
zvltjHU{Yg<vS9`}5okRVL``DdL5s%5sf~>*yEFNjnVtDt(rOMQoH)TElA7=|rGe3n
zt>nUi1wi<t^&_{lNSo9G9%f|*O*u{>kp8n1_vRfk5NY)n%DB38)i0K~2?+%v#tH}9
zCP--L$j*pZq#?ll>GBbtJ?`@tzgql-_m{@o&O+<$LE3_+KR$o`ySRq;ftdb>f`3&J
zR^J33o!!_!@B0Ib1BdpP-En-e-|F2Xy$>?+iac6XRdc1^iJ7fR<(IzFS9{A|hP~q3
zJel$hALV7s75|hU55K|kea1qUR^iPVZzddcSkTYHyw2GrKw@H8N9Cl#*dlY+!-1E)
zqj#%`A6T=-Z|l8Ow=a_$CET)9!Zw}^^ES+#Wpr`lMb*38)`%K^)AlQpo2b0C%sJXu
z)iCtw7Y&1DU1c7(`YkT34_!1f@%-g0S%K%+Z(I4@GTLPEEK_#&rh7+2*L4?)xNfqO
z3!3~gC4PzT;*5jw)BYtM-MM$m#_VX_t-X9R9TN`yod4D`JfLUdpVRNzkL}SpGv`#W
zA&;`5;Qnl7<|`3eH|>75?tOo2TJ9W?#K{`B-xk|%@O$0%(?w+EhP|g!U;bxtlf1_Q
zj<bY>w1xypHa9kb4UC^84%UdVu_~NpYEjsC=>LC#Pyd})c(J*eu}KK4E2!9CVPuF*
zWV;O3m&VZi|16K2L)Sz`UIqq@P{y6$G?0>zn3#}~a>ZZU%SZ3zne&&=YM=Gh_0l=1
z@8z#^;_R8tCI$u<bhXZU>YhB|bMBnCuAUi7mw|zip@C7x{|D(m6wm%}ZhP6T^O>J>
znw;FRJcWd*i9e3%A3mU<aQH)TKwG18TVP;EqvR6DUd5J$CoT6c*rZ`>yV%81IWqX*
zi31mW4w`RNIegvy;Km&m_LjziPk1iPabEPev0-9k=fptIoz?HZ-`d+#{r%saP17sa
z-_Y-T&;Q?Z!`r`m<GxhCJ+Hez`{vIdbyqt|y30NnFfu%;O`6NV01c-Uh9>W_{2d3s
zJd~ZCV8Y=0f`gp_o;MyORUBAxT;M>$;i~pVlL98MY@vl^Qq0WBGKs0Dqy%RDWGyq6
z=vg2!v$svsRZ`+#9P6fB2@8oM4O_S-q_NCCz|pqCMpWThqCnDwgb9aV>U4MsF5nV1
Z=;qJSa}jY<c4W}8=YGWe0+@?|7yyMv&Wiv5
new file mode 100644
index 0000000000000000000000000000000000000000..20bd5f7d5e3ca372859008c1b02156a65db8a7f5
GIT binary patch
literal 1232
zc$_QucXRU(3GruOV7b7+%>V?Pe;61UumWc{Hw6X;<`f|F8Uq8P{!um_Yd5zLU!Yza
zpqL~O%ak9BE%0>=@c@cN0QtE<brDQ#){Xwb`bHo&P|Oa96Zp9VJ|yQR768o?0rJg(
za!w4q%*QfP6M^P4asb7&fY^*>=_bz%pde7p1jv^LVihKT*83T`B^5wBP5}8JwH)jh
zfR-gz0L^0z0P@+um??lEFEKY2C^iF#D}m~>|E7JGF32w~0ov;WR3iq&3jf;}j4S3O
zC!{1KFg#*S`rPv9XYM|u0^|J*T?R2$dy<8Xcv)q5d=$7COyjtKCPUT!IrO7dvFj%*
zGmC-I0wy(vC>v&w6M@z<LDVGH9kgg{oZ8s9vOANXnc11YC9UQ_!if_sBB=>aQyLiE
z*h(%OSOA1ST0e3-i?m5C;9*u~(3Im80_i_H$=2_%fynXyp0~VoI(Ro$I&Se;`G#-m
zt+lJJFfPq0$rG7#>{Yw=ZAq&FX1)WO7HsMd^w#Xz@s3Z(qc`>E^XLD6e<)^%c_b^?
zcVBnY0YM8p=HolM=P{|Dc=Y;!?uQ@B1=8UseIM{ueDXTAqSSFW`}?Z}Evh~bB>Drb
z3!1jSP%Yp-{ll_ATIV0vor6X@kBdlE%V=~R;u4jS*rjn{ajAnSH&5rY6EBx6GF^O^
zWm=os)K6uudzhD1)f;4mT3*ww$~ka!!r9{Rv&Lo{!^E}9y(X_&5@Y#u-k+dZrrRdm
zdv_vDqsn!;>f5YUeit6pEh!00c8+>3%6jdIZmq34uh(R8-&G~y`AbjEHQN@nboF<q
zf^)&a`Ab%ZY`o{0EzSLJSA+cXxj)$~_Df~$|Kj^Tn5WI~?Ek#0hTR7`t~5P+dC>5(
zwfW^V)td{>-<<Jx+tMdWZYw=z&wrp3U-NMC?Erz#i7oH{Y}AkNNY+$(9P?#1_uR+2
z`xtjQKiSU!iMxb^w1xypHa9kb4UC^84%UdVu_~NpYEjsC=>LC#Pyd})c(J*eu}KK4
zE2!9CVPuF*WV;O3m&VZi|16K2L)Sz`UIqq@P{y5L^HLHL6BANWOnkJxeDqG9Ie+=A
z_E}$DFP)S6TIWvqXlv>kn3xzCT+r1z>#2M4gwMHi-nx2hodyO*h6Y9%{~x6PP(1s?
zx$R}U&S!qkX>xMM@)Q!LCjL0AfB1lc!r>3`0d0-WZGo{Jjgm_odlh>Yp0(V+V3UTi
z?P3>4<;dWJCk|ZjIcUC7<?wa)gBy2P*jpM4J`o6#b=kz;&@i#Fb7G+9&g%EyZ|&`=
z{{HXIrs<XIZ|HZv=l}1y;qBkOabK$6p4Z)<ee>s!x~m-}-DRH(7#Um&QjWvIDTSfQ
zyDWdl!7mSGXD662_`cv^XMpFC2T2tNRvZ^NkZ`!Fz0stA$tzoEVVM*&bF$2`4cGYJ
zoSGS*VBUO?qif>Lj2VGb8Xvw7Rk+>O*zCyTv6>;%Z3AadLdU6K#w0m5MK(@0%Q@@X
gSBNmK3Siv)<G?c~mqZgG7KX<+d4NI8!2AM&0ps`7@&Et;
new file mode 100644
index 0000000000000000000000000000000000000000..8ac98283483717271605853db852cbef8c372e40
GIT binary patch
literal 1164
zc$_QucXRU(3GruOVCiAtW&i??1_nk3tiajLO@V=d*#gKs2*e+vxp&36xrO)w#r^>K
zl0Ym|ek``Y*D=HcXr2U+pUJ?$7{SD5ywN{c-w4D8irE2i0za3)hveME0tN=A7eKx_
zP|k^gm-$#mYGMkI%>fkC0%9|krJFo6fPz3V6Cht2h*g;US?_1$mQ(=kXaPAGgxN1J
zFytmy0L^0z0P@+um??lEFEKY2C^iF#fqrB*P!F0SUXWj00%QaI!XO633jf;}j4S3O
zC!{1KFeI@Gdv2_#o4uar&Kdm%C7$Vg?{BpDI5V;{3wJOv@Ro2YGcZ8a{yFrcRk7<Q
zD>I9M(E=tlhA10mkQ0H{GeOiO)*ZBHY@FKIxUxHwpPAX2za_2aK*EU=EF!51Pg5Eg
z-PlSl99RH^KUzO>JBzeQE#P5RX3&)56awkr8n8R-uz`qek=%u)Q`Rk9u&^~{!$zYW
zD$yJ%8YgGnl~5Ps44W`lF+SwQh4U}+zp(#em%Wu7dDBa?)uW?&-8;+gtPiBt3uK&U
zo+ii5^WjlpLv&5Wor8-F<jM^N-phS25WZ7+=eWX+37WgYzs#Gn>L3&6n&XV`K0YgC
zSKVjxPD${*+;@ee%a7NpIJQ{w@L3pk9}{EE^5B`6=wj{0bCgR+dr6PT7Vj$KkUecW
zZ~vJVa-KQ+Fy-sE6;BPMPZq8b+UfN=Y3r2DtBhv)W#@X_-Vo;XHd6eOeNz9O&<j0p
ze$3u2)g`+{TzYnzZ_@?2@0GDHa>Ad5E=`jAqkGu@P;2(WvTH?*JEg6aVv|mN-=7w}
z_E1aB(egMIv1Mz-kL49x_9a<P-OqD$#jF;soNpB~m##gz{mFL8rNJ8J)z-|{cCOW!
zR=8%-vc=t7KJr$`OkW3%kA#G@h6G7AH#UI{jGrV9)`+pODx76%QP_9r|9^o`|D9KO
zvALPCNeHVesMud&WQa^;yA0Ns#?bu#ERUN**F;8M1_q5##+~5!O-V>hOh`$Y;;HTB
zqj&Pm`OCUmXFYXKp71$$&RbW{%!Gl7%}otx+gtfRJoZZ@>rWW`osjxZ@!+Myhi~w*
ztTE!L{+TKv!IC1u$8pM_;Do^*fusbs%z)&Al#ar4`YUu!`JC4Xyx?&y#K6eFB>01`
z&dCp70}FKY&iSA7VY%6PJg?>)6O$X8nwy*I(|3FCXMZ<(clUSk>9}WmllH6Zv;U)h
zaNGCqRTtlHwcq|{UF!44zeANT>E1efn3drQmoG4bLc=75p~<@}f5*Wu4`pX3m@xRh
z;9zHfr|}0#6$e%v7dVh`xT?L;q=3mQTWDdK6f<+O%tyD?+zIB*2RXVX-prU0IHmF7
z`%s13ZH>*2JRYkVGTk<C_9S$i3T8}_V^d_~WV4)eu5pD3<EjA0%|8x2b8<;E5n^G;
OoyPSTXczMf2nGNm6R7O~
new file mode 100644
index 0000000000000000000000000000000000000000..fa2e6f3aa6dbcd1ecfa65126a69e92a02420e4e7
GIT binary patch
literal 1144
zc$_QucXRU(3GruOV5wl>W&i>X9tK7RtiajLO@V=dSqI464aB!)?VTOm+(LYTVlRMv
zNg$RfKh_=N>loqzG>-+y&tza=j9_9@Kkgr_Zv<ik#q5AMfuBp@Lvn6n0Rscm9U$Kv
zDCfk$%X};&H8BOq<^YOm0kIj&(g&s)KtZ6G36L)h#41ewtoJi=ODceN6o4EI!t56q
z7;+OUfPBUPAfFA4nF1K{5_405Vl#l)1!&iWC7t$+1^LA#KsSp3)rbMH!v8h~<BB=S
z2`LE)43AnrDouX;Gk2d+f$@HZE`u1WnmSe^URD_%9|bN3ryE>Amq6A2IrO7dvFj%*
zGmC-I0wy(vC>v&w6M@z<LDVGH9kgg{oZ8s9vOANXnc11YC9UQ_!if_sBB=>aQyLiE
z*h(%OSOA1ST0e3-i?m5C;9*u~(3Im80_k5mVXqgnqe$!dC;U1h-BZLab9in{RC9F-
zl4R0U$qe4D<6Xv)^5~q%$;~f}JM20iFyHv0G;gk~u8YeFzQcc?z1z8;LGJ@g<FDPv
zj5axl_fM3*vd1mYsdM%T-IAluPt5d=MnB=()myCP`nqTRZGq5@4*aQ4*w%dz+SzHn
z$31VM`a9+Aibso|@cosaaX6`8d9y@LzyW8Y^ftS<0$ftcm$)?li)``UmsVgiO-uMs
z+aBg;W%W8|mwnhY{p-_!mlI=_?u*cUdd710%q!B~Gm~9T-Ov@f#k^S7`1$>>$Ja#3
zTFg(otzs(?{v`C)#-K0%BtHf3;h6g~!Mp3T!PSPKPR;lA)hn+`3BNLVe?4u(2J8JB
zo~lhh+&C%tXY~1!*Pe?eXDO@q?_ahh#ba-=<Ps~Nknf$PaaQjlwU)Anehk*GV=_>z
z;{e4$LPA<Yf+U+8o4^LfPZ9@f#MoFB&N8(q>^t=Tzrd&e&MUmw+|1Y{gw+*P?5{8~
zL?*Ib2FG0*L-YT$JZ=tM6B&6K7&Jl|cY@<IB_S~}AtgmbSI^Vu+%?}dCv?tvob@@c
zVQ6k(kdTm)l#p`a-@)@AoM(M(=ef)u`Al9&$JVCjuEPPX!yB67+Zr7m+7h<{qq&{u
zaH=9(+5|RJHf=Wh=YN_`DNS(Xk~%qy%Z;rqjcZ@0s^mU9uD+v3f1aG#sXFs#<qe&q
zuLKl;$u21+B<S6}-+RmSAK%++zx_?>J?(GakA5Gn7s{!wUoZYQ?sxI&_fchb`{GTN
z?k>7(6T@IL(`79KBrH-Gn!L;McO3lk@UwP;34`AZ4n+oddVY{pabU%9fddJLtJ)cP
zmMBYfFsm=vqBikWC!?E#4X@YX8wL$}c`dTWT}B3f_Hw1%HYhOYV9XR%ILl$yD9967
sspW9yu)-0Ag9`1JLj|TPIf!Z{h{@lG>CotLYhti#<$l8eDJei007);ODgXcg
new file mode 100644
index 0000000000000000000000000000000000000000..2addfc4b29dd49e5e1d500fd031b752365f34300
GIT binary patch
literal 1204
zc$_QucXRU(3GruOVA;aJ%>V?P4h)P8Sb?*fn*sv^a|Doij)8%Z?_7oCR5!N}U!YzM
zpqL~O%akAMj`4L2@c@ds0Qu=abrDQ#^56Y~^^HJmpqL#HC-8F#d`QksEC8Cv0pyzl
z<(wFJnU7_pCZ+({ATcc<He*@(z%&CW2oy5`@}+@Tg~^}wenxId1<;N)Kt4z<2m1vE
zhTOyopm~e|Kt3B7GX*f@CFZ6A#by9;KG3f7i+9`T7UUO~0ND;eHDW-l@V|}0xMEIn
zLP|mcLsI*>OHV55X0PYDb4I^GiDx?B{P;#6XGV5r;SMGS#|*A$1_r3wKZky_Dt7&3
zWo9ujTEL{n5M{#*aw5=rCWxBEx`P&tjZ+&NS9WLeGc!B$x1`k^NH}qVMI<%hX-Wg5
z8(YbR0}FugN9#v!XOT9k1w72k44QJBLLmJ|C;4YF8A=?z-xHM~zSKNJOtfU74-0Fo
z$dN}*C9mt|NX|WIKhe{HZPzQus+MnTvELQ;x|-Kc_PnmU<w1+$ewFvtKR>B5toz8s
zfA#v2I~(2Wir+lGw!1jy@Qv#7828KZR(TJO)tA*A{OPZ?_m`@>v2^Gr_j9tpcN}kj
zEH!Tr`|;8<f3%PDa{Ryek@b1JVdEh|)>xyQ35OgOv~uKrvt^keF+q%D=cG;Zi%r_n
zf<scv?3CpjI<3Siw|VwvxaMBue&V3xG+FOi(wePZn=Yhu8J+Ezura!1L(-DO;2X-z
z`qT8R*NKINP7n?{HP!UEVX6ANE9r5m&1Q*P-maNqm?fY7YX>)XZddH0%Q?A8rB+{>
z8#dX$`?UD~m3jAX*rkgKNAK24W_A1X|NW7v^N&pMoAm!XZ{jMxv{j#cm8$MK^c-2Q
z<vdrWEM@tgRmFEsPtvojUG-f#XU&$<)#tiWCuQ%G6lB>H{j9o8Wj_Po{1Xb`_)17f
zYe<k}b7K?O!1zhxV2v0XtHN2P7KMF>{{I*F^xt`f7n_?In}o2sf{OhWMux~lw##6B
zX$;N(&+@oAbWLRBWnj<<W!wpl|CEHp#DtWT5N$nApL5rIPw8u(Jnwr_NAHB^8Xdir
zXFPS!o$%52JgI-)*wnxvAt5CxA?3oqgXcdu&-&QTbD2N#nY@sWtxeBehXY!N3uec+
zH99)9J)8$jBkerOvjv!qJ({JO&7B*I>l{-9)2iG$o7^@nYns@&aAs3}l4slc{-)%@
z!n&fgB+s-uxxjS_=T<r#IC0>}i4{xk?EPN-HtyKo>iWE!r}u=vnSSJZ`+t=i-~Pqv
z|JwWQ`Lz36-_+FX&vM!2y1Q}*!=oor8yTSClfuyCU6#M&;FpJ=wG&Jj{AO?{GQji0
zgQSWBD~<~sNH|>8-e^+5<dyv>T<wly(@Zx98(y!&Hw+r|@>*n#yNnF}?Bz<iZBSs)
z!I&wkaF)ZYQIIFFQp@4YVTB_K2Nl{chYCzpauC%@5R<<V)1lGh*2Lgo$|J(S{DJ|D
E0rgzQK>z>%
new file mode 100644
index 0000000000000000000000000000000000000000..246154e2eab0b2ce5bb36867ee858e0e4bc519e7
GIT binary patch
literal 1172
zc$_QucXRU(3GruOV41?e%>V=(84QdJSb?*fn*sv^vkQ=U2#8;Oh<v@&%`L<isFwrC
zmjq&&@?+gGzK$UtKrs~{Kb?VrF@lLr`M!U!z7dEG6te^31b!}o56QWS1q=*KUx0jb
zpqvu}FY~dC)Wj4Z8)Tjq5Sy_qePEga6a<Qy0Qu5Dtit5adOst#qylJ156HnF%zlA^
zAvduCXdYt#kk1CjOaTmeiMgpju^B)d33R*kj5Q3q3-XIgfNUM08ZjVN_}|80Trnp(
zAtfP!A!(udy6KO9=I%2pFy7D5We{UkQ^#t=%PPa;qrk-wyqW6+0|Qj;pF=-d6}x`2
zGP4*MEnre(h_Yb@IT2_*6GTm7-9d}S#;J{sE4wrKnVFsWTheL{B%C<GB9fZ$G^K&j
zjjiOufdxSLqxB=Vvq+oN0v={%22D9mA&~yH6ZdBwHV`;wZ&|;<TchMuh_3F*2MLl{
z9@_*?1k71E;cnr-8@!!Ox*U}=`(%If)VA5Vw%a+i*G;>+wpA=i@r(7{+UL848~EZB
z0_x2>WVuT|zPWK&^q#DFfx6Lg-;$%VAB*Yj?R#vt>mIYR>*uCxdqYEY1Qr#%c__BN
zPrBmB^2cgUAD`@C)wGk@E?9MahQs3q;muEOC^R>^@N53Eab^kV@Gw^Sl46q_l{_V8
zR^ZOP=TBBAEaG3TbU(%I+ogRx%%UeFlQu=_98X<&*~V~ZT7;*o(_1k+u@EV}UfK7?
zX;GJ>F4-9+ANe27z0~v4+Wi-fy@;LpG<)-v<Fz6zC4UVM_upE%_V$g<3E#DEe{T3F
zr2OuGc+_i=wh)bX?>UoJeNS7}eb%e3*`e>rdfAK1+xC50{c&z}SaDfIK=l6UTRxup
z7Ja?%=PHe#Zblqe7OZ-DhP8&#u5(H=10-$|64DwHB-z~91U4{!k~ml+#>T2}mZ?Qy
z-=Y8i1wQ?EUg5>&X2vEVtgfJ9e}$1DGLh{vSYH}L^Z&CvZVp`&8F?8PG(s77g3U`w
zNK8yfNtvRh=jn6qn(rxn&6DSK&z<nm_B^S7-q_f{AR!?oDIw*;zk}yLIM4dn&U2YR
z@|nDlj;&44U55i&hd0cPZ)<dPXiJ<9Oatvahm{rC(k8H(vQ@WnJpa>lYSEc5JwmL#
zDZZ@A%+AKF_m52CiI-<R{^-%4CufdKI`hY7j@jcfAprv;14AP%&GPqu<91Ike1C6#
z^f#mTqQ6x?{(WfAxoz+M>)mzh|L%NRu4^rSf4$Viyo-5qdJHyuycfa3B!!{LyDWdl
z!7mR#YbTg6_|4!@WPqpl2T2tNRvZ^NkZ`!Fz0stA$t(L&m{Od>qM2?EHoRVkZx}S_
z<+aEfcNrP{*~^u3+n~UpgE3Q7;Vg$)qaaUUrIy2)!wN?f4l1-?4i%WH<RGe*ASQn!
TrbDC0t%*Uukq4L~UqCPbbVjqc
new file mode 100644
index 0000000000000000000000000000000000000000..02dd5153115a78ee427ef0fb69e565d48f6bc187
GIT binary patch
literal 1176
zc$_QucXRU(3GruOV41<d%>V=(a~K#IumWc{Hw6X;W)~pyC=h?X_MGpgn_Gx4P%jUV
zFA2ml<;S{Xd>unPfMOazemVmKV+0eM48MP{z7dEG6te^31b!}o56QWS1q=*KUx0jb
zpqvu}FY~dC)Wj4Zn*%7O1;l17OCOkK00n_!CP2P45UVixv)<3hEvW$7F#+UY5N5x?
zz>u3*0W^;>0LW(pW2OLxyu{p8px6u`js&{h!z6QtK|y|T36QM=R3iq&3jf;}j4S3O
zC!{1KFeKUSuU`K6XYM|u0^|J*T?R2$HFc~;ysR=jJ_=k6ffKp*GcZ8a{yFrcRk7<Q
zD>I9M(E=tlhA10mkQ0H{GeOiO)*ZBHY@FKIxUxHwpPAX2za_2aK*EU=EF!51Pg5Eg
z-PlSl99RH^KUzO>JBzeQE#P5RX3&)56awkrI&pv2VFQ6<_SW?ayiZ)wY@X)CRl324
z!*iR!iGWF_y9;`BC*3&q>CwUq>{@q!bJWh`i~a2yc3do~qjksplJ9fh`!TRpw6=fS
zefZkOcELEA-#d;jf6Vvn-sZ=8Zui<B%eB=#+0j4o{}GS%)rT)H4voEWSY~RnJby*`
z9mkivGUYS>pDpA+ByaUD`Q7@BZjC~`n=_W22yjm5k@&tv(8=SBhqXqP(JftnZHaYF
zfw%5u7xAs>-n6o0*3~4L``sE-j5h|CZoAzz)zEcK=vwYJNiC7B?kpAo*+2dZ=S#iL
zJi)0QxX1SL?Br8gdfdyxJ{nqN1kVdee)Vk1l2taZYoyw~{L{SZ?`gJe@~vB4myY-)
zI&~hH^n3r-?OC0yrIUVFZ@M8;xmR<QiQq*Q5$})D{UxVe6`D_{tT#Jmn<^T-C0}ce
z%Pr0=wF&b>+TLp%3h>i&ja@xSzm{wM3kFEsBqXFYBuKKku?cKo{3LO(MvRSB;Ve^&
z!oEZQ{|kKj@4Ui`&CQHWLReiv#r_H-Lu4Y`Ww5?9hUWigdE6YjCNlCeFldA_?gX2c
zl8~5~kdiV*SI^Vu+%?~mI(jEO*XZc2Jmcx>uXEz;8Dm2OgM@^Xq=b|U{|=u2;5_SN
zJI`hQ$Y=6GI<_`FcO4FB9WH2yZ)<dPXnVK;m<-x^4reQ}rA=TnWm9jnc>bs9)S@$A
zdW2YeQ+!#KnVpSU?;n}O6EDwt{L!O7PtF{fbmosu&9uj*5&{NB28Kpjn&t2R#_gV7
z`2ODf=x;{vMSrV){QJ<JbKBni*SqW1|K0htT-RFu{(7m2c^C8K^cd_L{j=fWlETpB
zU6#M&;FpJ=wG&Jj{AO?{GQiXPgQSWBD~<~sNH|>8-e^+5<drS7d`>;1U{_B=h18U`
zjfo7gcQ~xmfC>CZwdjd$i4PJ4m@jEL%o5CDQkGcsDWqXWyJLrAlOx~q)e2g!4O$@w
ZbnG_l6bO{)WnuK~<<VeZ21XDF0|4v8v^D?$
new file mode 100644
index 0000000000000000000000000000000000000000..173907405bdd3af04d7f563d4903ab3d995f899c
GIT binary patch
literal 1136
zc$_QucXRU(3GruOU@2hWW&i^ABMgiTSb?*fn*sv^vj&j46Nu06KgiGO<`&`$6ng^X
zO9HV>`LXU8U&jy+pm`uI=?n~v5ln2_>-~fEjX-Rmm>m!&@N)@#NX|_xU|?Xn0pyzl
z<(wFJnU7_pCZ+({96&KGAU0!J`oJ^;C<qiY0rI7RScS=-^?pWfNd?f33?Lt*mV^BQ
z14C|N1<*Xk03e?YjF|!$@)C1Xfnqa&7-%ok6u!jtR|WaSB|tX|0M&>AvBLj02IGo3
z$q6Y52@H?=uCBWF@sC-&<c^u~46YJ-vUdBKX0|c$v8f5LGT7hZ0{RxJ_Rpaot%_Yg
zS(#Z3j21AdF+|xggPaJoo(ZBRvF@NnW8>7u#+BWf{LIYG{4HrU2NF)4U=c}8c$(6{
z=*CuZ;lKhQ{L%W6+gYSdY5@<kGJ~cZrw~a0+zETL4jYKH_J0b_XrHu@f5FlT4zf#l
z6i<pVNqX&RTX%>hx+UV^wMlFFFRE*lyuGxs`u>9Vmy;t~gSc8fI)1-=b;q7@$1!b|
zTczE;DUFQtl*$WQzn_S`!zsD5y|G~8?<dbsh(Gbya(zE(`)`4(8yZ|hcQ#A!ae4QM
zQGR0e1f||P>h^B?6!s^wwk!LcNt(F0A^n)5QBQ_?z=au;f+Y5dY>~b<ZN*O6P_rMp
zJ6XcsRjkU1tFS!dR<!6#$g*SNFWvNB-%7n!@mgcGWTM1w_d{K6SNYEbBpu5->-N@n
zn}1QU&5fdEBDVFX>bR~gWR}ly{i#=Czsx7;;OF?WVM`64?|$}ko=kT5udS;;&7IRT
zHRi<qy%(20@AR9Md0r~_beuNZwt9_bpZ0UN_ieZvYWm+-Yw2;{gT<?A7^lt1$%KS|
zLPA<Yf+U+8o4^LfPZ9@f#MoFB&N8(q>^t=Tzrd&e&MUmw+|1Y{gw+*P?5{8~L?*Ib
z2J1^>X#Rhe$IYQ@A|o#YgGMOhPOy0?35kgbDJdd4dY(S#uKD`woH%=Cvw?v@LPAPX
zLdu1I2hV?Sp7pVv=Q4lfGkGB$TbrJ{4hOUj8??u_H99)9CGG=8Z#&Q7LPfT;32dfp
z#%$)#|1_OibmmKs5NmIWFRL=MvoY)aBa?XI<yntEdi3YXnIn_V{IMzFHk4st>EZF|
z>G3*sw|f8ecd2)Fe}6u0@0seOb)Gf#Km40=-@pI5=zey6-p|<6=Z=40?Y1O(%j|Y;
zhUIhY(->f3k;2gAU6#M&;FpJ=wG&Jj{AO?{GQiXFgQSWBD~<~sNH|>8-e^+5<drQH
zruE)o$xJr~8(y!&Hw+r|@>*n#yNnF}?Bz<iZBSs)!I&wkaF)ZYQIIFFQp@4YVTB_K
l2Nl{chYCzpauC%@5R<<V)1lGh*2KW($$f)?`2_<Q0|5V|rh@<g
new file mode 100644
index 0000000000000000000000000000000000000000..c8db7da1f71085fa192009f054c896c03f68593a
GIT binary patch
literal 1188
zc$_QucXRU(3GruOU|GVz%>V=(PZ$^(umWc{Hw6X;W)C3qBoP0vD>QL+a|`hW>J<U<
zC4pF`{8)F4uVV;M4T}MgpUJ?$7{SCQq3$27Zv<ik#q5AMfuBp@Lvn6n0Rscm4<O$h
zDCfk$%X};&H8BOq<^YOm0kIj&(g&s)KtZ6G36L)h#41ewtoJi=ODceN%mFzVgxN1J
zFytmy0L^0z0P@+um??lEFEKY2C^iF#6M$}SNn+gERghm?0%U{SCkDg{|JxXhE9N98
zq$DITJQ7(~@w%dJ_IjQ>XY?DCc&78sk8kvGW@Kj;?qFh2=jA-azyMYI=g^N<#jc;M
z%q#{*3z*axqHLH!P6S%d1W}V%chI7-acX1Z%I-{lW@cypmb97!2`5glh@>VwO=)0s
zV=K9EU;z;RX#L3TEYc>mfQMO`K~s)X2&8}Sr1M^f9R!Z;UsCVD^6|~ly&7*7Rd0JO
zJnFpULi3jE_mXFsd3&3uY)`m%O#Q=>{zn(fKYDM96BU`2I`jU!Gv+gAvK(NId$_PN
zZ$rTw#|LLO_G`btQ^@`OUiV|Omio6ln!W#@ES&!Reb1L$={C04R(09BXvqBDak%}l
z*u1^b$8%loEq~1S*`;rHM@_S&Nzs%ui#E8joz|-MxUgJSspZIwEiOL(Zo416WNh;`
z_PYALtA-)XN}qe{*)=azYYlmxM(8BZ))jwrP-5<i8(L=#oLzn1@-eQg`aMgM=k2`j
zsc-d9YHBV{Km2*(J;Bv0#d>-t$0n5S3sbv)eCfugcBV6L-AK0Ma(4Tp`PQHFd1?E$
zZLH6yT)Qk8pl0y&U%tPuSSRP#Nx!Q<&3N5B_jS_ju7?UD!5^dF^GuxVz;^YEztJnv
zqpVNQ+ACzuT%&&ZOKWJ9L|wUHTTy^$Mc1l*3@c`Ak^sd|LPA<Yf+U+8o4^LfPZ9@f
z#MoFB&N8(q>^t=Tzrd&e&MUmw+|1Y{gw+*P?5{8~L?*Ib2FGg}L-YT$JZ=tM6B&6K
z7&Jl|cY@<PB_S~}Atfb5N6*ve+%?^k`W`2B^iFuL(a~FZ##8s)2_J1uT>~=%gM@^X
zq=b|U{|=u2;5_SNJI`hQ$Y=6GI<_`FcO4FB9WH2#Z)<dPXnVK^m>k-94woykrA=Tn
zWixLxdH$#A)S@$AdW2YeQ+!#KnVpSU?;n}O6EDwt{L!O7PtF{fbmotZ6Q9Bh2L)gP
zO-cy~dUx;l-ZK5i_x9Rvf0KGo`<wTp--qjka;odsi~o)LU3~g|RGHnrcvGdji|*RQ
zFkF5b3d(elut{NP@-EBYaq!E-&)Nwl41O~>6dB-Y|3Om4ffdIE4kR3|YHu_tVDidN
zn*Qvb<DyE11ePP07nt<$GQW7_cEfP72J_+js~xuSGP5^JObBDV)RQ1AaZq6Da;76T
pJWf19JVh3<tbv+LAq$z(J~p0h?KqU7!oiUJmd6HY7xN1U1^~6(!`J`-
new file mode 100644
index 0000000000000000000000000000000000000000..85879065951ac466e6b018339d8ede5cc7ca2793
GIT binary patch
literal 1156
zc$_QucXRU(3GruOU}<6CW&i>X9tK7RtiajLO@V=d*#yYk3&giWjAN?Y+(LYTVqbuK
zNg$RfKh_=N>loqzG*1A?&tza=j9_BZpY9*5Zv<ik#q5AMfuBp@Lvn6n0Rscm6CmFl
zDCfk$%X};&H8BOq<^YOm0kIj&(g&s)KtZ4w$X;n6R$=mIy`Pa=QUSE12IOE6X1~C|
zkegTmG><U=$Y%p%rT~V##N1S%*bE@{1lo1MjkCnRAiuZ-=w=z98ZjVN_}|80Trnp(
zAtfP!;Sqo8%l^kdbN3k)81HB3GKjINsbe+bWtHLaQQ%@IILV#GzyMYI=g^N<#jc;M
z%q#{*3z*axqHMrH1GJtAq9(EKphaWj)W*h@-I@H%%+CBRX*CBDPMly7Nlkc~(!l7(
zR&wFM0wDa+`jOjNq)lo853@3ZrW~gbNdM9ad$SH3h_v>9Vwa7wN_n$j=>#6Wpf(31
zQx>BsDQ9DnCq=L*T@Pxr@&3)Tw04q{`~vUzCC4|N2=LN8a%kWEy>{W>*$;@uDFoPW
zk8lxt@yN2MdDiz6z8Zz%J6qRPblo|&^+(4Y$L)olOEY3G?B<UY<^B@jmiK@yf8y+l
z1L;r1sva2@NeAt5duQ;kRZ@pbrn38>n5B)%`!g4o%Q`tGyF@8Hoj<3{F`w&r@=7Dc
zyc5+4Y2C@0+jdUhyGFm=tN)47*Plg-%g^0Bv*cdHDH-FSXYVFVSoNyUTsr54?2F#W
ztyTqgXUjUbx1K(GOulPlP|v@apMq`p=l)FaKDV7Om{()!j1PbF^|wY{_W$AQH~Zk7
zcYp6pyS-J0Z_1n#_p8z(`i19)NLyC_5lz>a8T3AeZ6%-Z)!L-3da~bjre11Z^<$^#
ze#Yet^=lX)agmUa){r2{=Ef$lf$@{X!5T3(R)w=nEeiV%{r@lU>A&*|FE%$bHVI*M
z1r_@%j0};9Y?s0M(iocmpXG6L=$gpL%fO%!%D5A3UP?k@VnRxah_0Te&$(;5C-pVX
zo$%4t)ZJiWV33fIl9Z5g;orgYADm}>Z0EVmANfpPNXOQu=dQy6t-~9d;@cV>9oiDN
z0;9W~=WwbbTiOIRQ#Nfj`{#d}PAxj~rALUhH^rA#nc3Nx_5P7bJn{0Z#~(fV^W@Bt
zNoW4p<V;H{<q$A1GB7mK(ky@fH*WXz!uR*)M}ISVFZx^c<KKt&oZI&9zusN9{_oDG
z<+|4L_t#5J%)6K;r^isL?y3zBj}(R`@3Q<I2fsZ0tes%O;5UOqkpZ5zA0$;ASaDq7
zK*Hgw_C}KeCa>%yFU{-;8DcyVj2}%jc#{t^?Ajq%c1GjWfe+ucCgdJIa8QA5Noa$K
z@(mU@1DB^O84UTG1)5o!W&FY&L%JA3S2RS{Bor$yGLYhAV)&88eFSJ1^9u+D0Bt*@
A#Q*>R
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/mochitest.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+
+[test_fraction-parameters.html]
+support-files =
+ fonts/fraction-axisheight7000-rulethickness1000.woff
+ fonts/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff
+ fonts/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff
+ fonts/fraction-denominatorgapmin4000-rulethickness1000.woff
+ fonts/fraction-denominatorshiftdown3000-rulethickness1000.woff
+ fonts/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff
+ fonts/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff
+ fonts/fraction-numeratorgapmin9000-rulethickness1000.woff
+ fonts/fraction-numeratorshiftup11000-rulethickness1000.woff
+ fonts/fraction-rulethickness10000.woff
+[test_stack-parameters.html]
+support-files =
+ fonts/stack-axisheight7000.woff
+ fonts/stack-bottomdisplaystyleshiftdown5000.woff
+ fonts/stack-bottomshiftdown6000.woff
+ fonts/stack-displaystylegapmin4000.woff
+ fonts/stack-gapmin8000.woff
+ fonts/stack-topdisplaystyleshiftup3000.woff
+ fonts/stack-topshiftup9000.woff
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/test_fraction-parameters.html
@@ -0,0 +1,244 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Fraction parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Element mfrac correctly uses the fraction parameters from the MATH table.">
+<!-- Copyright 2016 MathML Association
+ Licensed under 3-Clause BSD-License -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ math, mspace {
+ font-size: 10px;
+ }
+ @font-face {
+ font-family: axisheight7000-rulethickness1000;
+ src: url("fonts/fraction-axisheight7000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: denominatordisplaystylegapmin5000-rulethickness1000;
+ src: url("fonts/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: denominatordisplaystyleshiftdown6000-rulethickness1000;
+ src: url("fonts/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: denominatorgapmin4000-rulethickness1000;
+ src: url("fonts/fraction-denominatorgapmin4000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: denominatorshiftdown3000-rulethickness1000;
+ src: url("fonts/fraction-denominatorshiftdown3000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: numeratordisplaystylegapmin8000-rulethickness1000;
+ src: url("fonts/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: numeratordisplaystyleshiftup2000-rulethickness1000;
+ src: url("fonts/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: numeratorgapmin9000-rulethickness1000;
+ src: url("fonts/fraction-numeratorgapmin9000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: numeratorshiftup11000-rulethickness1000;
+ src: url("fonts/fraction-numeratorshiftup11000-rulethickness1000.woff");
+ }
+ @font-face {
+ font-family: rulethickness10000;
+ src: url("fonts/fraction-rulethickness10000.woff");
+ }
+</style>
+<script>
+ var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+ var epsilon = 1;
+
+ function getBox(aId) {
+ return document.getElementById(aId).getBoundingClientRect();
+ }
+
+ var test_loaded = async_test("Page Loaded");
+ window.addEventListener("load", function() {
+ // FIXME: Use an API to wait for the Web fonts to arrive.
+ window.setTimeout(runTests, 250);
+ });
+
+ function runTests() {
+ test(function() {
+ var v1 = 7000 * emToPx;
+ var v2 = 1000 * emToPx;
+ assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
+ v1 + v2 / 2, epsilon, "mfrac: axis height");
+ }, "AxisHeight");
+
+ test(function() {
+ var v1 = 5000 * emToPx;
+ assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
+ v1, epsilon, "mfrac: denominator gap");
+ }, "DenominatorDisplayStyleGapMin");
+
+ test(function() {
+ var v1 = 6000 * emToPx;
+ assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
+ v1, epsilon, "mfrac: denominator shift");
+ }, "DenominatorDisplayStyleShiftDown");
+
+ test(function() {
+ var v1 = 4000 * emToPx;
+ assert_approx_equals(getBox("den0004").top - getBox("ref0004").bottom,
+ v1, epsilon, "mfrac: denominator gap");
+ }, "DenominatorGapMin");
+
+ test(function() {
+ var v1 = 3000 * emToPx;
+ assert_approx_equals(getBox("den0005").top - getBox("ref0005").bottom,
+ v1, epsilon, "mfrac: denominator shift");
+ }, "DenominatorShiftDown");
+
+ test(function() {
+ var v1 = 8000 * emToPx;
+ assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
+ v1, epsilon, "mfrac: numerator gap");
+ }, "NumeratorDisplayStyleGapMin");
+
+ test(function() {
+ var v1 = 2000 * emToPx;
+ assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
+ v1, epsilon, "mfrac: numerator shift");
+ }, "NumeratorDisplayStyleShiftDown");
+
+ test(function() {
+ var v1 = 9000 * emToPx;
+ assert_approx_equals(getBox("ref0008").top - getBox("num0008").bottom,
+ v1, epsilon, "mfrac: numerator gap");
+ }, "NumeratorGapMin");
+
+ test(function() {
+ var v1 = 11000 * emToPx;
+ assert_approx_equals(getBox("ref0009").top - getBox("num0009").bottom,
+ v1, epsilon, "mfrac: numerator shift");
+ }, "NumeratorShiftDown");
+
+ test(function() {
+ var v1 = 10000 * emToPx;
+ assert_approx_equals(getBox("den0010").top - getBox("num0010").bottom,
+ v1, epsilon, "mfrac: rule thickness");
+ }, "FractionRuleThickness");
+
+ test_loaded.done();
+ }
+</script>
+</head>
+<body>
+ <p>
+ <math style="font-family: axisheight7000-rulethickness1000;">
+ <mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: denominatordisplaystylegapmin5000-rulethickness1000;">
+ <mspace id="ref0002" width="3em"
+ height=".5em" depth=".5em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em"/>
+ <mspace width="3em" height="1em" id="den0002" mathbackground="blue"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: denominatordisplaystyleshiftdown6000-rulethickness1000;">
+ <mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em"/>
+ <mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: denominatorgapmin4000-rulethickness1000;">
+ <mspace id="ref0004" width="3em"
+ height=".5em" depth=".5em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em"/>
+ <mspace width="3em" height="1em" id="den0004" mathbackground="blue"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: denominatorshiftdown3000-rulethickness1000;">
+ <mspace id="ref0005" width="3em" height="1em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em"/>
+ <mspace width="3em" depth="1em" id="den0005" mathbackground="blue"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: numeratordisplaystylegapmin8000-rulethickness1000;">
+ <mspace id="ref0006" width="3em"
+ height=".5em" depth=".5em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em" depth="1em" id="num0006" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: numeratordisplaystyleshiftup2000-rulethickness1000;">
+ <mspace id="ref0007" width="3em"
+ depth="1em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: numeratorgapmin9000-rulethickness1000;">
+ <mspace id="ref0008" width="3em"
+ height=".5em" depth=".5em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em" depth="1em" id="num0008" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: numeratorshiftup11000-rulethickness1000;">
+ <mspace id="ref0009" width="3em"
+ depth="1em" mathbackground="green"/>
+ <mfrac>
+ <mspace width="3em" height="1em" id="num0009" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: rulethickness10000">
+ <mfrac>
+ <mspace width="3em" height="1em" id="num0010" mathbackground="blue"/>
+ <mspace width="3em" depth="1em" id="den0010" mathbackground="green"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/mathml/imptests/test_stack-parameters.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Stack parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Element mfrac correctly uses the stack parameters from the MATH table.">
+<!-- Copyright 2016 MathML Association
+ Licensed under 3-Clause BSD-License -->
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ math, mspace {
+ font-size: 10px;
+ }
+ @font-face {
+ font-family: axisheight7000;
+ src: url("fonts/stack-axisheight7000.woff");
+ }
+ @font-face {
+ font-family: bottomdisplaystyleshiftdown5000;
+ src: url("fonts/stack-bottomdisplaystyleshiftdown5000.woff");
+ }
+ @font-face {
+ font-family: bottomshiftdown6000;
+ src: url("fonts/stack-bottomshiftdown6000.woff");
+ }
+ @font-face {
+ font-family: displaystylegapmin4000;
+ src: url("fonts/stack-displaystylegapmin4000.woff");
+ }
+ @font-face {
+ font-family: gapmin8000;
+ src: url("fonts/stack-gapmin8000.woff");
+ }
+ @font-face {
+ font-family: topdisplaystyleshiftup3000;
+ src: url("fonts/stack-topdisplaystyleshiftup3000.woff");
+ }
+ @font-face {
+ font-family: topshiftup9000;
+ src: url("fonts/stack-topshiftup9000.woff");
+ }
+</style>
+<script>
+ var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+ var epsilon = 1;
+
+ function getBox(aId) {
+ return document.getElementById(aId).getBoundingClientRect();
+ }
+
+ var test_loaded = async_test("Page Loaded");
+ window.addEventListener("load", function() {
+ // FIXME: Use an API to wait for the Web fonts to arrive.
+ window.setTimeout(runTests, 250);
+ });
+
+ function runTests() {
+ test(function() {
+ var v = 7000 * emToPx;
+ assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
+ v, epsilon, "mfrac: axis height");
+ }, "AxisHeight");
+
+ test(function() {
+ var v = 5000 * emToPx;
+ assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
+ v, epsilon, "mfrac: denominator shift");
+ }, "BottomDisplayStyleShiftDown");
+
+ test(function() {
+ var v = 6000 * emToPx;
+ assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
+ v, epsilon, "mfrac: denominator shift");
+ }, "BottomShiftDown");
+
+ test(function() {
+ var v = 4000 * emToPx;
+ assert_approx_equals(getBox("den0004").top - getBox("num0004").bottom,
+ v, epsilon, "mfrac: gap");
+ }, "DisplayStyleGapMin");
+
+ test(function() {
+ var v = 8000 * emToPx;
+ assert_approx_equals(getBox("den0005").top - getBox("num0005").bottom,
+ v, epsilon, "mfrac: gap");
+ }, "GapMin");
+
+ test(function() {
+ var v = 3000 * emToPx;
+ assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
+ v, epsilon, "mfrac: numerator shift");
+ }, "TopDisplayStyleShiftUp");
+
+ test(function() {
+ var v = 9000 * emToPx;
+ assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
+ v, epsilon, "mfrac: numerator shift");
+ }, "ToShiftUp");
+
+ test_loaded.done();
+ }
+</script>
+</head>
+<body>
+ <p>
+ <math style="font-family: axisheight7000;">
+ <mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
+ <mfrac linethickness="0px">
+ <mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: bottomdisplaystyleshiftdown5000;">
+ <mspace id="ref0002" width="3em" height="1em" mathbackground="green"/>
+ <mfrac linethickness="0px">
+ <mspace width="3em"/>
+ <mspace width="3em" depth="1em" id="den0002" mathbackground="blue"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: bottomshiftdown6000;">
+ <mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
+ <mfrac linethickness="0px">
+ <mspace width="3em"/>
+ <mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: displaystylegapmin4000;">
+ <mfrac linethickness="0px">
+ <mspace width="3em" height="1em" id="num0004" mathbackground="blue"/>
+ <mspace width="3em" depth="1em" id="den0004" mathbackground="green"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: gapmin8000;">
+ <mfrac linethickness="0px">
+ <mspace width="3em" height="1em" id="num0005" mathbackground="blue"/>
+ <mspace width="3em" depth="1em" id="den0005" mathbackground="green"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math display="block" style="font-family: topdisplaystyleshiftup3000;">
+ <mspace id="ref0006" width="3em" depth="1em" mathbackground="green"/>
+ <mfrac linethickness="0px">
+ <mspace width="3em" height="1em" id="num0006" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+ <p>
+ <math style="font-family: topshiftup9000;">
+ <mspace id="ref0007" width="3em" depth="1em" mathbackground="green"/>
+ <mfrac linethickness="0px">
+ <mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
+ <mspace width="3em"/>
+ </mfrac>
+ </math>
+ </p>
+ <hr/>
+</body>
+</html>
--- a/layout/mathml/moz.build
+++ b/layout/mathml/moz.build
@@ -3,17 +3,20 @@
# 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/.
with Files('**'):
BUG_COMPONENT = ('Core', 'MathML')
if CONFIG['ENABLE_TESTS']:
- MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+ MOCHITEST_MANIFESTS += [
+ 'imptests/mochitest.ini',
+ 'tests/mochitest.ini',
+]
UNIFIED_SOURCES += [
'nsMathMLChar.cpp',
'nsMathMLContainerFrame.cpp',
'nsMathMLFrame.cpp',
'nsMathMLmactionFrame.cpp',
'nsMathMLmencloseFrame.cpp',
'nsMathMLmfencedFrame.cpp',
--- a/layout/mathml/nsMathMLmfracFrame.cpp
+++ b/layout/mathml/nsMathMLmfracFrame.cpp
@@ -328,16 +328,21 @@ nsMathMLmfracFrame::PlaceInternal(DrawTa
7 * defaultRuleThickness : 3 * defaultRuleThickness;
if (mathFont) {
minClearance =
mathFont->GetMathConstant(displayStyle ?
gfxFontEntry::StackDisplayStyleGapMin :
gfxFontEntry::StackGapMin,
oneDevPixel);
}
+ // Factor in axis height
+ // http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2
+ numShift += axisHeight;
+ denShift += axisHeight;
+
nscoord actualClearance =
(numShift - bmNum.descent) - (bmDen.ascent - denShift);
// actualClearance should be >= minClearance
if (actualClearance < minClearance) {
nscoord halfGap = (minClearance - actualClearance)/2;
numShift += halfGap;
denShift += halfGap;
}
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -164,16 +164,17 @@ CSS_KEY(bold-script, bold_script)
CSS_KEY(bolder, bolder)
CSS_KEY(border-box, border_box)
CSS_KEY(both, both)
CSS_KEY(bottom, bottom)
CSS_KEY(bottom-outside, bottom_outside)
CSS_KEY(break-all, break_all)
CSS_KEY(break-word, break_word)
CSS_KEY(brightness, brightness)
+CSS_KEY(browser, browser)
CSS_KEY(bullets, bullets)
CSS_KEY(button, button)
CSS_KEY(buttonface, buttonface)
CSS_KEY(buttonhighlight, buttonhighlight)
CSS_KEY(buttonshadow, buttonshadow)
CSS_KEY(buttontext, buttontext)
CSS_KEY(capitalize, capitalize)
CSS_KEY(caption, caption)
@@ -275,16 +276,17 @@ CSS_KEY(flat, flat)
CSS_KEY(flex, flex)
CSS_KEY(flex-end, flex_end)
CSS_KEY(flex-start, flex_start)
CSS_KEY(flip, flip)
CSS_KEY(forwards, forwards)
CSS_KEY(fraktur, fraktur)
CSS_KEY(from-image, from_image)
CSS_KEY(full-width, full_width)
+CSS_KEY(fullscreen, fullscreen)
CSS_KEY(grab, grab)
CSS_KEY(grabbing, grabbing)
CSS_KEY(grad, grad)
CSS_KEY(grayscale, grayscale)
CSS_KEY(graytext, graytext)
CSS_KEY(grid, grid)
CSS_KEY(groove, groove)
CSS_KEY(hard-light, hard_light)
@@ -365,16 +367,17 @@ CSS_KEY(ltr, ltr)
CSS_KEY(luminance, luminance)
CSS_KEY(luminosity, luminosity)
CSS_KEY(mandatory, mandatory)
CSS_KEY(manipulation, manipulation)
CSS_KEY(manual, manual)
CSS_KEY(margin-box, margin_box)
CSS_KEY(markers, markers)
CSS_KEY(match-parent, match_parent)
+CSS_KEY(match-source, match_source)
CSS_KEY(matrix, matrix)
CSS_KEY(matrix3d, matrix3d)
CSS_KEY(max-content, max_content)
CSS_KEY(medium, medium)
CSS_KEY(menu, menu)
CSS_KEY(menutext, menutext)
CSS_KEY(message-box, message_box)
CSS_KEY(middle, middle)
@@ -530,16 +533,17 @@ CSS_KEY(space-around, space_around)
CSS_KEY(space-between, space_between)
CSS_KEY(space-evenly, space_evenly)
CSS_KEY(span, span)
CSS_KEY(spell-out, spell_out)
CSS_KEY(square, square)
CSS_KEY(stacked-fractions, stacked_fractions)
CSS_KEY(start, start)
CSS_KEY(static, static)
+CSS_KEY(standalone, standalone)
CSS_KEY(status-bar, status_bar)
CSS_KEY(step-end, step_end)
CSS_KEY(step-start, step_start)
CSS_KEY(sticky, sticky)
CSS_KEY(stretch, stretch)
CSS_KEY(stretch-to-fit, stretch_to_fit)
CSS_KEY(stretched, stretched)
CSS_KEY(strict, strict)
@@ -701,16 +705,17 @@ CSS_KEY(menuarrow, menuarrow)
CSS_KEY(menuimage, menuimage)
CSS_KEY(menuitemtext, menuitemtext)
CSS_KEY(menulist, menulist)
CSS_KEY(menulist-button, menulist_button)
CSS_KEY(menulist-text, menulist_text)
CSS_KEY(menulist-textfield, menulist_textfield)
CSS_KEY(meterbar, meterbar)
CSS_KEY(meterchunk, meterchunk)
+CSS_KEY(minimal-ui, minimal_ui)
CSS_KEY(range, range)
CSS_KEY(range-thumb, range_thumb)
CSS_KEY(sans-serif, sans_serif)
CSS_KEY(sans-serif-bold-italic, sans_serif_bold_italic)
CSS_KEY(sans-serif-italic, sans_serif_italic)
CSS_KEY(scale-horizontal, scale_horizontal)
CSS_KEY(scale-vertical, scale_vertical)
CSS_KEY(scalethumb-horizontal, scalethumb_horizontal)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -11856,17 +11856,17 @@ CSSParserImpl::ParseImageLayersItem(
RefPtr<nsCSSValue::Array> positionArr = nsCSSValue::Array::Create(4);
aState.mPosition->mValue.SetArrayValue(positionArr, eCSSUnit_Array);
positionArr->Item(1).SetPercentValue(0.0f);
positionArr->Item(3).SetPercentValue(0.0f);
aState.mSize->mXValue.SetAutoValue();
aState.mSize->mYValue.SetAutoValue();
aState.mComposite->mValue.SetIntValue(NS_STYLE_MASK_COMPOSITE_ADD,
eCSSUnit_Enumerated);
- aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_AUTO,
+ aState.mMode->mValue.SetIntValue(NS_STYLE_MASK_MODE_MATCH_SOURCE,
eCSSUnit_Enumerated);
bool haveColor = false,
haveImage = false,
haveRepeat = false,
haveAttach = false,
havePositionAndSize = false,
haveOrigin = false,
haveComposite = false,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -928,20 +928,17 @@ const KTableEntry nsCSSProps::kImageLaye
{ eCSSKeyword_contain, NS_STYLE_IMAGELAYER_SIZE_CONTAIN },
{ eCSSKeyword_cover, NS_STYLE_IMAGELAYER_SIZE_COVER },
{ eCSSKeyword_UNKNOWN, -1 }
};
const KTableEntry nsCSSProps::kImageLayerModeKTable[] = {
{ eCSSKeyword_alpha, NS_STYLE_MASK_MODE_ALPHA },
{ eCSSKeyword_luminance, NS_STYLE_MASK_MODE_LUMINANCE },
- // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1224424
- // It's ambigious at mask shorthand parsing while we have both mask-mode:auto
- // and mask-size:auto.
- { eCSSKeyword_auto, NS_STYLE_MASK_MODE_AUTO },
+ { eCSSKeyword_match_source, NS_STYLE_MASK_MODE_MATCH_SOURCE },
{ eCSSKeyword_UNKNOWN, -1 }
};
const KTableEntry nsCSSProps::kImageLayerCompositeKTable[] = {
{ eCSSKeyword_add, NS_STYLE_MASK_COMPOSITE_ADD },
{ eCSSKeyword_substract, NS_STYLE_MASK_COMPOSITE_SUBSTRACT },
{ eCSSKeyword_intersect, NS_STYLE_MASK_COMPOSITE_INTERSECT },
{ eCSSKeyword_exclude, NS_STYLE_MASK_COMPOSITE_EXCLUDE },
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -6013,17 +6013,17 @@ nsComputedDOMStyle::DoGetMask()
// Mask is now a shorthand, but it used to be a longhand, so that we
// need to support computed style for the cases where it used to be
// a longhand.
if (svg->mMask.mImageCount > 1 ||
firstLayer.mClip != NS_STYLE_IMAGELAYER_CLIP_BORDER ||
firstLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_PADDING ||
firstLayer.mComposite != NS_STYLE_MASK_COMPOSITE_ADD ||
- firstLayer.mMaskMode != NS_STYLE_MASK_MODE_AUTO ||
+ firstLayer.mMaskMode != NS_STYLE_MASK_MODE_MATCH_SOURCE ||
!firstLayer.mPosition.IsInitialValue() ||
!firstLayer.mRepeat.IsInitialValue() ||
!firstLayer.mSize.IsInitialValue() ||
!(firstLayer.mImage.GetType() == eStyleImageType_Null ||
firstLayer.mImage.GetType() == eStyleImageType_Image)){
return nullptr;
}
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -12,16 +12,17 @@
#include "nsStyleConsts.h"
#include "nsPresContext.h"
#include "nsCSSValue.h"
#ifdef XP_WIN
#include "mozilla/LookAndFeel.h"
#endif
#include "nsCSSRuleProcessor.h"
#include "nsDeviceContext.h"
+#include "nsIBaseWindow.h"
#include "nsIDocument.h"
#include "nsContentUtils.h"
#include "mozilla/StyleSheetHandle.h"
#include "mozilla/StyleSheetHandleInlines.h"
using namespace mozilla;
static const nsCSSProps::KTableEntry kOrientationKeywords[] = {
@@ -31,16 +32,24 @@ static const nsCSSProps::KTableEntry kOr
};
static const nsCSSProps::KTableEntry kScanKeywords[] = {
{ eCSSKeyword_progressive, NS_STYLE_SCAN_PROGRESSIVE },
{ eCSSKeyword_interlace, NS_STYLE_SCAN_INTERLACE },
{ eCSSKeyword_UNKNOWN, -1 }
};
+static const nsCSSProps::KTableEntry kDisplayModeKeywords[] = {
+ { eCSSKeyword_browser, NS_STYLE_DISPLAY_MODE_BROWSER },
+ { eCSSKeyword_minimal_ui, NS_STYLE_DISPLAY_MODE_MINIMAL_UI },
+ { eCSSKeyword_standalone, NS_STYLE_DISPLAY_MODE_STANDALONE },
+ { eCSSKeyword_fullscreen, NS_STYLE_DISPLAY_MODE_FULLSCREEN },
+ { eCSSKeyword_UNKNOWN, -1 }
+};
+
#ifdef XP_WIN
struct WindowsThemeName {
LookAndFeel::WindowsTheme id;
const wchar_t* name;
};
// Windows theme identities used in the -moz-windows-theme media query.
const WindowsThemeName themeStrings[] = {
@@ -300,16 +309,44 @@ GetScan(nsPresContext* aPresContext, con
{
// Since Gecko doesn't support the 'tv' media type, the 'scan'
// feature is never present.
aResult.Reset();
return NS_OK;
}
static nsresult
+GetDisplayMode(nsPresContext* aPresContext, const nsMediaFeature*,
+ nsCSSValue& aResult)
+{
+ nsCOMPtr<nsISupports> container = aPresContext->GetRootPresContext()->
+ Document()->GetContainer();
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
+ if (!baseWindow) {
+ aResult.SetIntValue(NS_STYLE_DISPLAY_MODE_BROWSER, eCSSUnit_Enumerated);
+ return NS_OK;
+ }
+ nsCOMPtr<nsIWidget> mainWidget;
+ baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+ int32_t displayMode;
+ nsSizeMode mode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
+ switch (mode) {
+ case nsSizeMode_Fullscreen:
+ displayMode = NS_STYLE_DISPLAY_MODE_FULLSCREEN;
+ break;
+ default:
+ displayMode = NS_STYLE_DISPLAY_MODE_BROWSER;
+ break;
+ }
+
+ aResult.SetIntValue(displayMode, eCSSUnit_Enumerated);
+ return NS_OK;
+}
+
+static nsresult
GetGrid(nsPresContext* aPresContext, const nsMediaFeature*,
nsCSSValue& aResult)
{
// Gecko doesn't support grid devices (e.g., ttys), so the 'grid'
// feature is always 0.
aResult.SetIntValue(0, eCSSUnit_Integer);
return NS_OK;
}
@@ -528,16 +565,24 @@ nsMediaFeatures::features[] = {
{
&nsGkAtoms::grid,
nsMediaFeature::eMinMaxNotAllowed,
nsMediaFeature::eBoolInteger,
nsMediaFeature::eNoRequirements,
{ nullptr },
GetGrid
},
+ {
+ &nsGkAtoms::displayMode,
+ nsMediaFeature::eMinMaxNotAllowed,
+ nsMediaFeature::eEnumerated,
+ nsMediaFeature::eNoRequirements,
+ { kDisplayModeKeywords },
+ GetDisplayMode
+ },
// Webkit extensions that we support for de-facto web compatibility
// -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):
{
&nsGkAtoms::devicePixelRatio,
nsMediaFeature::eMinMaxAllowed,
nsMediaFeature::eFloat,
nsMediaFeature::eHasWebkitPrefix |
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -9832,17 +9832,17 @@ nsRuleNode::ComputeSVGResetData(void* aS
svgReset->mMask.mSizeCount, maxItemCount, rebuild,
conditions);
// mask-mode: enum, inherit, initial [list]
SetImageLayerList(aContext, *aRuleData->ValueForMaskMode(),
svgReset->mMask.mLayers,
parentSVGReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mMaskMode,
- uint8_t(NS_STYLE_MASK_MODE_AUTO),
+ uint8_t(NS_STYLE_MASK_MODE_MATCH_SOURCE),
parentSVGReset->mMask.mMaskModeCount,
svgReset->mMask.mMaskModeCount, maxItemCount, rebuild, conditions);
// mask-composite: enum, inherit, initial [list]
SetImageLayerList(aContext, *aRuleData->ValueForMaskComposite(),
svgReset->mMask.mLayers,
parentSVGReset->mMask.mLayers,
&nsStyleImageLayers::Layer::mComposite,
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -301,17 +301,17 @@ enum class FillMode : uint32_t;
// See nsStyleImageLayers
#define NS_STYLE_IMAGELAYER_SIZE_CONTAIN 0
#define NS_STYLE_IMAGELAYER_SIZE_COVER 1
// Mask mode
#define NS_STYLE_MASK_MODE_ALPHA 0
#define NS_STYLE_MASK_MODE_LUMINANCE 1
-#define NS_STYLE_MASK_MODE_AUTO 2
+#define NS_STYLE_MASK_MODE_MATCH_SOURCE 2
// See nsStyleBackground
#define NS_STYLE_BG_INLINE_POLICY_EACH_BOX 0
#define NS_STYLE_BG_INLINE_POLICY_CONTINUOUS 1
#define NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX 2
// See nsStyleTable
#define NS_STYLE_BORDER_COLLAPSE 0
@@ -1200,11 +1200,17 @@ enum class FillMode : uint32_t;
// orientation
#define NS_STYLE_ORIENTATION_PORTRAIT 0
#define NS_STYLE_ORIENTATION_LANDSCAPE 1
// scan
#define NS_STYLE_SCAN_PROGRESSIVE 0
#define NS_STYLE_SCAN_INTERLACE 1
+// display-mode
+#define NS_STYLE_DISPLAY_MODE_BROWSER 0
+#define NS_STYLE_DISPLAY_MODE_MINIMAL_UI 1
+#define NS_STYLE_DISPLAY_MODE_STANDALONE 2
+#define NS_STYLE_DISPLAY_MODE_FULLSCREEN 3
+
} // namespace mozilla
#endif /* nsStyleConsts_h___ */
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2518,17 +2518,17 @@ nsStyleImageLayers::Repeat::SetInitialVa
}
nsStyleImageLayers::Layer::Layer()
: mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER),
mOrigin(NS_STYLE_IMAGELAYER_ORIGIN_PADDING),
mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL),
mBlendMode(NS_STYLE_BLEND_NORMAL),
mComposite(NS_STYLE_MASK_COMPOSITE_ADD),
- mMaskMode(NS_STYLE_MASK_MODE_AUTO)
+ mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE)
{
mPosition.SetInitialPercentValues(0.0f); // Initial value is "0% 0%"
mImage.SetNull();
mRepeat.SetInitialValues();
mSize.SetInitialValues();
}
nsStyleImageLayers::Layer::~Layer()
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -577,17 +577,17 @@ struct nsStyleImageLayers {
// For a background layer, it should always
// be the initial value, which is
// NS_STYLE_COMPOSITE_MODE_ADD.
uint8_t mMaskMode; // [reset] See nsStyleConsts.h
// mask-only property
// This property is used for mask layer only.
// For a background layer, it should always
// be the initial value, which is
- // NS_STYLE_MASK_MODE_AUTO.
+ // NS_STYLE_MASK_MODE_MATCH_SOURCE.
Repeat mRepeat; // [reset] See nsStyleConsts.h
// Initializes only mImage
Layer();
~Layer();
// Register/unregister images with the document. We do this only
// after the dust has settled in ComputeBackgroundData.
--- a/layout/style/test/chrome/chrome.ini
+++ b/layout/style/test/chrome/chrome.ini
@@ -11,11 +11,12 @@ support-files =
[test_addSheet.html]
[test_additional_sheets.html]
[test_author_specified_style.html]
[test_bug418986-2.xul]
[test_bug1157097.html]
[test_bug1160724.xul]
[test_bug535806.xul]
+[test_display_mode.html]
[test_hover.html]
skip-if = buildapp == 'mulet'
[test_moz_document_rules.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/chrome/test_display_mode.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1104916
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Display Mode</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+/** Test for Display Mode **/
+SimpleTest.waitForExplicitFinish();
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function waitOneEvent(element, name) {
+ return new Promise(function(resolve, reject) {
+ element.addEventListener(name, function listener() {
+ element.removeEventListener(name, listener);
+ resolve();
+ });
+ });
+}
+
+add_task(function* () {
+ yield waitOneEvent(window, "load");
+
+ var iframe = document.getElementById("subdoc");
+ var subdoc = iframe.contentDocument;
+ var style = subdoc.getElementById("style");
+ var bodyComputedStyled = subdoc.defaultView.getComputedStyle(subdoc.body, "");
+ var win = Services.wm.getMostRecentWindow("navigator:browser");
+
+ function queryApplies(q) {
+ style.setAttribute("media", q);
+ return bodyComputedStyled.getPropertyValue("text-decoration") == "underline";
+ }
+
+ function shouldApply(q) {
+ ok(queryApplies(q), q + " should apply");
+ }
+
+ function shouldNotApply(q) {
+ ok(!queryApplies(q), q + " should not apply");
+ }
+
+ shouldApply("all and (display-mode: browser)");
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: standalone)");
+ shouldNotApply("all and (display-mode: minimal-ui)");
+
+ // Test entering the OS's fullscreen mode.
+ var fullScreenEntered = waitOneEvent(win, "sizemodechange");
+ synthesizeKey("VK_F11", {});
+ yield fullScreenEntered;
+ shouldApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: browser)");
+ var fullScreenExited = waitOneEvent(win, "sizemodechange");
+ synthesizeKey("VK_F11", {});
+ yield fullScreenExited;
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldApply("all and (display-mode: browser)");
+
+ // Test entering fullscreen through document requestFullScreen.
+ fullScreenEntered = waitOneEvent(document, "mozfullscreenchange");
+ document.body.mozRequestFullScreen();
+ yield fullScreenEntered
+ ok(document.mozFullScreenElement, "window entered fullscreen");
+ shouldApply("all and (display-mode: fullscreen)");
+ shouldNotApply("all and (display-mode: browser)");
+ fullScreenExited = waitOneEvent(document, "mozfullscreenchange");
+ document.mozCancelFullScreen();
+ yield fullScreenExited;
+ ok(!document.mozFullScreenElement, "window exited fullscreen");
+ shouldNotApply("all and (display-mode: fullscreen)");
+ shouldApply("all and (display-mode: browser)");
+});
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1104916">Mozilla Bug 1104916</a>
+<iframe id="subdoc" src="http://mochi.test:8888/tests/layout/style/test/media_queries_iframe.html"></iframe>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4112,19 +4112,19 @@ var gCSSProperties = {
invalid_values: []
},
"mask": {
domProp: "mask",
inherited: false,
type: CSS_TYPE_SHORTHAND_AND_LONGHAND,
/* FIXME: All mask-border-* should be added when we implement them. */
subproperties: ["mask-clip", "mask-image", "mask-mode", "mask-origin", "mask-position", "mask-repeat", "mask-size" , "mask-composite"],
- initial_values: [ "auto", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto",
+ initial_values: [ "match-source", "none", "repeat", "add", "0% 0%", "top left", "left top", "0% 0% / auto", "top left / auto", "left top / auto", "0% 0% / auto auto",
"top left none", "left top none", "none left top", "none top left", "none 0% 0%", "left top / auto none", "left top / auto auto none",
- "auto none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add auto", "none repeat add 0% 0% / auto auto auto" ],
+ "match-source none repeat add top left", "left top repeat none add", "none repeat add top left / auto", "left top / auto repeat none add match-source", "none repeat add 0% 0% / auto auto match-source" ],
other_values: [
"none alpha repeat add left top",
"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)",
"repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') alpha left top add",
"repeat-x",
"repeat-y",
"no-repeat",
"none repeat-y alpha add 0% 0%",
@@ -4178,16 +4178,17 @@ var gCSSProperties = {
"-moz-linear-gradient(10 10px -45deg, red, blue) repeat",
"-moz-linear-gradient(10px 10 -45deg, red, blue) repeat",
"linear-gradient(red -99, yellow, green, blue 120%)",
/* bug 258080: don't accept background-position separated */
"left url(404.png) top", "top url(404.png) left",
"alpha padding-box url(404.png) border-box",
"alpha padding-box url(404.png) padding-box",
"-moz-element(#a rubbish)",
+ "left top / match-source"
]
},
"mask-clip": {
domProp: "maskClip",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "border-box" ],
other_values: [ "content-box", "padding-box", "border-box, padding-box", "padding-box, padding-box, padding-box", "border-box, border-box" ],
@@ -4211,19 +4212,19 @@ var gCSSProperties = {
].concat(invalidGradientAndElementValues),
unbalanced_values: [
].concat(unbalancedGradientAndElementValues)
},
"mask-mode": {
domProp: "maskMode",
inherited: false,
type: CSS_TYPE_LONGHAND,
- initial_values: [ "auto" ],
- other_values: [ "alpha", "luminance", "auto, auto", "auto, alpha", "alpha, luminance, auto"],
- invalid_values: [ "auto auto", "alpha auto" ]
+ initial_values: [ "match-source" ],
+ other_values: [ "alpha", "luminance", "match-source, match-source", "match-source, alpha", "alpha, luminance, match-source"],
+ invalid_values: [ "match-source match-source", "alpha match-source" ]
},
"mask-composite": {
domProp: "maskComposite",
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: [ "add" ],
other_values: [ "substract", "intersect", "exclude", "add, add", "substract, intersect", "substract, substract, add"],
invalid_values: [ "add substract", "intersect exclude" ]
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -248,16 +248,24 @@ function run() {
expression_should_not_be_parseable("min-" + feature + ": -1px");
expression_should_not_be_parseable("max-" + feature + ": -1px");
expression_should_not_be_parseable(feature + ": -0.00001mm");
expression_should_not_be_parseable(feature + ": -100000em");
expression_should_not_be_parseable("min-" + feature);
expression_should_not_be_parseable("max-" + feature);
}
+ var mediatypes = ["browser", "minimal-ui", "standalone", "fullscreen"];
+
+ mediatypes.forEach(function(type) {
+ expression_should_be_parseable("display-mode: " + type);
+ });
+
+ expression_should_not_be_parseable("display-mode: invalid")
+
var content_div = document.getElementById("content");
content_div.style.font = "initial";
var em_size =
getComputedStyle(content_div, "").fontSize.match(/^(\d+)px$/)[1];
// in this test, assume the common underlying implementation is correct
var width_val = 117; // pick two not-too-round numbers
var height_val = 76;
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -66,56 +66,30 @@ function doConfirm(msg)
{
let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
getService(Components.interfaces.nsIPromptService);
return prompts.confirm(window, null, msg);
}
function RefreshDeviceList()
{
- var modules = secmoddb.listModules();
- var done = false;
+ let modules = secmoddb.listModules();
+ while (modules.hasMoreElements()) {
+ let module = modules.getNext().QueryInterface(nsIPKCS11Module);
+ let slotnames = [];
+ let slots = module.listSlots();
+ while (slots.hasMoreElements()) {
+ let slot = slots.getNext().QueryInterface(nsIPKCS11Slot);
+ // Token names are preferred because NSS prefers lookup by token name.
+ slotnames.push(slot.tokenName ? slot.tokenName : slot.name);
+ }
+ AddModule(module.name, slotnames);
+ }
- try {
- modules.isDone();
- } catch (e) { done = true; }
- while (!done) {
- var module = modules.currentItem().QueryInterface(nsIPKCS11Module);
- if (module) {
- var slotnames = [];
- var slots = module.listSlots();
- var slots_done = false;
- try {
- slots.isDone();
- } catch (e) { slots_done = true; }
- while (!slots_done) {
- var slot = null;
- try {
- slot = slots.currentItem().QueryInterface(nsIPKCS11Slot);
- } catch (e) { slot = null; }
- // in the ongoing discussion of whether slot names or token names
- // are to be shown, I've gone with token names because NSS will
- // prefer lookup by token name. However, the token may not be
- // present, so maybe slot names should be listed, while token names
- // are "remembered" for lookup?
- if (slot != null) {
- slotnames[slotnames.length] = slot.tokenName ? slot.tokenName
- : slot.name;
- }
- try {
- slots.next();
- } catch (e) { slots_done = true; }
- }
- AddModule(module.name, slotnames);
- }
- try {
- modules.next();
- } catch (e) { done = true; }
- }
- /* Set the text on the fips button */
+ // Set the text on the FIPS button.
SetFIPSButton();
}
function SetFIPSButton()
{
var fipsButton = document.getElementById("fipsbutton");
var label;
if (secmoddb.isFIPSEnabled) {
--- a/security/manager/pki/resources/content/password.js
+++ b/security/manager/pki/resources/content/password.js
@@ -34,38 +34,34 @@ function onLoad()
// as window name must be a subset of ascii, and the code was
// previously trying to assign unicode to the window's name.
// I checked all the places where we get a password prompt and
// all of them pass an argument as part of this patch.
tokenName = "";
}
if (tokenName == "") {
- var sectokdb = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
- var tokenList = sectokdb.listTokens();
- var enumElement;
- let i = 0;
- var menu = document.getElementById("tokenMenu");
- try {
- for (; !tokenList.isDone(); tokenList.next()) {
- enumElement = tokenList.currentItem();
- var token = enumElement.QueryInterface(nsIPK11Token);
- if(token.needsLogin() || !(token.needsUserInit)) {
- var menuItemNode = document.createElement("menuitem");
- menuItemNode.setAttribute("value", token.tokenName);
- menuItemNode.setAttribute("label", token.tokenName);
- menu.firstChild.appendChild(menuItemNode);
- if (i == 0) {
- menu.selectedItem = menuItemNode;
- tokenName = token.tokenName;
- }
- i++;
- }
+ let tokenDB = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+ let tokenList = tokenDB.listTokens();
+ let i = 0;
+ let menu = document.getElementById("tokenMenu");
+ while (tokenList.hasMoreElements()) {
+ let token = tokenList.getNext().QueryInterface(nsIPK11Token);
+ if (token.needsLogin() || !(token.needsUserInit)) {
+ let menuItemNode = document.createElement("menuitem");
+ menuItemNode.setAttribute("value", token.tokenName);
+ menuItemNode.setAttribute("label", token.tokenName);
+ menu.firstChild.appendChild(menuItemNode);
+ if (i == 0) {
+ menu.selectedItem = menuItemNode;
+ tokenName = token.tokenName;
}
- } catch (exception) {}
+ i++;
+ }
+ }
} else {
var sel = document.getElementById("tokenMenu");
sel.setAttribute("hidden", "true");
var tag = document.getElementById("tokenName");
tag.setAttribute("value", tokenName);
}
process();
--- a/security/manager/ssl/nsIPK11TokenDB.idl
+++ b/security/manager/ssl/nsIPK11TokenDB.idl
@@ -2,17 +2,17 @@
*
* 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 "nsISupports.idl"
interface nsIPK11Token;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
/**
* The PK11 Token Database provides access to the PK11 modules
* that are installed, and the tokens that are available.
* Interfaces: nsIPK11TokenDB
* Threading: ??
*/
%{C++
@@ -33,12 +33,10 @@ interface nsIPK11TokenDB : nsISupports
/*
* Find a token by name
*/
nsIPK11Token findTokenByName(in wstring tokenName);
/*
* List all tokens
*/
- nsIEnumerator listTokens();
-
+ nsISimpleEnumerator listTokens();
};
-
--- a/security/manager/ssl/nsIPKCS11Module.idl
+++ b/security/manager/ssl/nsIPKCS11Module.idl
@@ -2,23 +2,20 @@
*
* 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 "nsISupports.idl"
interface nsIPKCS11Slot;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
[scriptable, uuid(8a44bdf9-d1a5-4734-bd5a-34ed7fe564c2)]
interface nsIPKCS11Module : nsISupports
{
-
readonly attribute wstring name;
readonly attribute wstring libName;
nsIPKCS11Slot findSlotByName(in wstring name);
- nsIEnumerator listSlots();
-
+ nsISimpleEnumerator listSlots();
};
-
--- a/security/manager/ssl/nsIPKCS11ModuleDB.idl
+++ b/security/manager/ssl/nsIPKCS11ModuleDB.idl
@@ -3,34 +3,33 @@
* 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 "nsISupports.idl"
interface nsIPKCS11Module;
interface nsIPKCS11Slot;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
%{C++
#define NS_PKCS11MODULEDB_CONTRACTID "@mozilla.org/security/pkcs11moduledb;1"
%}
[scriptable, uuid(ff9fbcd7-9517-4334-b97a-ceed78909974)]
interface nsIPKCS11ModuleDB : nsISupports
{
nsIPKCS11Module getInternal();
nsIPKCS11Module getInternalFIPS();
nsIPKCS11Module findModuleByName(in wstring name);
nsIPKCS11Slot findSlotByName(in wstring name);
- nsIEnumerator listModules();
+ nsISimpleEnumerator listModules();
readonly attribute boolean canToggleFIPS;
void toggleFIPSMode();
readonly attribute boolean isFIPSEnabled;
};
-
--- a/security/manager/ssl/nsPK11TokenDB.cpp
+++ b/security/manager/ssl/nsPK11TokenDB.cpp
@@ -1,23 +1,23 @@
/* -*- Mode: C++; tab-width: 2; 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 "nsPK11TokenDB.h"
+
+#include "nsIMutableArray.h"
#include "nsISupports.h"
-#include "nsISupportsArray.h"
-#include "nsIPK11TokenDB.h"
+#include "nsNSSComponent.h"
+#include "nsReadableUtils.h"
+#include "nsServiceManagerUtils.h"
#include "prerror.h"
+#include "ScopedNSSTypes.h"
#include "secerr.h"
-#include "nsReadableUtils.h"
-#include "nsNSSComponent.h"
-#include "nsServiceManagerUtils.h"
-
-#include "nsPK11TokenDB.h"
extern PRLogModuleInfo* gPIPNSSLog;
NS_IMPL_ISUPPORTS(nsPK11Token, nsIPK11Token)
nsPK11Token::nsPK11Token(PK11SlotInfo *slot)
{
nsNSSShutDownPreventionLock locker;
@@ -442,42 +442,36 @@ FindTokenByName(const char16_t* tokenNam
token = new nsPK11Token(slot);
token.forget(_retval);
done:
if (slot) PK11_FreeSlot(slot);
return rv;
}
-NS_IMETHODIMP nsPK11TokenDB::ListTokens(nsIEnumerator* *_retval)
+NS_IMETHODIMP
+nsPK11TokenDB::ListTokens(nsISimpleEnumerator** _retval)
{
nsNSSShutDownPreventionLock locker;
- nsCOMPtr<nsISupportsArray> array;
- PK11SlotList *list = 0;
- PK11SlotListElement *le;
+ nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!array) {
+ return NS_ERROR_FAILURE;
+ }
*_retval = nullptr;
- nsresult rv = NS_NewISupportsArray(getter_AddRefs(array));
- if (NS_FAILED(rv)) { goto done; }
+
+ UniquePK11SlotList list(
+ PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0));
+ if (!list) {
+ return NS_ERROR_FAILURE;
+ }
- /* List all tokens, creating PK11Token objects and putting them
- * into the array.
- */
- list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0);
- if (!list) { rv = NS_ERROR_FAILURE; goto done; }
-
- for (le = PK11_GetFirstSafe(list); le; le = PK11_GetNextSafe(list, le, false)) {
+ for (PK11SlotListElement* le = PK11_GetFirstSafe(list.get()); le;
+ le = PK11_GetNextSafe(list.get(), le, false)) {
nsCOMPtr<nsIPK11Token> token = new nsPK11Token(le->slot);
- rv = array->AppendElement(token);
+ nsresult rv = array->AppendElement(token, false);
if (NS_FAILED(rv)) {
- PK11_FreeSlotListElement(list, le);
- rv = NS_ERROR_OUT_OF_MEMORY;
- goto done;
+ return rv;
}
}
- rv = array->Enumerate(_retval);
-
-done:
- if (list) PK11_FreeSlotList(list);
- return rv;
+ return array->Enumerate(_retval);
}
-
--- a/security/manager/ssl/nsPKCS11Slot.cpp
+++ b/security/manager/ssl/nsPKCS11Slot.cpp
@@ -2,17 +2,17 @@
* 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 "nsPKCS11Slot.h"
#include "mozilla/Logging.h"
#include "mozilla/Telemetry.h"
#include "nsCOMPtr.h"
-#include "nsISupportsArray.h"
+#include "nsIMutableArray.h"
#include "nsPK11TokenDB.h"
#include "secmod.h"
using mozilla::LogLevel;
extern PRLogModuleInfo* gPIPNSSLog;
NS_IMPL_ISUPPORTS(nsPKCS11Slot, nsIPKCS11Slot)
@@ -313,44 +313,45 @@ nsPKCS11Module::FindSlotByName(const cha
}
free(asciiname);
nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotinfo);
PK11_FreeSlot(slotinfo);
slot.forget(_retval);
return NS_OK;
}
-NS_IMETHODIMP
-nsPKCS11Module::ListSlots(nsIEnumerator **_retval)
+NS_IMETHODIMP
+nsPKCS11Module::ListSlots(nsISimpleEnumerator** _retval)
{
nsNSSShutDownPreventionLock locker;
- if (isAlreadyShutDown())
+ if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
+ }
- nsresult rv = NS_OK;
- int i;
- /* get isupports array */
- nsCOMPtr<nsISupportsArray> array;
- rv = NS_NewISupportsArray(getter_AddRefs(array));
- if (NS_FAILED(rv)) return rv;
+ nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!array) {
+ return NS_ERROR_FAILURE;
+ }
+
/* applications which allow new slot creation (which Firefox now does
* since it uses the WaitForSlotEvent call) need to hold the
* ModuleList Read lock to prevent the slot array from changing out
* from under it. */
- SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
- SECMOD_GetReadLock(lock);
- for (i=0; i<mModule->slotCount; i++) {
+ AutoSECMODListReadLock lock;
+ for (int i = 0; i < mModule->slotCount; i++) {
if (mModule->slots[i]) {
nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(mModule->slots[i]);
- array->AppendElement(slot);
+ nsresult rv = array->AppendElement(slot, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
}
}
- SECMOD_ReleaseReadLock(lock);
- rv = array->Enumerate(_retval);
- return rv;
+
+ return array->Enumerate(_retval);
}
NS_IMPL_ISUPPORTS(nsPKCS11ModuleDB, nsIPKCS11ModuleDB, nsICryptoFIPSInfo)
nsPKCS11ModuleDB::nsPKCS11ModuleDB()
{
}
@@ -412,45 +413,47 @@ nsPKCS11ModuleDB::FindSlotByName(const c
if (!slotinfo)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotinfo);
PK11_FreeSlot(slotinfo);
slot.forget(_retval);
return NS_OK;
}
-NS_IMETHODIMP
-nsPKCS11ModuleDB::ListModules(nsIEnumerator **_retval)
+NS_IMETHODIMP
+nsPKCS11ModuleDB::ListModules(nsISimpleEnumerator** _retval)
{
nsNSSShutDownPreventionLock locker;
- nsresult rv = NS_OK;
- /* get isupports array */
- nsCOMPtr<nsISupportsArray> array;
- rv = NS_NewISupportsArray(getter_AddRefs(array));
- if (NS_FAILED(rv)) return rv;
- /* get the default list of modules */
- SECMODModuleList *list = SECMOD_GetDefaultModuleList();
+ nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!array) {
+ return NS_ERROR_FAILURE;
+ }
+
/* lock down the list for reading */
- SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
- SECMOD_GetReadLock(lock);
- while (list) {
+ AutoSECMODListReadLock lock;
+ for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
+ list = list->next) {
nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
- array->AppendElement(module);
- list = list->next;
+ nsresult rv = array->AppendElement(module, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
}
+
/* Get the modules in the database that didn't load */
- list = SECMOD_GetDeadModuleList();
- while (list) {
+ for (SECMODModuleList* list = SECMOD_GetDeadModuleList(); list;
+ list = list->next) {
nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
- array->AppendElement(module);
- list = list->next;
+ nsresult rv = array->AppendElement(module, false);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
}
- SECMOD_ReleaseReadLock(lock);
- rv = array->Enumerate(_retval);
- return rv;
+
+ return array->Enumerate(_retval);
}
NS_IMETHODIMP nsPKCS11ModuleDB::GetCanToggleFIPS(bool *aCanToggleFIPS)
{
nsNSSShutDownPreventionLock locker;
*aCanToggleFIPS = SECMOD_CanDeleteInternalModule();
return NS_OK;
}
--- a/security/manager/ssl/nsPKCS11Slot.h
+++ b/security/manager/ssl/nsPKCS11Slot.h
@@ -1,25 +1,25 @@
/* -*- Mode: C++; tab-width: 2; 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 __NS_PKCS11SLOT_H__
-#define __NS_PKCS11SLOT_H__
+#ifndef nsPKCS11Slot_h
+#define nsPKCS11Slot_h
-#include "nsISupports.h"
-#include "nsIPKCS11Slot.h"
+#include "nsICryptoFIPSInfo.h"
#include "nsIPKCS11Module.h"
#include "nsIPKCS11ModuleDB.h"
-#include "nsICryptoFIPSInfo.h"
+#include "nsIPKCS11Slot.h"
+#include "nsISupports.h"
+#include "nsNSSShutDown.h"
#include "nsString.h"
#include "pk11func.h"
-#include "nsNSSShutDown.h"
class nsPKCS11Slot : public nsIPKCS11Slot,
public nsNSSShutDownObject
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPKCS11SLOT
@@ -72,9 +72,28 @@ protected:
virtual ~nsPKCS11ModuleDB();
/* additional members */
};
#define NS_PKCS11MODULEDB_CID \
{ 0xff9fbcd7, 0x9517, 0x4334, \
{ 0xb9, 0x7a, 0xce, 0xed, 0x78, 0x90, 0x99, 0x74 }}
-#endif
+class MOZ_RAII AutoSECMODListReadLock final
+{
+public:
+ AutoSECMODListReadLock()
+ : mLock(SECMOD_GetDefaultModuleListLock())
+ {
+ MOZ_ASSERT(mLock, "Should have the default SECMOD lock");
+ SECMOD_GetReadLock(mLock);
+ }
+
+ ~AutoSECMODListReadLock()
+ {
+ SECMOD_ReleaseReadLock(mLock);
+ }
+
+private:
+ SECMODListLock* mLock;
+};
+
+#endif // nsPKCS11Slot_h
--- a/security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
@@ -11,26 +11,48 @@
// has been succssfully observed, and then it unloads the test module.
// Ensure that the appropriate initialization has happened.
do_get_profile();
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
const gExpectedTokenLabel = "Test PKCS11 TokeƱ Label";
+const gTokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
+ .getService(Ci.nsIPK11TokenDB);
+
function SmartcardObserver(type) {
this.type = type;
do_test_pending();
}
SmartcardObserver.prototype = {
observe: function(subject, topic, data) {
equal(topic, this.type, "Observed and expected types should match");
equal(gExpectedTokenLabel, data,
"Expected and observed token labels should match");
+
+ // Test that the token list contains the test token only when the test
+ // module is loaded.
+ // Note: This test is located here out of convenience. In particular,
+ // observing the "smartcard-insert" event is the only time where it
+ // is reasonably certain for the test token to be present (see the top
+ // level comment for this file for why).
+ let tokenList = gTokenDB.listTokens();
+ let testTokenLabelFound = false;
+ while (tokenList.hasMoreElements()) {
+ let token = tokenList.getNext().QueryInterface(Ci.nsIPK11Token);
+ if (token.tokenLabel == gExpectedTokenLabel) {
+ testTokenLabelFound = true;
+ break;
+ }
+ }
+ equal(testTokenLabelFound, this.type == "smartcard-insert",
+ "Should find test token only when the test module is loaded");
+
Services.obs.removeObserver(this, this.type);
do_test_finished();
}
};
function run_test() {
Services.obs.addObserver(new SmartcardObserver("smartcard-insert"),
"smartcard-insert", false);
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_list.js
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods for listing PKCS #11 modules and slots via loading and
+// unloading a test PKCS #11 module.
+
+// Note: Tests for listing PKCS #11 tokens are located in
+// test_pkcs11_insert_remove.js out of convenience.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+const gModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
+ .getService(Ci.nsIPKCS11ModuleDB);
+
+function checkTestModuleNotPresent() {
+ let modules = gModuleDB.listModules();
+ ok(modules.hasMoreElements(),
+ "One or more modules should be present with test module not present");
+ while (modules.hasMoreElements()) {
+ let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+ notEqual(module.name, "PKCS11 Test Module",
+ "Non-test module name shouldn't equal 'PKCS11 Test Module'");
+ }
+}
+
+/**
+ * Checks that the test module exists in the module list.
+ *
+ * @returns {nsIPKCS11Module}
+ * The test module.
+ */
+function checkTestModuleExists() {
+ let modules = gModuleDB.listModules();
+ ok(modules.hasMoreElements(),
+ "One or more modules should be present with test module present");
+ let testModule = null;
+ while (modules.hasMoreElements()) {
+ let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+ if (module.name == "PKCS11 Test Module") {
+ testModule = module;
+ break;
+ }
+ }
+ notEqual(testModule, null, "Test module should have been found");
+
+ return testModule;
+}
+
+function run_test() {
+ let libraryName = ctypes.libraryName("pkcs11testmodule");
+ let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
+ libraryFile.append("pkcs11testmodule");
+ libraryFile.append(libraryName);
+ ok(libraryFile.exists(), "The pkcs11testmodule file should exist");
+
+ // Check that if we have never added the test module, that we don't find it
+ // in the module list.
+ checkTestModuleNotPresent();
+
+ // Check that adding the test module makes it appear in the module list.
+ let pkcs11 = Cc["@mozilla.org/security/pkcs11;1"].getService(Ci.nsIPKCS11);
+ do_register_cleanup(() => {
+ try {
+ pkcs11.deleteModule("PKCS11 Test Module");
+ } catch (e) {
+ // deleteModule() throws if the module we tell it to delete is missing,
+ // or if some other thing went wrong. Since we're just cleaning up,
+ // there's nothing to do even if the call fails. In addition, we delete
+ // the test module during a normal run of this test file, so we need to
+ // catch the exception that is raised to not have the test fail.
+ }
+ });
+ pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0);
+ let testModule = checkTestModuleExists();
+
+ // Check that listing the slots for the test module works.
+ let slots = testModule.listSlots();
+ let testModuleSlotCount = 0;
+ while (slots.hasMoreElements()) {
+ let slot = slots.getNext().QueryInterface(Ci.nsIPKCS11Slot);
+ equal(slot.name, "Test PKCS11 Slot",
+ "Test module slot should have correct name");
+ testModuleSlotCount++;
+ }
+ equal(testModuleSlotCount, 1, "Test module should only have one slot");
+
+ // Check that deleting the test module makes it disappear from the module list.
+ pkcs11.deleteModule("PKCS11 Test Module");
+ checkTestModuleNotPresent();
+}
--- a/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
+++ b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
@@ -3,14 +3,17 @@ head = head_psm.js
tail =
tags = psm
skip-if = toolkit == 'android' || toolkit == 'gonk'
support-files =
[test_pkcs11_insert_remove.js]
# Bug 1049969: this test doesn't work on windows
skip-if = os == "win"
+[test_pkcs11_list.js]
+# Bug 1049969: this test doesn't work on windows
+skip-if = os == "win"
[test_pkcs11_no_events_after_removal.js]
# Bug 1049969: this test doesn't work on windows
skip-if = os == "win"
[test_pkcs11_safe_mode.js]
# Bug 1049969: this test doesn't work on windows
skip-if = os == "win"
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -29,16 +29,22 @@
}
],
"service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html": [
{
"path": "service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html",
"url": "/_mozilla/service-workers/service-worker/ServiceWorkerGlobalScope/update.https.html"
}
],
+ "service-workers/service-worker/activate-event-after-install-state-change.https.html": [
+ {
+ "path": "service-workers/service-worker/activate-event-after-install-state-change.https.html",
+ "url": "/_mozilla/service-workers/service-worker/activate-event-after-install-state-change.https.html"
+ }
+ ],
"service-workers/service-worker/activation-after-registration.https.html": [
{
"path": "service-workers/service-worker/activation-after-registration.https.html",
"url": "/_mozilla/service-workers/service-worker/activation-after-registration.https.html"
}
],
"service-workers/service-worker/active.https.html": [
{
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/activate-event-after-install-state-change.https.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Service Worker: registration events</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+promise_test(function(t) {
+ var script = 'resources/empty-worker.js';
+ var scope = 'resources/blank.html';
+ var registration;
+
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(function(registration) {
+ var sw = registration.installing;
+
+ return new Promise(t.step_func(function(resolve) {
+ sw.onstatechange = t.step_func(function() {
+ if (sw.state === 'installed') {
+ assert_equals(registration.active, null,
+ 'installed event should be fired before activating service worker');
+ resolve();
+ }
+ });
+ }));
+ })
+ .then(function() {
+ return service_worker_unregister_and_done(t, scope);
+ })
+ .catch(unreached_rejection(t));
+ }, 'installed event should be fired before activating service worker');
+
+</script>
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/performance-timeline-worker.js
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/performance-timeline-worker.js
@@ -2,44 +2,35 @@ importScripts('/resources/testharness.js
promise_test(function(test) {
var durationMsec = 100;
// There are limits to our accuracy here. Timers may fire up to a
// millisecond early due to platform-dependent rounding. In addition
// the performance API introduces some rounding as well to prevent
// timing attacks.
var accuracy = 1.5;
- var dateStart;
return new Promise(function(resolve) {
- dateStart = Date.now();
performance.mark('startMark');
setTimeout(resolve, durationMsec);
}).then(function() {
performance.mark('endMark');
- var dateEnd = Date.now();
performance.measure('measure', 'startMark', 'endMark');
var startMark = performance.getEntriesByName('startMark')[0];
var endMark = performance.getEntriesByName('endMark')[0];
var measure = performance.getEntriesByType('measure')[0];
assert_equals(measure.startTime, startMark.startTime);
assert_approx_equals(endMark.startTime - startMark.startTime,
measure.duration, 0.001);
assert_greater_than(measure.duration, durationMsec - accuracy);
assert_equals(performance.getEntriesByType('mark').length, 2);
assert_equals(performance.getEntriesByType('measure').length, 1);
performance.clearMarks('startMark');
performance.clearMeasures('measure');
assert_equals(performance.getEntriesByType('mark').length, 1);
assert_equals(performance.getEntriesByType('measure').length, 0);
-
- // Performance API's fuzzy resolution means its measured duration
- // can round to a slightly different value compared to Date.now()
- // measurements.
- var dateDuration = dateEnd - dateStart;
- assert_approx_equals(Math.round(measure.duration), dateDuration, 1);
});
}, 'User Timing');
promise_test(function(test) {
return fetch('dummy.txt')
.then(function(resp) {
return resp.text();
})
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
@@ -1,33 +1,24 @@
self.state = 'starting';
self.addEventListener('install', function() {
self.state = 'installing';
});
-self.addEventListener('activate', function() {
- self.state = 'activating';
- });
-
self.addEventListener('message', function(event) {
var port = event.data.port;
if (self.state !== 'installing') {
port.postMessage('FAIL: Worker should be waiting in installed state');
return;
}
self.skipWaiting()
.then(function(result) {
if (result !== undefined) {
port.postMessage('FAIL: Promise should be resolved with undefined');
return;
}
- if (self.state !== 'activating') {
- port.postMessage(
- 'FAIL: Promise should be resolved after worker activated');
- return;
- }
port.postMessage('PASS');
})
.catch(function(e) {
port.postMessage('FAIL: unexpected exception: ' + e);
});
});
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-installed.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/skip-waiting-installed.https.html
@@ -5,52 +5,56 @@
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
promise_test(function(t) {
var scope = 'resources/blank.html';
var url1 = 'resources/empty.js';
var url2 = 'resources/skip-waiting-installed-worker.js';
- var frame, frame_sw, service_worker, onmessage, oncontrollerchanged;
+ var frame, frame_sw, service_worker, registration, onmessage, oncontrollerchanged;
var saw_message = new Promise(function(resolve) {
onmessage = function(e) {
var message = e.data;
assert_equals(
message, 'PASS',
- 'skipWaiting promise should be resolved after activated');
+ 'skipWaiting promise should be resolved with undefined');
+
+ assert_equals(registration.active.scriptURL, normalizeURL(url2),
+ "skipWaiting should make worker become active");
resolve();
};
});
var saw_controllerchanged = new Promise(function(resolve) {
oncontrollerchanged = function() {
assert_equals(
frame_sw.controller.scriptURL, normalizeURL(url2),
'Controller scriptURL should change to the second one');
resolve();
};
});
return service_worker_unregister_and_register(t, url1, scope)
- .then(function(registration) {
- return wait_for_state(t, registration.installing, 'activated');
+ .then(function(r) {
+ return wait_for_state(t, r.installing, 'activated');
})
.then(function() {
return with_iframe(scope);
})
.then(function(f) {
frame = f;
frame_sw = f.contentWindow.navigator.serviceWorker;
assert_equals(
frame_sw.controller.scriptURL, normalizeURL(url1),
'Document controller scriptURL should equal to the first one');
frame_sw.oncontrollerchange = t.step_func(oncontrollerchanged);
return navigator.serviceWorker.register(url2, {scope: scope});
})
- .then(function(registration) {
- service_worker = registration.installing;
+ .then(function(r) {
+ registration = r;
+ service_worker = r.installing;
return wait_for_state(t, service_worker, 'installed');
})
.then(function() {
var channel = new MessageChannel();
channel.port1.onmessage = t.step_func(onmessage);
service_worker.postMessage({port: channel.port2}, [channel.port2]);
return Promise.all([saw_message, saw_controllerchanged]);
})
--- a/testing/web-platform/tests/fetch/api/request/request-init-002.html
+++ b/testing/web-platform/tests/fetch/api/request/request-init-002.html
@@ -18,37 +18,47 @@
var headers = new Headers(headerDict);
var request = new Request("", { "headers" : headers })
for (var name in headerDict) {
assert_equals(request.headers.get(name), headerDict[name],
"request's headers has " + name + " : " + headerDict[name]);
}
}, "Initialize Request with headers values");
+ function makeRequestInit(body, method) {
+ return {"method": method, "body": body};
+ }
+
function checkRequestInit(body, bodyType, expectedTextBody) {
promise_test(function(test) {
- var request = new Request("", {"method": "POST", "body": body});
- assert_throws(new TypeError(),
- function() { new Request("", {"method": "GET", "body": body}); }
- );
+ var request = new Request("", makeRequestInit(body, "POST"));
+ if (body) {
+ assert_throws(new TypeError(),
+ function() { new Request("", makeRequestInit(body, "GET")); }
+ );
+ } else {
+ new Request("", makeRequestInit(body, "GET")); // should not throw
+ }
var reqHeaders = request.headers;
var mime = reqHeaders.get("Content-Type");
- assert_true(mime && mime.search(bodyType) > -1, "Content-Type header should be \"" + bodyType + "\", not \"" + mime + "\"");
+ assert_true(!body || (mime && mime.search(bodyType) > -1), "Content-Type header should be \"" + bodyType + "\", not \"" + mime + "\"");
return request.text().then(function(bodyAsText) {
//not equals: cannot guess formData exact value
assert_true( bodyAsText.search(expectedTextBody) > -1, "Retrieve and verify request body");
});
}, "Initialize Response's body with " + bodyType);
}
var blob = new Blob(["This is a blob"], {type: "application/octet-binary"});
var formaData = new FormData();
formaData.append("name", "value");
var usvString = "This is a USVString"
+ checkRequestInit(undefined, undefined, "");
+ checkRequestInit(null, null, "");
checkRequestInit(blob, "application/octet-binary", "This is a blob");
checkRequestInit(formaData, "multipart/form-data", "name=\"name\"\r\n\r\nvalue");
checkRequestInit(usvString, "text/plain;charset=UTF-8", "This is a USVString");
// Ensure test does not time out in case of missing URLSearchParams support.
if (window.URLSearchParams) {
var urlSearchParams = new URLSearchParams("name=value");
checkRequestInit(urlSearchParams, "application/x-www-form-urlencoded;charset=UTF-8", "name=value");
--- a/tools/mercurial/eslintvalidate.py
+++ b/tools/mercurial/eslintvalidate.py
@@ -2,17 +2,17 @@
# GNU General Public License version 2 or any later version.
import os
import sys
import re
import json
from subprocess import check_output, CalledProcessError
-lintable = re.compile(r'.+\.(?:js|jsm|jsx|xml)$')
+lintable = re.compile(r'.+\.(?:js|jsm|jsx|xml|html)$')
ignored = "File ignored because of your .eslintignore file. Use --no-ignore to override."
def is_lintable(filename):
return lintable.match(filename)
def display(ui, output):
results = json.loads(output)
for file in results:
@@ -30,16 +30,16 @@ def eslinthook(ui, repo, node=None, **op
deleted = repo.status(ctx.p1().node(), ctx.node()).deleted
files = [f for f in ctx.files() if f not in deleted and is_lintable(f)]
if len(files) == 0:
return
try:
- output = check_output(["eslint", "--format", "json"] + files)
+ output = check_output(["eslint", "--format", "json", "--plugin", "html"] + files)
display(ui, output)
except CalledProcessError as ex:
display(ui, ex.output)
ui.warn("ESLint found problems in your changes, please correct them.\n")
def reposetup(ui, repo):
ui.setconfig('hooks', 'commit.eslint', eslinthook)
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6154,16 +6154,18 @@ void nsWindow::OnWindowPosChanged(WINDOW
// handle it.
if (mSizeMode == nsSizeMode_Minimized && (wp->flags & SWP_NOACTIVATE))
return;
WINDOWPLACEMENT pl;
pl.length = sizeof(pl);
::GetWindowPlacement(mWnd, &pl);
+ nsSizeMode previousSizeMode = mSizeMode;
+
// Windows has just changed the size mode of this window. The call to
// SizeModeChanged will trigger a call into SetSizeMode where we will
// set the min/max window state again or for nsSizeMode_Normal, call
// SetWindow with a parameter of SW_RESTORE. There's no need however as
// this window's mode has already changed. Updating mSizeMode here
// insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
// to window docking. (bug 489258)
if (pl.showCmd == SW_SHOWMAXIMIZED)
@@ -6198,17 +6200,17 @@ void nsWindow::OnWindowPosChanged(WINDOW
("*** mSizeMode: nsSizeMode_Maximized\n"));
break;
default:
MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** mSizeMode: ??????\n"));
break;
}
#endif
- if (mWidgetListener)
+ if (mWidgetListener && mSizeMode != previousSizeMode)
mWidgetListener->SizeModeChanged(mSizeMode);
// If window was restored, window activation was bypassed during the
// SetSizeMode call originating from OnWindowPosChanging to avoid saving
// pre-restore attributes. Force activation now to get correct attributes.
if (mLastSizeMode != nsSizeMode_Normal && mSizeMode == nsSizeMode_Normal)
DispatchFocusToTopLevelWindow(true);
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -632,18 +632,16 @@
ERROR(NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED, FAILURE(48)),
ERROR(NS_ERROR_XPC_BAD_ID_STRING, FAILURE(49)),
ERROR(NS_ERROR_XPC_BAD_INITIALIZER_NAME, FAILURE(50)),
ERROR(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, FAILURE(51)),
ERROR(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, FAILURE(52)),
ERROR(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL, FAILURE(53)),
ERROR(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, FAILURE(54)),
/* any new errors here should have an associated entry added in xpc.msg */
-
- ERROR(NS_SUCCESS_I_DID_SOMETHING, SUCCESS(1)),
#undef MODULE
/* ======================================================================= */
/* 19: NS_ERROR_MODULE_PROFILE */
/* ======================================================================= */
#define MODULE NS_ERROR_MODULE_PROFILE
ERROR(NS_ERROR_LAUNCHED_CHILD_PROCESS, FAILURE(200)),
--- a/xpfe/appshell/nsWebShellWindow.cpp
+++ b/xpfe/appshell/nsWebShellWindow.cpp
@@ -360,16 +360,21 @@ nsWebShellWindow::SizeModeChanged(nsSize
ourWindow->SetFullScreen(false);
}
}
// And always fire a user-defined sizemodechange event on the window
ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("sizemodechange"));
}
+ nsIPresShell* presShell;
+ if ((presShell = GetPresShell())) {
+ presShell->GetPresContext()->SizeModeChanged(sizeMode);
+ }
+
// Note the current implementation of SetSizeMode just stores
// the new state; it doesn't actually resize. So here we store
// the state and pass the event on to the OS. The day is coming
// when we'll handle the event here, and the return result will
// then need to be different.
}
void