Merge mozilla-inbound to mozilla-central. a=merge

This commit is contained in:
Andreea Pavel 2018-02-17 23:44:10 +02:00
commit 2e506ce663
42 changed files with 551 additions and 197 deletions

View File

@ -59,6 +59,12 @@ ChromeUtils.defineModuleGetter(this, "BrowserUtils",
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
/**
* Safety timeout after which asynchronous events will be canceled if any of the
* registered blockers does not return.
*/
const BLOCKERS_TIMEOUT_MS = 10000;
const TRANSITION_PHASES = Object.freeze({
START: 1,
PREPARE: 2,
@ -81,6 +87,12 @@ this.AssociatedToNode = class {
* Node associated to this object.
*/
this.node = node;
/**
* This promise is resolved when the current set of blockers set by event
* handlers have all been processed.
*/
this._blockersPromise = Promise.resolve();
}
/**
@ -133,6 +145,63 @@ this.AssociatedToNode = class {
this.node.dispatchEvent(event);
return event.defaultPrevented;
}
/**
* Dispatches a custom event on this element and waits for any blocking
* promises registered using the "addBlocker" function on the details object.
* If this function is called again, the event is only dispatched after all
* the previously registered blockers have returned.
*
* The event can be canceled either by resolving any blocking promise to the
* boolean value "false" or by calling preventDefault on the event. Rejections
* and exceptions will be reported and will cancel the event.
*
* Blocking should be used sporadically because it slows down the interface.
* Also, non-reentrancy is not strictly guaranteed because a safety timeout of
* BLOCKERS_TIMEOUT_MS is implemented, after which the event will be canceled.
* This helps to prevent deadlocks if any of the event handlers does not
* resolve a blocker promise.
*
* @note Since there is no use case for dispatching different asynchronous
* events in parallel for the same element, this function will also wait
* for previous blockers when the event name is different.
*
* @param eventName
* Name of the custom event to dispatch.
*
* @resolves True if the event was canceled by a handler, false otherwise.
*/
async dispatchAsyncEvent(eventName) {
// Wait for all the previous blockers before dispatching the event.
let blockersPromise = this._blockersPromise.catch(() => {});
return this._blockersPromise = blockersPromise.then(async () => {
let blockers = new Set();
let cancel = this.dispatchCustomEvent(eventName, {
addBlocker(promise) {
// Any exception in the blocker will cancel the operation.
blockers.add(promise.catch(ex => {
Cu.reportError(ex);
return true;
}));
},
}, true);
if (blockers.size) {
let timeoutPromise = new Promise((resolve, reject) => {
this.window.setTimeout(reject, BLOCKERS_TIMEOUT_MS);
});
try {
let results = await Promise.race([Promise.all(blockers),
timeoutPromise]);
cancel = cancel || results.some(result => result === false);
} catch (ex) {
Cu.reportError(new Error(
`One of the blockers for ${eventName} timed out.`));
return true;
}
}
return cancel;
});
}
};
/**
@ -226,13 +295,6 @@ this.PanelMultiView = class extends this.AssociatedToNode {
let panelView = this.openViews[this.openViews.length - 1];
return (panelView && panelView.node) || this._mainView;
}
/**
* @return {Promise} showSubView() returns a promise, which is kept here for
* random access.
*/
get currentShowPromise() {
return this._currentShowPromise || Promise.resolve();
}
constructor(node) {
super(node);
@ -271,7 +333,6 @@ this.PanelMultiView = class extends this.AssociatedToNode {
// Set CSS-determined attributes now to prevent a layout flush when we do
// it when transitioning between panels.
this._dir = cs.direction;
this.showMainView();
// Proxy these public properties and methods, as used elsewhere by various
// parts of the browser, to this instance.
@ -281,7 +342,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
value: (...args) => this[method](...args)
});
});
["current", "currentShowPromise", "showingSubView"].forEach(property => {
["current", "showingSubView"].forEach(property => {
Object.defineProperty(this.node, property, {
enumerable: true,
get: () => this[property]
@ -405,7 +466,10 @@ this.PanelMultiView = class extends this.AssociatedToNode {
" its display turned off by the hidden attribute.");
}
}
// (The rest of the asynchronous preparation goes here.)
// Allow any of the ViewShowing handlers to prevent showing the main view.
if (!(await this.showMainView())) {
cancelCallback();
}
} catch (ex) {
cancelCallback();
throw ex;
@ -488,9 +552,9 @@ this.PanelMultiView = class extends this.AssociatedToNode {
this.showSubView(current, null, previous);
}
showMainView() {
async showMainView() {
if (!this.node || !this._mainViewId)
return Promise.resolve();
return false;
return this.showSubView(this._mainView);
}
@ -522,8 +586,8 @@ this.PanelMultiView = class extends this.AssociatedToNode {
this.showingSubView = nextPanelView.node.id != this._mainViewId;
}
showSubView(aViewId, aAnchor, aPreviousView) {
this._currentShowPromise = (async () => {
async showSubView(aViewId, aAnchor, aPreviousView) {
try {
// Support passing in the node directly.
let viewNode = typeof aViewId == "string" ? this.node.querySelector("#" + aViewId) : aViewId;
if (!viewNode) {
@ -575,26 +639,9 @@ this.PanelMultiView = class extends this.AssociatedToNode {
// Emit the ViewShowing event so that the widget definition has a chance
// to lazily populate the subview with things or perhaps even cancel this
// whole operation.
let detail = {
blockers: new Set(),
addBlocker(promise) {
this.blockers.add(promise);
}
};
let cancel = nextPanelView.dispatchCustomEvent("ViewShowing", detail, true);
if (detail.blockers.size) {
try {
let results = await Promise.all(detail.blockers);
cancel = cancel || results.some(val => val === false);
} catch (e) {
Cu.reportError(e);
cancel = true;
}
}
if (cancel) {
if (await nextPanelView.dispatchAsyncEvent("ViewShowing")) {
this._viewShowing = null;
return;
return false;
}
}
@ -607,8 +654,12 @@ this.PanelMultiView = class extends this.AssociatedToNode {
} else {
this.hideAllViewsExcept(nextPanelView);
}
})().catch(e => Cu.reportError(e));
return this._currentShowPromise;
return true;
} catch (ex) {
Cu.reportError(ex);
return false;
}
}
/**
@ -934,7 +985,9 @@ this.PanelMultiView = class extends this.AssociatedToNode {
this._viewShowing = null;
this._transitioning = false;
this.node.removeAttribute("panelopen");
this.showMainView();
// Raise the ViewHiding event for the current view.
this._cleanupTransitionPhase();
this.hideAllViewsExcept(null);
this.window.removeEventListener("keydown", this);
this._panel.removeEventListener("mousemove", this);
this.openViews.forEach(panelView => panelView.clearNavigation());

View File

@ -422,10 +422,6 @@ const PanelUI = {
await oldMultiView.showMainView();
}
let viewShown = false;
let listener = () => viewShown = true;
viewNode.addEventListener("ViewShown", listener, {once: true});
let multiView = document.createElement("panelmultiview");
multiView.setAttribute("id", "customizationui-widget-multiview");
multiView.setAttribute("viewCacheId", "appMenu-viewCache");
@ -435,6 +431,7 @@ const PanelUI = {
tempPanel.appendChild(multiView);
viewNode.classList.add("cui-widget-panelview");
let viewShown = false;
let panelRemover = () => {
viewNode.classList.remove("cui-widget-panelview");
if (viewShown) {
@ -449,18 +446,6 @@ const PanelUI = {
tempPanel.remove();
};
// Wait until all the tasks needed to show a view are done.
await multiView.currentShowPromise;
if (!viewShown) {
viewNode.removeEventListener("ViewShown", listener);
panelRemover();
return;
}
CustomizableUI.addPanelCloseListeners(tempPanel);
tempPanel.addEventListener("popuphidden", panelRemover);
if (aAnchor.parentNode.id == "PersonalToolbar") {
tempPanel.classList.add("bookmarks-toolbar");
}
@ -471,10 +456,21 @@ const PanelUI = {
anchor.setAttribute("consumeanchor", aAnchor.id);
}
PanelMultiView.openPopup(tempPanel, anchor, {
position: "bottomcenter topright",
triggerEvent: domEvent,
}).catch(Cu.reportError);
try {
viewShown = await PanelMultiView.openPopup(tempPanel, anchor, {
position: "bottomcenter topright",
triggerEvent: domEvent,
});
} catch (ex) {
Cu.reportError(ex);
}
if (viewShown) {
CustomizableUI.addPanelCloseListeners(tempPanel);
tempPanel.addEventListener("popuphidden", panelRemover);
} else {
panelRemover();
}
}
},

View File

@ -1,4 +1,6 @@
[DEFAULT]
prefs =
dom.animations-api.core.enabled=true
support-files =
head.js
head_pageAction.js
@ -210,3 +212,4 @@ tags = fullscreen
skip-if = os == 'mac' # Fails when windows are randomly opened in fullscreen mode
[browser_ext_windows_update.js]
tags = fullscreen
[browser_ext_contentscript_animate.js]

View File

@ -0,0 +1,95 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(async function test_animate() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"content_scripts": [
{
"matches": ["http://mochi.test/*"],
"js": ["content-script.js"],
},
],
},
files: {
"content-script.js": function() {
let elem = document.getElementsByTagName("body")[0];
elem.style.border = "2px solid red";
let anim = elem.animate({opacity: [1, 0]}, 2000);
let frames = anim.effect.getKeyframes();
browser.test.assertEq(frames.length, 2,
"frames for Element.animate should be non-zero");
browser.test.assertEq(frames[0].opacity, "1",
"first frame opacity for Element.animate should be specified value");
browser.test.assertEq(frames[0].computedOffset, 0,
"first frame offset for Element.animate should be 0");
browser.test.assertEq(frames[1].opacity, "0",
"last frame opacity for Element.animate should be specified value");
browser.test.assertEq(frames[1].computedOffset, 1,
"last frame offset for Element.animate should be 1");
browser.test.notifyPass("contentScriptAnimate");
},
},
});
await extension.startup();
await extension.awaitFinish("contentScriptAnimate");
await extension.unload();
await BrowserTestUtils.removeTab(tab);
});
add_task(async function test_KeyframeEffect() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"content_scripts": [
{
"matches": ["http://mochi.test/*"],
"js": ["content-script.js"],
},
],
},
files: {
"content-script.js": function() {
let elem = document.getElementsByTagName("body")[0];
elem.style.border = "2px solid red";
let effect = new KeyframeEffect(elem, [
{opacity: 1, offset: 0},
{opacity: 0, offset: 1},
], {duration: 1000, fill: "forwards"});
let frames = effect.getKeyframes();
browser.test.assertEq(frames.length, 2,
"frames for KeyframeEffect ctor should be non-zero");
browser.test.assertEq(frames[0].opacity, "1",
"first frame opacity for KeyframeEffect ctor should be specified value");
browser.test.assertEq(frames[0].computedOffset, 0,
"first frame offset for KeyframeEffect ctor should be 0");
browser.test.assertEq(frames[1].opacity, "0",
"last frame opacity for KeyframeEffect ctor should be specified value");
browser.test.assertEq(frames[1].computedOffset, 1,
"last frame offset for KeyframeEffect ctor should be 1");
let animation = new Animation(effect, document.timeline);
animation.play();
browser.test.notifyPass("contentScriptKeyframeEffect");
},
},
});
await extension.startup();
await extension.awaitFinish("contentScriptKeyframeEffect");
await extension.unload();
await BrowserTestUtils.removeTab(tab);
});

View File

@ -69,14 +69,19 @@ add_task(async function test_menu() {
// check the button's functionality
CustomizableUI.addWidgetToArea("sync-button", "nav-bar");
let remoteTabsView = document.getElementById("PanelUI-remotetabs");
let viewShown = BrowserTestUtils.waitForEvent(remoteTabsView, "ViewShown");
let syncButton = document.getElementById("sync-button");
syncButton.click();
await viewShown;
await tabsUpdated;
// Get our 1 tab and click on it.
let viewHidden = BrowserTestUtils.waitForEvent(remoteTabsView, "ViewHiding");
let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
let tabEntry = tabList.firstChild.nextSibling;
tabEntry.click();
await viewHidden;
let counts = BUIT._countableEvents[BUIT.currentBucket];
Assert.deepEqual(counts, {

View File

@ -49,6 +49,16 @@ AnimationUtils::GetCurrentRealmDocument(JSContext* aCx)
return win->GetDoc();
}
/* static */ nsIDocument*
AnimationUtils::GetDocumentFromGlobal(JSObject* aGlobalObject)
{
nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobalObject);
if (!win) {
return nullptr;
}
return win->GetDoc();
}
/* static */ bool
AnimationUtils::IsOffscreenThrottlingEnabled()
{

View File

@ -61,6 +61,14 @@ public:
static nsIDocument*
GetCurrentRealmDocument(JSContext* aCx);
/**
* Get the document from the global object, or nullptr if the document has
* no window, to use when constructing DOM object without entering the
* target window's compartment (see KeyframeEffect constructor).
*/
static nsIDocument*
GetDocumentFromGlobal(JSObject* aGlobalObject);
/**
* Checks if offscreen animation throttling is enabled.
*/

View File

@ -903,7 +903,17 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(
const OptionsType& aOptions,
ErrorResult& aRv)
{
nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
// We should get the document from `aGlobal` instead of the current Realm
// to make this works in Xray case.
//
// In all non-Xray cases, `aGlobal` matches the current Realm, so this
// matches the spec behavior.
//
// In Xray case, the new objects should be created using the document of
// the target global, but KeyframeEffect and KeyframeEffectReadOnly
// constructors are called in the caller's compartment to access
// `aKeyframes` object.
nsIDocument* doc = AnimationUtils::GetDocumentFromGlobal(aGlobal.Get());
if (!doc) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;

View File

@ -8,6 +8,7 @@ support-files =
[chrome/test_animate_xrays.html]
# file_animate_xrays.html needs to go in mochitest.ini since it is served
# over HTTP
[chrome/test_keyframe_effect_xrays.html]
[chrome/test_animation_observers_async.html]
[chrome/test_animation_observers_sync.html]
[chrome/test_animation_performance_warning.html]

View File

@ -6,10 +6,9 @@
Element.prototype.animate = function() {
throw 'Called animate() as defined in content document';
}
// Bug 1211783: Use KeyframeEffect (not KeyframeEffectReadOnly) here
for (var obj of [KeyframeEffectReadOnly, Animation]) {
obj = function() {
throw 'Called overridden ' + String(obj) + ' constructor';
for (let name of ["KeyframeEffect", "KeyframeEffectReadOnly", "Animation"]) {
this[name] = function() {
throw `Called overridden ${name} constructor`;
};
}
</script>

View File

@ -6,8 +6,8 @@
<script type="application/javascript" src="../testcommon.js"></script>
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045994"
target="_blank">Mozilla Bug 1045994</a>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1414674"
target="_blank">Mozilla Bug 1414674</a>
<div id="log"></div>
<iframe id="iframe"
src="http://example.org/tests/dom/animation/test/chrome/file_animate_xrays.html"></iframe>
@ -19,10 +19,19 @@ var win = document.getElementById('iframe').contentWindow;
async_test(function(t) {
window.addEventListener('load', t.step_func(function() {
var target = win.document.getElementById('target');
var anim = target.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
// In the x-ray case, the frames object will be given an opaque wrapper
// so it won't be possible to fetch any frames from it.
assert_equals(anim.effect.getKeyframes().length, 0);
var anim = target.animate({opacity: [ 1, 0 ]}, 100 * MS_PER_SEC);
// The frames object should be accessible via x-ray.
var frames = anim.effect.getKeyframes();
assert_equals(frames.length, 2,
"frames for Element.animate should be non-zero");
assert_equals(frames[0].opacity, "1",
"first frame opacity for Element.animate should be specified value");
assert_equals(frames[0].computedOffset, 0,
"first frame offset for Element.animate should be 0");
assert_equals(frames[1].opacity, "0",
"last frame opacity for Element.animate should be specified value");
assert_equals(frames[1].computedOffset, 1,
"last frame offset for Element.animate should be 1");
t.done();
}));
}, 'Calling animate() across x-rays');

View File

@ -0,0 +1,45 @@
<!doctype html>
<head>
<meta charset=utf-8>
<script type="application/javascript" src="../testharness.js"></script>
<script type="application/javascript" src="../testharnessreport.js"></script>
<script type="application/javascript" src="../testcommon.js"></script>
</head>
<body>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1414674"
target="_blank">Mozilla Bug 1414674</a>
<div id="log"></div>
<iframe id="iframe"
src="http://example.org/tests/dom/animation/test/chrome/file_animate_xrays.html"></iframe>
<script>
'use strict';
var win = document.getElementById('iframe').contentWindow;
async_test(function(t) {
window.addEventListener('load', t.step_func(function() {
var target = win.document.getElementById('target');
var effect = new win.KeyframeEffect(target, [
{opacity: 1, offset: 0},
{opacity: 0, offset: 1},
], {duration: 100 * MS_PER_SEC, fill: "forwards"});
// The frames object should be accessible via x-ray.
var frames = effect.getKeyframes();
assert_equals(frames.length, 2,
"frames for KeyframeEffect ctor should be non-zero");
assert_equals(frames[0].opacity, "1",
"first frame opacity for KeyframeEffect ctor should be specified value");
assert_equals(frames[0].computedOffset, 0,
"first frame offset for KeyframeEffect ctor should be 0");
assert_equals(frames[1].opacity, "0",
"last frame opacity for KeyframeEffect ctor should be specified value");
assert_equals(frames[1].computedOffset, 1,
"last frame offset for KeyframeEffect ctor should be 1");
var animation = new win.Animation(effect, document.timeline);
animation.play();
t.done();
}));
}, 'Calling KeyframeEffect() ctor across x-rays');
</script>
</body>

View File

@ -3804,22 +3804,23 @@ Element::Animate(const Nullable<ElementOrCSSPseudoElement>& aTarget,
GlobalObject global(aContext, ownerGlobal->GetGlobalJSObject());
MOZ_ASSERT(!global.Failed());
// Wrap the aKeyframes object for the cross-compartment case.
JS::Rooted<JSObject*> keyframes(aContext);
keyframes = aKeyframes;
// KeyframeEffect constructor doesn't follow the standard Xray calling
// convention and needs to be called in caller's compartment.
// This should match to RunConstructorInCallerCompartment attribute in
// KeyframeEffect.webidl.
RefPtr<KeyframeEffect> effect =
KeyframeEffect::Constructor(global, aTarget, aKeyframes, aOptions,
aError);
if (aError.Failed()) {
return nullptr;
}
// Animation constructor follows the standard Xray calling convention and
// needs to be called in the target element's compartment.
Maybe<JSAutoCompartment> ac;
if (js::GetContextCompartment(aContext) !=
js::GetObjectCompartment(ownerGlobal->GetGlobalJSObject())) {
ac.emplace(aContext, ownerGlobal->GetGlobalJSObject());
if (!JS_WrapObject(aContext, &keyframes)) {
return nullptr;
}
}
RefPtr<KeyframeEffect> effect =
KeyframeEffect::Constructor(global, aTarget, keyframes, aOptions, aError);
if (aError.Failed()) {
return nullptr;
}
AnimationTimeline* timeline = referenceElement->OwnerDoc()->Timeline();

View File

@ -2239,15 +2239,16 @@ nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
// Note: this should match nsDocShell::OnLoadingSite
NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
bool isWyciwyg = false;
uri->SchemeIs("wyciwyg", &isWyciwyg);
if (isWyciwyg) {
nsCOMPtr<nsIURI> cleanURI;
nsresult rv = nsContentUtils::RemoveWyciwygScheme(uri, getter_AddRefs(cleanURI));
if (NS_SUCCEEDED(rv)) {
uri = cleanURI;
bool isWyciwyg = false;
uri->SchemeIs("wyciwyg", &isWyciwyg);
if (isWyciwyg) {
nsCOMPtr<nsIURI> cleanURI;
nsresult rv =
nsContentUtils::RemoveWyciwygScheme(uri, getter_AddRefs(cleanURI));
if (NS_SUCCEEDED(rv)) {
uri = cleanURI;
}
}
}
nsIScriptSecurityManager *securityManager =
nsContentUtils::GetSecurityManager();

View File

@ -7806,7 +7806,10 @@ class CGPerSignatureCall(CGThing):
needsUnwrap = False
argsPost = []
if isConstructor:
runConstructorInCallerCompartment = \
descriptor.interface.getExtendedAttribute(
'RunConstructorInCallerCompartment')
if isConstructor and not runConstructorInCallerCompartment:
needsUnwrap = True
needsUnwrappedVar = False
unwrappedVar = "obj"

View File

@ -1743,6 +1743,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "LegacyEventInit" or
identifier == "ProbablyShortLivingWrapper" or
identifier == "LegacyUnenumerableNamedProperties" or
identifier == "RunConstructorInCallerCompartment" or
identifier == "NonOrdinaryGetPrototypeOf"):
# Known extended attributes that do not take values
if not attr.noArguments():

View File

@ -20,7 +20,10 @@ dictionary KeyframeEffectOptions : AnimationEffectTimingProperties {
CompositeOperation composite = "replace";
};
// KeyframeEffectReadOnly should run in the caller's compartment to do custom
// processing on the `keyframes` object.
[Func="nsDocument::IsWebAnimationsEnabled",
RunConstructorInCallerCompartment,
Constructor ((Element or CSSPseudoElement)? target,
object? keyframes,
optional (unrestricted double or KeyframeEffectOptions) options),
@ -54,7 +57,10 @@ partial interface KeyframeEffectReadOnly {
[ChromeOnly, Throws] sequence<AnimationPropertyDetails> getProperties();
};
// KeyframeEffect should run in the caller's compartment to do custom
// processing on the `keyframes` object.
[Func="nsDocument::IsWebAnimationsEnabled",
RunConstructorInCallerCompartment,
Constructor ((Element or CSSPseudoElement)? target,
object? keyframes,
optional (unrestricted double or KeyframeEffectOptions) options),

View File

@ -26,6 +26,13 @@ static const JSClass global_class = {
&global_classOps
};
static volatile int dontOptimizeMeAway = 0;
void
usePointer(const void* ptr) {
dontOptimizeMeAway++;
}
template<typename T>
static inline T*
checkPtr(T* ptr)

View File

@ -22,8 +22,26 @@
#include "NamespaceImports.h"
#include "js/GCAnnotations.h"
void breakpoint();
extern void usePointer(const void* ptr);
template <typename T>
void use(const T& thing) {
usePointer(&thing);
}
struct AutoSuppressHazardsForTest {
int dummy;
AutoSuppressHazardsForTest() : dummy(3) {}
~AutoSuppressHazardsForTest() {
// Need nontrivial destructor.
usePointer(&dummy);
}
} JS_HAZ_GC_SUPPRESSED;
struct GDBFragment {
GDBFragment() {
next = allFragments;

View File

@ -12,10 +12,16 @@
import os
import sys
import traceback
def execfile(filename, globs, locs):
with open(filename) as f:
code = compile(f.read(), filename, 'exec')
exec(code, globs, locs)
try:
# testlibdir is set on the GDB command line, via:
# --eval-command python testlibdir=...
exec(open(os.path.join(testlibdir, 'prologue.py')).read())
execfile(os.path.join(testlibdir, 'prologue.py'), globals(), locals())
except Exception as err:
sys.stderr.write('Error running GDB prologue:\n')
traceback.print_exc()

View File

@ -7,11 +7,13 @@ import traceback
# testlibdir is set on the GDB command line, via --eval-command python testlibdir=...
sys.path[0:0] = [testlibdir]
active_fragment = None
# Run the C++ fragment named |fragment|, stopping on entry to |function|
# ('breakpoint', by default) and then select the calling frame.
def run_fragment(fragment, function='breakpoint'):
# Arrange to stop at a reasonable place in the test program.
bp = gdb.Breakpoint(function);
bp = gdb.Breakpoint(function)
try:
gdb.execute("run %s" % (fragment,))
# Check that we did indeed stop by hitting the breakpoint we set.
@ -20,6 +22,9 @@ def run_fragment(fragment, function='breakpoint'):
bp.delete()
gdb.execute('frame 1')
global active_fragment
active_fragment = fragment
# Assert that |actual| is equal to |expected|; if not, complain in a helpful way.
def assert_eq(actual, expected):
if actual != expected:
@ -79,12 +84,14 @@ gdb.execute('set width 0')
try:
# testscript is set on the GDB command line, via:
# --eval-command python testscript=...
exec(open(testscript).read())
execfile(testscript, globals(), locals())
except AssertionError as err:
sys.stderr.write('\nAssertion traceback:\n')
header = '\nAssertion traceback'
if active_fragment:
header += ' for ' + active_fragment
sys.stderr.write(header + ':\n')
(t, v, tb) = sys.exc_info()
traceback.print_tb(tb)
sys.stderr.write('\nTest assertion failed:\n')
sys.stderr.write(str(err))
sys.exit(1)

View File

@ -6,8 +6,13 @@
GeckoProgram('gdb-tests', linkage=None)
SOURCES += [
# This file must not be unified with any of the test files, or the use()
# and breakpoint() functions might get optimized out.
'gdb-tests.cpp'
]
UNIFIED_SOURCES += [
'gdb-tests.cpp',
'tests/test-asmjs.cpp',
'tests/test-ExecutableAllocator.cpp',
'tests/test-GCCellPtr.cpp',

View File

@ -55,14 +55,14 @@ class jsjitExecutableAllocator(object):
def __iter__(self):
return self;
def next(self):
def __next__(self):
cur = self.index
if cur >= self.max:
raise StopIteration()
self.index = self.index + 1
if self.table[cur]['keyHash'] > 1: # table[i]->isLive()
return self.table[cur]['mem']['u']['mDummy'].cast(self.entryType)
return self.next()
return self.__next__()
@ptr_pretty_printer("js::jit::ExecutablePool")
class jsjitExecutablePool(mozilla.prettyprinters.Pointer):

View File

@ -16,6 +16,7 @@ class JSObjectTypeCache(object):
self.func_ptr_type = gdb.lookup_type('JSFunction').pointer()
self.class_NON_NATIVE = gdb.parse_and_eval('js::Class::NON_NATIVE')
self.NativeObject_ptr_t = gdb.lookup_type('js::NativeObject').pointer()
self.Shape_ptr_t = gdb.lookup_type('js::Shape').pointer()
# There should be no need to register this for JSFunction as well, since we
# search for pretty-printers under the names of base classes, and
@ -47,7 +48,7 @@ class JSObjectPtrOrRef(prettyprinters.Pointer):
return '[object {}]'.format(class_name)
else:
native = self.value.cast(self.otc.NativeObject_ptr_t)
shape = deref(native['shape_'])
shape = native['shapeOrExpando_'].cast(self.otc.Shape_ptr_t)
baseshape = deref(shape['base_'])
flags = baseshape['flags']
is_delegate = bool(flags & self.otc.flag_DELEGATE)

View File

@ -10,7 +10,7 @@ FRAGMENT(ExecutableAllocator, empty) {
breakpoint();
(void) execAlloc;
use(execAlloc);
}
FRAGMENT(ExecutableAllocator, onepool) {
@ -21,8 +21,8 @@ FRAGMENT(ExecutableAllocator, onepool) {
breakpoint();
(void) pool;
(void) execAlloc;
use(pool);
use(execAlloc);
}
FRAGMENT(ExecutableAllocator, twopools) {
@ -39,5 +39,5 @@ FRAGMENT(ExecutableAllocator, twopools) {
breakpoint();
(void) execAlloc;
use(execAlloc);
}

View File

@ -16,8 +16,8 @@ FRAGMENT(GCCellPtr, simple) {
breakpoint();
(void) nulll;
(void) object;
(void) string;
(void) symbol;
use(nulll);
use(object);
use(string);
use(symbol);
}

View File

@ -73,7 +73,7 @@ FRAGMENT(Interpreter, Regs) {
breakpoint();
(void) regs;
use(regs);
}
FRAGMENT(Interpreter, AbstractFramePtr) {
@ -95,8 +95,8 @@ FRAGMENT(Interpreter, AbstractFramePtr) {
breakpoint();
(void) sfidptr;
(void) ifptr;
(void) bfptr;
(void) rfptr;
use(sfidptr);
use(ifptr);
use(bfptr);
use(rfptr);
}

View File

@ -2,6 +2,8 @@
#include "jsapi.h"
FRAGMENT(JSObject, simple) {
AutoSuppressHazardsForTest noanalysis;
JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
JS::Rooted<JSObject*> plain(cx, JS_NewPlainObject(cx));
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
@ -16,20 +18,24 @@ FRAGMENT(JSObject, simple) {
JSObject* plainRaw = plain;
JSObject* funcRaw = func;
static const JSClass cls = { "\xc7X" };
// JS_NewObject will now assert if you feed it a bad class name, so mangle
// the name after construction.
char namebuf[20] = "goodname";
static JSClass cls { namebuf };
JS::RootedObject badClassName(cx, JS_NewObject(cx, &cls));
strcpy(namebuf, "\xc7X");
breakpoint();
(void) glob;
(void) plain;
(void) func;
(void) anon;
(void) funcPtr;
(void) &plainRef;
(void) &funcRef;
(void) plainRaw;
(void) funcRaw;
use(glob);
use(plain);
use(func);
use(anon);
use(funcPtr);
use(&plainRef);
use(&funcRef);
use(plainRaw);
use(funcRaw);
}
FRAGMENT(JSObject, null) {
@ -38,6 +44,6 @@ FRAGMENT(JSObject, null) {
breakpoint();
(void) null;
(void) nullRaw;
use(null);
use(nullRaw);
}

View File

@ -6,6 +6,8 @@
#include "vm/String.h"
FRAGMENT(JSString, simple) {
AutoSuppressHazardsForTest noanalysis;
JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
JS::Rooted<JSString*> x(cx, JS_NewStringCopyN(cx, "x", 1));
JS::Rooted<JSString*> z(cx, JS_NewStringCopyZ(cx, "z"));
@ -28,23 +30,25 @@ FRAGMENT(JSString, simple) {
breakpoint();
(void) empty;
(void) x;
(void) z;
(void) stars;
(void) xz;
(void) doubleStars;
(void) xRaw;
use(empty);
use(x);
use(z);
use(stars);
use(xz);
use(doubleStars);
use(xRaw);
}
FRAGMENT(JSString, null) {
AutoSuppressHazardsForTest noanalysis;
JS::Rooted<JSString*> null(cx, nullptr);
JSString* nullRaw = null;
breakpoint();
(void) null;
(void) nullRaw;
use(null);
use(nullRaw);
}
FRAGMENT(JSString, subclasses) {
@ -52,12 +56,12 @@ FRAGMENT(JSString, subclasses) {
breakpoint();
(void) flat;
use(flat);
}
FRAGMENT(JSString, atom) {
JSAtom* molybdenum = js::Atomize(cx, "molybdenum", 10);
breakpoint();
(void) molybdenum;
use(molybdenum);
}

View File

@ -13,8 +13,8 @@ FRAGMENT(JSSymbol, simple) {
breakpoint();
(void) unique;
(void) unique_with_desc;
(void) registry;
(void) well_known;
use(unique);
use(unique_with_desc);
use(registry);
use(well_known);
}

View File

@ -10,7 +10,7 @@ FRAGMENT(Root, null) {
breakpoint();
(void) null;
use(null);
}
void callee(JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> mutableObj)
@ -24,7 +24,7 @@ void callee(JS::Handle<JSObject*> obj, JS::MutableHandle<JSObject*> mutableObj)
FRAGMENT(Root, handle) {
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
callee(global, &global);
(void) global;
use(global);
}
FRAGMENT(Root, HeapSlot) {
@ -33,8 +33,8 @@ FRAGMENT(Root, HeapSlot) {
breakpoint();
(void) plinth;
(void) array;
use(plinth);
use(array);
}
FRAGMENT(Root, barriers) {
@ -50,12 +50,12 @@ FRAGMENT(Root, barriers) {
breakpoint();
(void) prebarriered;
(void) heapptr;
(void) relocatable;
(void) val;
(void) prebarrieredValue;
(void) heapValue;
(void) relocatableValue;
use(prebarriered);
use(heapptr);
use(relocatable);
use(val);
use(prebarrieredValue);
use(heapValue);
use(relocatableValue);
}

View File

@ -14,7 +14,7 @@ run_fragment('Root.HeapSlot')
# This depends on implementation details of arrays, but since HeapSlot is
# not a public type, I'm not sure how to avoid doing *something* ugly.
assert_pretty('((js::NativeObject *) array.get())->elements_[0]', '$jsval("plinth")')
assert_pretty('((js::NativeObject *) array.ptr)->elements_[0]', '$jsval("plinth")')
run_fragment('Root.barriers');

View File

@ -33,6 +33,6 @@ FRAGMENT(asmjs, segfault) {
breakpoint();
(void) ok;
(void) rval;
use(ok);
use(rval);
}

View File

@ -17,13 +17,13 @@ FRAGMENT(jsid, simple) {
breakpoint();
(void) string_id;
(void) int_id;
(void) unique_symbol_id;
(void) registry_symbol_id;
(void) well_known_symbol_id;
(void) void_id;
(void) empty_id;
use(string_id);
use(int_id);
use(unique_symbol_id);
use(registry_symbol_id);
use(well_known_symbol_id);
use(void_id);
use(empty_id);
}
void

View File

@ -26,15 +26,15 @@ FRAGMENT(jsval, simple) {
breakpoint();
(void) fortytwo;
(void) negone;
(void) undefined;
(void) js_true;
(void) js_false;
(void) null;
(void) elements_hole;
(void) empty_string;
(void) friendly_string;
(void) symbol;
(void) global;
use(fortytwo);
use(negone);
use(undefined);
use(js_true);
use(js_false);
use(null);
use(elements_hole);
use(empty_string);
use(friendly_string);
use(symbol);
use(global);
}

View File

@ -26,13 +26,13 @@ FRAGMENT(prettyprinters, implemented_types) {
breakpoint();
(void) i;
(void) a;
(void) b;
(void) c;
(void) c_;
(void) e;
(void) e_;
(void) f;
(void) h;
use(i);
use(a);
use(b);
use(c);
use(c_);
use(e);
use(e_);
use(f);
use(h);
}

View File

@ -7,5 +7,5 @@ FRAGMENT(typedef_printers, one) {
breakpoint();
(void) i;
use(i);
}

View File

@ -1,6 +1,11 @@
This directory contains FreeType v2.9 downloaded from
https://download.savannah.gnu.org/releases/freetype/
There are currently no local changes applied to the FreeType tree,
except that the file docs/LICENSE.TXT has been copied to the top-level
directory of the FreeType tree as LICENSE.TXT.
The following post-2.9 commits have been cherry-picked from
the upstream FreeType repository (see bug 1434697, bug 1438522):
http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=994eb2b34934bc5face9f83b2d3b12cf7a9262ab
http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=cc2f3cdecff5a351e7e8961b9f2e389ab740231a
http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=4a03f17449ae45f0dacf4de4694ccd6e5e1b24d1
http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=68dddcdcbe18a08d778026efc01b1369e35cbf6a
http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=29c759284e305ec428703c9a5831d0b1fc3497ef

View File

@ -852,7 +852,8 @@
curY = ADD_INT32( curY, cf2_stack_popFixed( opStack ) );
cf2_glyphpath_moveTo( &glyphPath, curX, curY );
if ( !decoder->flex_state )
cf2_glyphpath_moveTo( &glyphPath, curX, curY );
break;
@ -2674,7 +2675,8 @@
curX = ADD_INT32( curX, cf2_stack_popFixed( opStack ) );
cf2_glyphpath_moveTo( &glyphPath, curX, curY );
if ( !decoder->flex_state )
cf2_glyphpath_moveTo( &glyphPath, curX, curY );
break;

View File

@ -2762,8 +2762,9 @@
}
}
/* return value -1 indicates `no change' */
if ( !have_diff )
/* return value -1 indicates `no change'; */
/* we can exit early if `normalizedcoords' is already computed */
if ( blend->normalizedcoords && !have_diff )
return -1;
if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )

View File

@ -5782,6 +5782,7 @@
FT_F26Dot6 distance;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
FT_F26Dot6 control_value_cutin = 0;
FT_F26Dot6 delta;
if ( SUBPIXEL_HINTING_INFINALITY )
@ -5817,11 +5818,15 @@
distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
delta = SUB_LONG( distance, args[1] );
if ( delta < 0 )
delta = NEG_LONG( delta );
/* subpixel hinting - make MSIRP respect CVT cut-in; */
if ( SUBPIXEL_HINTING_INFINALITY &&
exc->ignore_x_mode &&
exc->GS.freeVector.x != 0 &&
FT_ABS( SUB_LONG( distance, args[1] ) ) >= control_value_cutin )
if ( SUBPIXEL_HINTING_INFINALITY &&
exc->ignore_x_mode &&
exc->GS.freeVector.x != 0 &&
delta >= control_value_cutin )
distance = args[1];
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
@ -5978,7 +5983,14 @@
if ( ( exc->opcode & 1 ) != 0 ) /* rounding and control cut-in flag */
{
if ( FT_ABS( distance - org_dist ) > control_value_cutin )
FT_F26Dot6 delta;
delta = SUB_LONG( distance, org_dist );
if ( delta < 0 )
delta = NEG_LONG( delta );
if ( delta > control_value_cutin )
distance = org_dist;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
@ -6259,6 +6271,9 @@
if ( exc->GS.gep0 == exc->GS.gep1 )
{
FT_F26Dot6 delta;
/* XXX: According to Greg Hitchcock, the following wording is */
/* the right one: */
/* */
@ -6271,7 +6286,11 @@
/* `ttinst2.doc', version 1.66, is thus incorrect since */
/* it implies `>=' instead of `>'. */
if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin )
delta = SUB_LONG( cvt_dist, org_dist );
if ( delta < 0 )
delta = NEG_LONG( delta );
if ( delta > control_value_cutin )
cvt_dist = org_dist;
}
@ -6289,7 +6308,14 @@
exc->ignore_x_mode &&
exc->GS.gep0 == exc->GS.gep1 )
{
if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin )
FT_F26Dot6 delta;
delta = SUB_LONG( cvt_dist, org_dist );
if ( delta < 0 )
delta = NEG_LONG( delta );
if ( delta > control_value_cutin )
cvt_dist = org_dist;
}
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
@ -7532,8 +7558,16 @@
return;
}
for ( i = 0; i < num_axes; i++ )
args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
if ( coords )
{
for ( i = 0; i < num_axes; i++ )
args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
}
else
{
for ( i = 0; i < num_axes; i++ )
args[i] = 0;
}
}

View File

@ -354,12 +354,16 @@ html|*.help-button:hover:active {
background-color: var(--in-content-category-background-active);
}
html|*.numberbox-input::-moz-number-spin-box {
margin-inline-end: 1px;
}
html|*.numberbox-input::-moz-number-spin-up,
html|*.numberbox-input::-moz-number-spin-down {
padding: 5px 8px;
margin: 1px;
margin-inline-start: 10px;
margin: 0;
min-height: initial;
background-position: center;
}
html|*.numberbox-input::-moz-number-spin-up {
@ -468,6 +472,14 @@ xul|textbox {
padding-left: 10px;
}
xul|textbox[type="number"] {
padding-inline-end: 0;
}
html|*.numberbox-input::-moz-number-text {
margin-inline-end: 10px;
}
/* Create a separate rule to unset these styles on .tree-input instead of
using :not(.tree-input) so the selector specifity doesn't change. */
xul|textbox.tree-input {