mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Merge mozilla-inbound to mozilla-central. a=merge
This commit is contained in:
commit
2e506ce663
@ -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());
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
});
|
@ -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, {
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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>
|
||||
|
@ -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');
|
||||
|
45
dom/animation/test/chrome/test_keyframe_effect_xrays.html
Normal file
45
dom/animation/test/chrome/test_keyframe_effect_xrays.html
Normal 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>
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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():
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ FRAGMENT(GCCellPtr, simple) {
|
||||
|
||||
breakpoint();
|
||||
|
||||
(void) nulll;
|
||||
(void) object;
|
||||
(void) string;
|
||||
(void) symbol;
|
||||
use(nulll);
|
||||
use(object);
|
||||
use(string);
|
||||
use(symbol);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
||||
|
@ -33,6 +33,6 @@ FRAGMENT(asmjs, segfault) {
|
||||
|
||||
breakpoint();
|
||||
|
||||
(void) ok;
|
||||
(void) rval;
|
||||
use(ok);
|
||||
use(rval);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ FRAGMENT(typedef_printers, one) {
|
||||
|
||||
breakpoint();
|
||||
|
||||
(void) i;
|
||||
use(i);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 ) )
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user