зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
397a156a63
|
@ -201,7 +201,6 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
this.node.getElementsByTagName("panelview"),
|
||||
node => PanelView.forNode(node)));
|
||||
this.openViews = [];
|
||||
this._mainViewHeight = 0;
|
||||
this.__transitioning = false;
|
||||
this.showingSubView = false;
|
||||
|
||||
|
@ -357,43 +356,34 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
viewNode.panelMultiView = this.node;
|
||||
|
||||
let reverse = !!aPreviousView;
|
||||
if (!reverse) {
|
||||
nextPanelView.headerText = viewNode.getAttribute("title") ||
|
||||
(aAnchor && aAnchor.getAttribute("label"));
|
||||
}
|
||||
|
||||
let previousViewNode = aPreviousView || this._currentSubView;
|
||||
// If the panelview to show is the same as the previous one, the 'ViewShowing'
|
||||
// event has already been dispatched. Don't do it twice.
|
||||
let showingSameView = viewNode == previousViewNode;
|
||||
let playTransition = (!!previousViewNode && !showingSameView && this._panel.state == "open");
|
||||
let isMainView = viewNode.id == this._mainViewId;
|
||||
|
||||
let previousRect = previousViewNode.__lastKnownBoundingRect =
|
||||
this._dwu.getBoundsWithoutFlushing(previousViewNode);
|
||||
// Cache the measures that have the same caching lifetime as the width
|
||||
// or height of the main view, i.e. whilst the panel is shown and/ or
|
||||
// visible.
|
||||
if (!this._mainViewWidth) {
|
||||
this._mainViewWidth = previousRect.width;
|
||||
}
|
||||
if (!this._mainViewHeight) {
|
||||
this._mainViewHeight = previousRect.height;
|
||||
this._viewContainer.style.minHeight = this._mainViewHeight + "px";
|
||||
}
|
||||
let prevPanelView = PanelView.forNode(previousViewNode);
|
||||
prevPanelView.captureKnownSize();
|
||||
|
||||
this._viewShowing = viewNode;
|
||||
// Because the 'mainview' attribute may be out-of-sync, due to view node
|
||||
// reparenting in combination with ephemeral PanelMultiView instances,
|
||||
// this is the best place to correct it (just before showing).
|
||||
nextPanelView.mainview = isMainView;
|
||||
|
||||
let reverse = !!aPreviousView;
|
||||
if (!reverse) {
|
||||
// We are opening a new view, either because we are navigating forward
|
||||
// or because we are showing the main view. Some properties of the view
|
||||
// may vary between panels, so we make sure to update them every time.
|
||||
// Firstly, make sure that the header matches how the view was opened.
|
||||
nextPanelView.headerText = viewNode.getAttribute("title") ||
|
||||
(aAnchor && aAnchor.getAttribute("label"));
|
||||
// The main view of a panel can be a subview in another one.
|
||||
let isMainView = viewNode.id == this._mainViewId;
|
||||
nextPanelView.mainview = isMainView;
|
||||
// The constrained width of subviews may also vary between panels.
|
||||
nextPanelView.minMaxWidth = isMainView ? 0 : prevPanelView.knownWidth;
|
||||
}
|
||||
|
||||
if (aAnchor) {
|
||||
viewNode.classList.add("PanelUI-subView");
|
||||
}
|
||||
if (!isMainView && this._mainViewWidth)
|
||||
viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
|
||||
|
||||
if (!showingSameView || !viewNode.hasAttribute("current")) {
|
||||
// Emit the ViewShowing event so that the widget definition has a chance
|
||||
|
@ -425,8 +415,8 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
// Now we have to transition the panel. If we've got an older transition
|
||||
// still running, make sure to clean it up.
|
||||
await this._cleanupTransitionPhase();
|
||||
if (playTransition) {
|
||||
await this._transitionViews(previousViewNode, viewNode, reverse, previousRect, aAnchor);
|
||||
if (!showingSameView && this._panel.state == "open") {
|
||||
await this._transitionViews(previousViewNode, viewNode, reverse, aAnchor);
|
||||
nextPanelView.focusSelectedElement();
|
||||
} else {
|
||||
this.hideAllViewsExcept(nextPanelView);
|
||||
|
@ -448,12 +438,10 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
* after the transition has finished.
|
||||
* @param {Boolean} reverse Whether we're navigation back to a
|
||||
* previous view or forward to a next view.
|
||||
* @param {Object} previousRect Rect object, with the same structure as
|
||||
* a DOMRect, of the `previousViewNode`.
|
||||
* @param {Element} anchor the anchor for which we're opening
|
||||
* a new panelview, if any
|
||||
*/
|
||||
async _transitionViews(previousViewNode, viewNode, reverse, previousRect, anchor) {
|
||||
async _transitionViews(previousViewNode, viewNode, reverse, anchor) {
|
||||
// There's absolutely no need to show off our epic animation skillz when
|
||||
// the panel's not even open.
|
||||
if (this._panel.state != "open") {
|
||||
|
@ -463,6 +451,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
const {window, document} = this;
|
||||
|
||||
let nextPanelView = PanelView.forNode(viewNode);
|
||||
let prevPanelView = PanelView.forNode(previousViewNode);
|
||||
|
||||
if (this._autoResizeWorkaroundTimer)
|
||||
window.clearTimeout(this._autoResizeWorkaroundTimer);
|
||||
|
@ -481,23 +470,27 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
previousViewNode.setAttribute("in-transition", true);
|
||||
// Set the viewContainer dimensions to make sure only the current view is
|
||||
// visible.
|
||||
this._viewContainer.style.height = Math.max(previousRect.height, this._mainViewHeight) + "px";
|
||||
this._viewContainer.style.width = previousRect.width + "px";
|
||||
let olderView = reverse ? nextPanelView : prevPanelView;
|
||||
this._viewContainer.style.minHeight = olderView.knownHeight + "px";
|
||||
this._viewContainer.style.height = prevPanelView.knownHeight + "px";
|
||||
this._viewContainer.style.width = prevPanelView.knownWidth + "px";
|
||||
// Lock the dimensions of the window that hosts the popup panel.
|
||||
let rect = this._panel.popupBoxObject.getOuterScreenRect();
|
||||
this._panel.setAttribute("width", rect.width);
|
||||
this._panel.setAttribute("height", rect.height);
|
||||
|
||||
let viewRect;
|
||||
if (reverse && viewNode.__lastKnownBoundingRect) {
|
||||
if (reverse) {
|
||||
// Use the cached size when going back to a previous view, but not when
|
||||
// reopening a subview, because its contents may have changed.
|
||||
viewRect = viewNode.__lastKnownBoundingRect;
|
||||
viewRect = { width: nextPanelView.knownWidth,
|
||||
height: nextPanelView.knownHeight };
|
||||
viewNode.setAttribute("in-transition", true);
|
||||
} else if (viewNode.customRectGetter) {
|
||||
// Can't use Object.assign directly with a DOM Rect object because its properties
|
||||
// aren't enumerable.
|
||||
let {height, width} = previousRect;
|
||||
let width = prevPanelView.knownWidth;
|
||||
let height = prevPanelView.knownHeight;
|
||||
viewRect = Object.assign({height, width}, viewNode.customRectGetter());
|
||||
let header = viewNode.firstChild;
|
||||
if (header && header.classList.contains("panel-header")) {
|
||||
|
@ -506,8 +499,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
viewNode.setAttribute("in-transition", true);
|
||||
} else {
|
||||
let oldSibling = viewNode.nextSibling || null;
|
||||
this._offscreenViewStack.style.minHeight =
|
||||
this._viewContainer.style.height;
|
||||
this._offscreenViewStack.style.minHeight = olderView.knownHeight + "px";
|
||||
this._offscreenViewStack.appendChild(viewNode);
|
||||
viewNode.setAttribute("in-transition", true);
|
||||
|
||||
|
@ -533,7 +525,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
// The 'magic' part: build up the amount of pixels to move right or left.
|
||||
let moveToLeft = (this._dir == "rtl" && !reverse) || (this._dir == "ltr" && reverse);
|
||||
let deltaX = previousRect.width;
|
||||
let deltaX = prevPanelView.knownWidth;
|
||||
let deepestNode = reverse ? previousViewNode : viewNode;
|
||||
|
||||
// With a transition when navigating backwards - user hits the 'back'
|
||||
|
@ -554,7 +546,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
// Now set the viewContainer dimensions to that of the new view, which
|
||||
// kicks of the height animation.
|
||||
this._viewContainer.style.height = Math.max(viewRect.height, this._mainViewHeight) + "px";
|
||||
this._viewContainer.style.height = viewRect.height + "px";
|
||||
this._viewContainer.style.width = viewRect.width + "px";
|
||||
this._panel.removeAttribute("width");
|
||||
this._panel.removeAttribute("height");
|
||||
|
@ -757,13 +749,6 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
this._transitioning = false;
|
||||
this.node.removeAttribute("panelopen");
|
||||
this.showMainView();
|
||||
for (let panelView of this._viewStack.children) {
|
||||
if (panelView.nodeName != "children") {
|
||||
panelView.__lastKnownBoundingRect = null;
|
||||
panelView.style.removeProperty("min-width");
|
||||
panelView.style.removeProperty("max-width");
|
||||
}
|
||||
}
|
||||
this.window.removeEventListener("keydown", this);
|
||||
this._panel.removeEventListener("mousemove", this);
|
||||
this.openViews.forEach(panelView => panelView.clearNavigation());
|
||||
|
@ -771,12 +756,10 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
// Clear the main view size caches. The dimensions could be different
|
||||
// when the popup is opened again, e.g. through touch mode sizing.
|
||||
this._mainViewHeight = 0;
|
||||
this._mainViewWidth = 0;
|
||||
this._viewContainer.style.removeProperty("min-height");
|
||||
this._viewStack.style.removeProperty("max-height");
|
||||
this._viewContainer.style.removeProperty("min-width");
|
||||
this._viewContainer.style.removeProperty("max-width");
|
||||
this._viewContainer.style.removeProperty("width");
|
||||
this._viewContainer.style.removeProperty("height");
|
||||
|
||||
this.dispatchCustomEvent("PanelMultiViewHidden");
|
||||
break;
|
||||
|
@ -816,6 +799,20 @@ this.PanelView = class extends this.AssociatedToNode {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constrains the width of this view using the "min-width" and "max-width"
|
||||
* styles. Setting this to zero removes the constraints.
|
||||
*/
|
||||
set minMaxWidth(value) {
|
||||
let style = this.node.style;
|
||||
if (value) {
|
||||
style.minWidth = style.maxWidth = value + "px";
|
||||
} else {
|
||||
style.removeProperty("min-width");
|
||||
style.removeProperty("max-width");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header with the given title, or removes it if the title is empty.
|
||||
*/
|
||||
|
@ -867,6 +864,19 @@ this.PanelView = class extends this.AssociatedToNode {
|
|||
return super.dispatchCustomEvent(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the "knownWidth" and "knownHeight" properties with the current
|
||||
* dimensions of the view. These may be zero if the view is invisible.
|
||||
*
|
||||
* These values are relevant during transitions and are retained for backwards
|
||||
* navigation if the view is still open but is invisible.
|
||||
*/
|
||||
captureKnownSize() {
|
||||
let rect = this._dwu.getBoundsWithoutFlushing(this.node);
|
||||
this.knownWidth = rect.width;
|
||||
this.knownHeight = rect.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the main view or a subview contains wrapping elements, the attribute
|
||||
* "descriptionheightworkaround" should be set on the view to force all the
|
||||
|
|
|
@ -12,9 +12,18 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor()
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor(uint64_t aId,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerState aState)
|
||||
: mData(MakeUnique<IPCServiceWorkerDescriptor>())
|
||||
{
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
PrincipalToPrincipalInfo(aPrincipal, &mData->principalInfo()));
|
||||
|
||||
mData->id() = aId;
|
||||
mData->scope() = aScope;
|
||||
mData->state() = aState;
|
||||
}
|
||||
|
||||
ServiceWorkerDescriptor::ServiceWorkerDescriptor(uint64_t aId,
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef _mozilla_dom_ServiceWorkerDescriptor_h
|
||||
#define _mozilla_dom_ServiceWorkerDescriptor_h
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
|
@ -29,7 +31,10 @@ class ServiceWorkerDescriptor final
|
|||
UniquePtr<IPCServiceWorkerDescriptor> mData;
|
||||
|
||||
public:
|
||||
ServiceWorkerDescriptor();
|
||||
ServiceWorkerDescriptor(uint64_t aId,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
ServiceWorkerState aState);
|
||||
|
||||
ServiceWorkerDescriptor(uint64_t aId,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
|
|
|
@ -221,6 +221,7 @@ ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
|||
const nsAString& aCacheName,
|
||||
nsLoadFlags aImportsLoadFlags)
|
||||
: mPrincipal(aPrincipal)
|
||||
, mDescriptor(GetNextID(), aPrincipal, aScope, ServiceWorkerState::Parsed)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mCacheName(aCacheName)
|
||||
, mImportsLoadFlags(aImportsLoadFlags)
|
||||
|
@ -243,12 +244,6 @@ ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
|||
// Otherwise, we might not be able to update a service worker correctly, if
|
||||
// there is a service worker generating the script.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mImportsLoadFlags & nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
|
||||
|
||||
PrincipalInfo principalInfo;
|
||||
MOZ_ALWAYS_SUCCEEDS(PrincipalToPrincipalInfo(aPrincipal, &principalInfo));
|
||||
|
||||
mDescriptor = ServiceWorkerDescriptor(GetNextID(), principalInfo, aScope,
|
||||
ServiceWorkerState::Parsed);
|
||||
}
|
||||
|
||||
ServiceWorkerInfo::~ServiceWorkerInfo()
|
||||
|
|
|
@ -101,6 +101,16 @@ dictionary InspectorVariationAxis {
|
|||
required float defaultValue;
|
||||
};
|
||||
|
||||
dictionary InspectorVariationValue {
|
||||
required DOMString axis;
|
||||
required float value;
|
||||
};
|
||||
|
||||
dictionary InspectorVariationInstance {
|
||||
required DOMString name;
|
||||
required sequence<InspectorVariationValue> values;
|
||||
};
|
||||
|
||||
[ChromeOnly]
|
||||
interface InspectorFontFace {
|
||||
// An indication of how we found this font during font-matching.
|
||||
|
@ -115,7 +125,8 @@ interface InspectorFontFace {
|
|||
// (not necessarily the actual name that was used,
|
||||
// due to aliases, generics, localized names, etc)
|
||||
|
||||
[NewObject] sequence<InspectorVariationAxis> getVariationAxes();
|
||||
[NewObject,Throws] sequence<InspectorVariationAxis> getVariationAxes();
|
||||
[NewObject,Throws] sequence<InspectorVariationInstance> getVariationInstances();
|
||||
|
||||
// meaningful only when the font is a user font defined using @font-face
|
||||
readonly attribute CSSFontFaceRule? rule; // null if no associated @font-face rule
|
||||
|
|
|
@ -2166,8 +2166,8 @@ RuntimeService::CrashIfHanging()
|
|||
nsCString msg;
|
||||
|
||||
// A: active Workers | S: active ServiceWorkers | Q: queued Workers
|
||||
msg.AppendPrintf("Workers Hanging - A:%d|S:%d|Q:%d", activeWorkers,
|
||||
activeServiceWorkers, inactiveWorkers);
|
||||
msg.AppendPrintf("Workers Hanging - %d|A:%d|S:%d|Q:%d", mShuttingDown ? 1 : 0,
|
||||
activeWorkers, activeServiceWorkers, inactiveWorkers);
|
||||
|
||||
// For each thread, let's print some data to know what is going wrong.
|
||||
for (uint32_t i = 0; i < workers.Length(); ++i) {
|
||||
|
|
|
@ -120,7 +120,7 @@ GPUVideoTextureHost::NumSubTextures() const
|
|||
}
|
||||
|
||||
void
|
||||
GPUVideoTextureHost::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
GPUVideoTextureHost::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
|
||||
virtual uint32_t NumSubTextures() const override;
|
||||
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID) override;
|
||||
|
|
|
@ -577,13 +577,13 @@ BufferTextureHost::NumSubTextures() const
|
|||
}
|
||||
|
||||
void
|
||||
BufferTextureHost::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
BufferTextureHost::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
{
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::ResourceUpdateQueue::AddExternalImage
|
||||
: &wr::ResourceUpdateQueue::UpdateExternalImage;
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::TransactionBuilder::AddExternalImage
|
||||
: &wr::TransactionBuilder::UpdateExternalImage;
|
||||
auto bufferType = wr::WrExternalImageBufferType::ExternalBuffer;
|
||||
|
||||
if (GetFormat() != gfx::SurfaceFormat::YUV) {
|
||||
|
|
|
@ -42,7 +42,7 @@ class Shmem;
|
|||
|
||||
namespace wr {
|
||||
class DisplayListBuilder;
|
||||
class ResourceUpdateQueue;
|
||||
class TransactionBuilder;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
|
@ -637,7 +637,7 @@ public:
|
|||
};
|
||||
|
||||
// Add all necessary TextureHost informations to the resource update queue.
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
|
@ -754,7 +754,7 @@ public:
|
|||
|
||||
virtual uint32_t NumSubTextures() const override;
|
||||
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID) override;
|
||||
|
|
|
@ -1090,7 +1090,7 @@ DXGITextureHostD3D11::NumSubTextures() const
|
|||
}
|
||||
|
||||
void
|
||||
DXGITextureHostD3D11::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
DXGITextureHostD3D11::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
|
@ -1101,8 +1101,8 @@ DXGITextureHostD3D11::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
|||
}
|
||||
|
||||
MOZ_ASSERT(mHandle);
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::ResourceUpdateQueue::AddExternalImage
|
||||
: &wr::ResourceUpdateQueue::UpdateExternalImage;
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::TransactionBuilder::AddExternalImage
|
||||
: &wr::TransactionBuilder::UpdateExternalImage;
|
||||
switch (mFormat) {
|
||||
case gfx::SurfaceFormat::R8G8B8X8:
|
||||
case gfx::SurfaceFormat::R8G8B8A8:
|
||||
|
@ -1341,7 +1341,7 @@ DXGIYCbCrTextureHostD3D11::NumSubTextures() const
|
|||
}
|
||||
|
||||
void
|
||||
DXGIYCbCrTextureHostD3D11::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
DXGIYCbCrTextureHostD3D11::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
|
@ -1356,8 +1356,8 @@ DXGIYCbCrTextureHostD3D11::PushResourceUpdates(wr::ResourceUpdateQueue& aResourc
|
|||
MOZ_ASSERT(mSize.width % 2 == 0);
|
||||
MOZ_ASSERT(mSize.height % 2 == 0);
|
||||
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::ResourceUpdateQueue::AddExternalImage
|
||||
: &wr::ResourceUpdateQueue::UpdateExternalImage;
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::TransactionBuilder::AddExternalImage
|
||||
: &wr::TransactionBuilder::UpdateExternalImage;
|
||||
auto bufferType = wr::WrExternalImageBufferType::TextureExternalHandle;
|
||||
|
||||
// y
|
||||
|
|
|
@ -360,7 +360,7 @@ public:
|
|||
|
||||
virtual uint32_t NumSubTextures() const override;
|
||||
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID) override;
|
||||
|
@ -422,7 +422,7 @@ public:
|
|||
|
||||
virtual uint32_t NumSubTextures() const override;
|
||||
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID) override;
|
||||
|
|
|
@ -148,15 +148,15 @@ MacIOSurfaceTextureHostOGL::NumSubTextures() const
|
|||
}
|
||||
|
||||
void
|
||||
MacIOSurfaceTextureHostOGL::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
MacIOSurfaceTextureHostOGL::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
{
|
||||
MOZ_ASSERT(mSurface);
|
||||
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::ResourceUpdateQueue::AddExternalImage
|
||||
: &wr::ResourceUpdateQueue::UpdateExternalImage;
|
||||
auto method = aOp == TextureHost::ADD_IMAGE ? &wr::TransactionBuilder::AddExternalImage
|
||||
: &wr::TransactionBuilder::UpdateExternalImage;
|
||||
auto bufferType = wr::WrExternalImageBufferType::TextureRectHandle;
|
||||
|
||||
switch (GetFormat()) {
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
|
||||
virtual uint32_t NumSubTextures() const override;
|
||||
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID) override;
|
||||
|
|
|
@ -131,11 +131,9 @@ AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipel
|
|||
AsyncImagePipeline* holder = entry.Data();
|
||||
++mAsyncImageEpoch; // Update webrender epoch
|
||||
aTxn.ClearDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
|
||||
wr::ResourceUpdateQueue resources;
|
||||
for (wr::ImageKey key : holder->mKeys) {
|
||||
resources.DeleteImage(key);
|
||||
aTxn.DeleteImage(key);
|
||||
}
|
||||
aTxn.UpdateResources(resources);
|
||||
entry.Remove();
|
||||
RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
|
||||
}
|
||||
|
@ -165,7 +163,7 @@ AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipel
|
|||
}
|
||||
|
||||
Maybe<TextureHost::ResourceUpdateOp>
|
||||
AsyncImagePipelineManager::UpdateImageKeys(wr::ResourceUpdateQueue& aResources,
|
||||
AsyncImagePipelineManager::UpdateImageKeys(wr::TransactionBuilder& aResources,
|
||||
AsyncImagePipeline* aPipeline,
|
||||
nsTArray<wr::ImageKey>& aKeys)
|
||||
{
|
||||
|
@ -230,7 +228,7 @@ AsyncImagePipelineManager::UpdateImageKeys(wr::ResourceUpdateQueue& aResources,
|
|||
}
|
||||
|
||||
Maybe<TextureHost::ResourceUpdateOp>
|
||||
AsyncImagePipelineManager::UpdateWithoutExternalImage(wr::ResourceUpdateQueue& aResources,
|
||||
AsyncImagePipelineManager::UpdateWithoutExternalImage(wr::TransactionBuilder& aResources,
|
||||
TextureHost* aTexture,
|
||||
wr::ImageKey aKey,
|
||||
TextureHost::ResourceUpdateOp aOp)
|
||||
|
@ -286,15 +284,11 @@ AsyncImagePipelineManager::ApplyAsyncImages()
|
|||
// We use a pipeline with a very small display list for each video element.
|
||||
// Update each of them if needed.
|
||||
for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
|
||||
wr::ResourceUpdateQueue resourceUpdates;
|
||||
|
||||
wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
|
||||
AsyncImagePipeline* pipeline = iter.Data();
|
||||
|
||||
nsTArray<wr::ImageKey> keys;
|
||||
auto op = UpdateImageKeys(resourceUpdates, pipeline, keys);
|
||||
|
||||
txn.UpdateResources(resourceUpdates);
|
||||
auto op = UpdateImageKeys(txn, pipeline, keys);
|
||||
|
||||
bool updateDisplayList = pipeline->mInitialised &&
|
||||
(pipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
|
||||
|
|
|
@ -155,11 +155,11 @@ private:
|
|||
};
|
||||
|
||||
Maybe<TextureHost::ResourceUpdateOp>
|
||||
UpdateImageKeys(wr::ResourceUpdateQueue& aResourceUpdates,
|
||||
UpdateImageKeys(wr::TransactionBuilder& aResourceUpdates,
|
||||
AsyncImagePipeline* aPipeline,
|
||||
nsTArray<wr::ImageKey>& aKeys);
|
||||
Maybe<TextureHost::ResourceUpdateOp>
|
||||
UpdateWithoutExternalImage(wr::ResourceUpdateQueue& aResources,
|
||||
UpdateWithoutExternalImage(wr::TransactionBuilder& aResources,
|
||||
TextureHost* aTexture,
|
||||
wr::ImageKey aKey,
|
||||
TextureHost::ResourceUpdateOp);
|
||||
|
|
|
@ -270,7 +270,7 @@ bool
|
|||
WebRenderBridgeParent::UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
|
||||
const nsTArray<RefCountedShmem>& aSmallShmems,
|
||||
const nsTArray<ipc::Shmem>& aLargeShmems,
|
||||
wr::ResourceUpdateQueue& aUpdates)
|
||||
wr::TransactionBuilder& aUpdates)
|
||||
{
|
||||
wr::ShmSegmentsReader reader(aSmallShmems, aLargeShmems);
|
||||
|
||||
|
@ -374,7 +374,7 @@ WebRenderBridgeParent::UpdateResources(const nsTArray<OpUpdateResource>& aResour
|
|||
|
||||
bool
|
||||
WebRenderBridgeParent::AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
|
||||
wr::ResourceUpdateQueue& aResources)
|
||||
wr::TransactionBuilder& aResources)
|
||||
{
|
||||
Range<wr::ImageKey> keys(&aKey, 1);
|
||||
// Check if key is obsoleted.
|
||||
|
@ -446,16 +446,14 @@ WebRenderBridgeParent::RecvUpdateResources(nsTArray<OpUpdateResource>&& aResourc
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
wr::ResourceUpdateQueue updates;
|
||||
wr::TransactionBuilder txn;
|
||||
|
||||
if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, updates)) {
|
||||
if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
|
||||
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
|
||||
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
|
||||
IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
|
||||
}
|
||||
|
||||
wr::TransactionBuilder txn;
|
||||
txn.UpdateResources(updates);
|
||||
mApi->SendTransaction(txn);
|
||||
|
||||
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
|
||||
|
@ -595,14 +593,11 @@ WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntSize& aSize,
|
|||
|
||||
ProcessWebRenderParentCommands(aCommands);
|
||||
|
||||
wr::ResourceUpdateQueue resources;
|
||||
if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, resources)) {
|
||||
wr::TransactionBuilder txn;
|
||||
if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
|
||||
return IPC_FAIL(this, "Failed to deserialize resource updates");
|
||||
}
|
||||
|
||||
wr::TransactionBuilder txn;
|
||||
txn.UpdateResources(resources);
|
||||
|
||||
wr::Vec<uint8_t> dlData(Move(dl));
|
||||
|
||||
// If id namespaces do not match, it means the command is obsolete, probably
|
||||
|
@ -610,8 +605,9 @@ WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntSize& aSize,
|
|||
// In that case do not send the commands to webrender.
|
||||
if (mIdNamespace == aIdNamespace) {
|
||||
if (mWidget) {
|
||||
LayoutDeviceIntSize size = mWidget->GetClientSize();
|
||||
txn.SetWindowParameters(size);
|
||||
LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
|
||||
LayoutDeviceIntRect docRect(LayoutDeviceIntPoint(), widgetSize);
|
||||
txn.SetWindowParameters(widgetSize, docRect);
|
||||
}
|
||||
gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
|
||||
txn.SetDisplayList(clearColor, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
|
||||
|
|
|
@ -199,9 +199,9 @@ private:
|
|||
bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
|
||||
const nsTArray<RefCountedShmem>& aSmallShmems,
|
||||
const nsTArray<ipc::Shmem>& aLargeShmems,
|
||||
wr::ResourceUpdateQueue& aUpdates);
|
||||
wr::TransactionBuilder& aUpdates);
|
||||
bool AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
|
||||
wr::ResourceUpdateQueue& aResources);
|
||||
wr::TransactionBuilder& aResources);
|
||||
|
||||
uint64_t GetLayersId() const;
|
||||
void ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands);
|
||||
|
|
|
@ -143,7 +143,7 @@ WebRenderTextureHost::NumSubTextures() const
|
|||
}
|
||||
|
||||
void
|
||||
WebRenderTextureHost::PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
WebRenderTextureHost::PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID)
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
|
||||
virtual uint32_t NumSubTextures() const override;
|
||||
|
||||
virtual void PushResourceUpdates(wr::ResourceUpdateQueue& aResources,
|
||||
virtual void PushResourceUpdates(wr::TransactionBuilder& aResources,
|
||||
ResourceUpdateOp aOp,
|
||||
const Range<wr::ImageKey>& aImageKeys,
|
||||
const wr::ExternalImageId& aExtID) override;
|
||||
|
|
|
@ -649,6 +649,13 @@ gfxDWriteFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxDWriteFontEntry::GetVariationInstances(
|
||||
nsTArray<gfxFontVariationInstance>& aInstances)
|
||||
{
|
||||
gfxFontUtils::GetVariationInstances(this, aInstances);
|
||||
}
|
||||
|
||||
gfxFont *
|
||||
gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
|
||||
bool aNeedsBold)
|
||||
|
|
|
@ -191,6 +191,7 @@ public:
|
|||
|
||||
bool HasVariations() override;
|
||||
void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes) override;
|
||||
void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) override;
|
||||
|
||||
void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
|
||||
bool GetForceGDIClassic() { return mForceGDIClassic; }
|
||||
|
|
|
@ -624,12 +624,15 @@ gfxFT2FontBase::SetupVarCoords(FT_Face aFace,
|
|||
{
|
||||
aCoords->TruncateLength(0);
|
||||
if (aFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
||||
typedef FT_UInt (*GetVarFunc)(FT_Face, FT_MM_Var**);
|
||||
typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
|
||||
static GetVarFunc getVar;
|
||||
typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
|
||||
static DoneVarFunc doneVar;
|
||||
static bool firstTime = true;
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
getVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
|
||||
doneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
|
||||
}
|
||||
FT_MM_Var* ftVar;
|
||||
if (getVar && FT_Err_Ok == (*getVar)(aFace, &ftVar)) {
|
||||
|
@ -645,7 +648,11 @@ gfxFT2FontBase::SetupVarCoords(FT_Face aFace,
|
|||
}
|
||||
}
|
||||
}
|
||||
free(ftVar);
|
||||
if (doneVar) {
|
||||
(*doneVar)(aFace->glyph->library, ftVar);
|
||||
} else {
|
||||
free(ftVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -360,8 +360,37 @@ gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
|
|||
mIgnoreFcCharmap = true;
|
||||
}
|
||||
|
||||
typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
|
||||
typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
|
||||
static GetVarFunc sGetVar;
|
||||
static DoneVarFunc sDoneVar;
|
||||
static bool sInitializedVarFuncs = false;
|
||||
|
||||
static void
|
||||
InitializeVarFuncs()
|
||||
{
|
||||
if (sInitializedVarFuncs) {
|
||||
return;
|
||||
}
|
||||
sInitializedVarFuncs = true;
|
||||
sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
|
||||
sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
|
||||
}
|
||||
|
||||
gfxFontconfigFontEntry::~gfxFontconfigFontEntry()
|
||||
{
|
||||
if (mMMVar) {
|
||||
// Prior to freetype 2.9, there was no specific function to free the
|
||||
// FT_MM_Var record, and the docs just said to use free().
|
||||
// InitializeVarFuncs must have been called in order for mMMVar to be
|
||||
// non-null here, so we don't need to do it again.
|
||||
if (sDoneVar) {
|
||||
MOZ_ASSERT(mFTFace, "How did mMMVar get set without a face?");
|
||||
(*sDoneVar)(mFTFace->glyph->library, mMMVar);
|
||||
} else {
|
||||
free(mMMVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -469,6 +498,14 @@ gfxFontconfigFontEntry::MaybeReleaseFTFace()
|
|||
// only close out FT_Face for system fonts, not for data fonts
|
||||
if (!mIsDataUserFont) {
|
||||
if (mFTFace) {
|
||||
if (mMMVar) {
|
||||
if (sDoneVar) {
|
||||
(*sDoneVar)(mFTFace->glyph->library, mMMVar);
|
||||
} else {
|
||||
free(mMMVar);
|
||||
}
|
||||
mMMVar = nullptr;
|
||||
}
|
||||
Factory::ReleaseFTFace(mFTFace);
|
||||
mFTFace = nullptr;
|
||||
}
|
||||
|
@ -1017,31 +1054,36 @@ gfxFontconfigFontEntry::HasVariations()
|
|||
return false;
|
||||
}
|
||||
|
||||
FT_MM_Var*
|
||||
gfxFontconfigFontEntry::GetMMVar()
|
||||
{
|
||||
if (mMMVarInitialized) {
|
||||
return mMMVar;
|
||||
}
|
||||
mMMVarInitialized = true;
|
||||
InitializeVarFuncs();
|
||||
if (!sGetVar) {
|
||||
return nullptr;
|
||||
}
|
||||
FT_Face face = GetFTFace();
|
||||
if (!face) {
|
||||
return nullptr;
|
||||
}
|
||||
if (FT_Err_Ok != (*sGetVar)(face, &mMMVar)) {
|
||||
mMMVar = nullptr;
|
||||
}
|
||||
return mMMVar;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontconfigFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes)
|
||||
{
|
||||
MOZ_ASSERT(aAxes.IsEmpty());
|
||||
FT_Face face = GetFTFace();
|
||||
if (!face) {
|
||||
return;
|
||||
}
|
||||
typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
|
||||
static GetVarFunc getVar;
|
||||
typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
|
||||
static DoneVarFunc doneVar;
|
||||
static bool firstTime = true;
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
getVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
|
||||
doneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
|
||||
}
|
||||
if (!getVar) {
|
||||
return;
|
||||
}
|
||||
FT_MM_Var* mmVar;
|
||||
if (FT_Err_Ok != (*getVar)(face, &mmVar)) {
|
||||
FT_MM_Var* mmVar = GetMMVar();
|
||||
if (!mmVar) {
|
||||
return;
|
||||
}
|
||||
aAxes.SetCapacity(mmVar->num_axis);
|
||||
for (unsigned i = 0; i < mmVar->num_axis; i++) {
|
||||
const auto& a = mmVar->axis[i];
|
||||
gfxFontVariationAxis axis;
|
||||
|
@ -1052,13 +1094,40 @@ gfxFontconfigFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes)
|
|||
axis.mName.Assign(NS_ConvertUTF8toUTF16(a.name));
|
||||
aAxes.AppendElement(axis);
|
||||
}
|
||||
// Prior to freetype 2.9, there was no specific function to free the FT_MM_Var,
|
||||
// and the docs just said to use free().
|
||||
if (doneVar) {
|
||||
(*doneVar)(face->glyph->library, mmVar);
|
||||
} else {
|
||||
free(mmVar);
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontconfigFontEntry::GetVariationInstances(
|
||||
nsTArray<gfxFontVariationInstance>& aInstances)
|
||||
{
|
||||
MOZ_ASSERT(aInstances.IsEmpty());
|
||||
FT_MM_Var* mmVar = GetMMVar();
|
||||
if (!mmVar) {
|
||||
return;
|
||||
}
|
||||
hb_blob_t* nameTable = GetFontTable(TRUETYPE_TAG('n','a','m','e'));
|
||||
if (!nameTable) {
|
||||
return;
|
||||
}
|
||||
aInstances.SetCapacity(mmVar->num_namedstyles);
|
||||
for (unsigned i = 0; i < mmVar->num_namedstyles; i++) {
|
||||
const auto& ns = mmVar->namedstyle[i];
|
||||
gfxFontVariationInstance inst;
|
||||
nsresult rv =
|
||||
gfxFontUtils::ReadCanonicalName(nameTable, ns.strid, inst.mName);
|
||||
if (NS_FAILED(rv)) {
|
||||
continue;
|
||||
}
|
||||
inst.mValues.SetCapacity(mmVar->num_axis);
|
||||
for (unsigned j = 0; j < mmVar->num_axis; j++) {
|
||||
gfxFontVariationValue value;
|
||||
value.mAxis = mmVar->axis[j].tag;
|
||||
value.mValue = ns.coords[j] / 65536.0;
|
||||
inst.mValues.AppendElement(value);
|
||||
}
|
||||
aInstances.AppendElement(inst);
|
||||
}
|
||||
hb_blob_destroy(nameTable);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "ft2build.h"
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_TRUETYPE_TABLES_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
#include <cairo.h>
|
||||
#include <cairo-ft.h>
|
||||
|
||||
|
@ -115,8 +116,11 @@ public:
|
|||
|
||||
FT_Face GetFTFace();
|
||||
|
||||
FT_MM_Var* GetMMVar();
|
||||
|
||||
bool HasVariations() override;
|
||||
void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes) override;
|
||||
void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) override;
|
||||
|
||||
hb_blob_t* GetFontTable(uint32_t aTableTag) override;
|
||||
|
||||
|
@ -188,6 +192,11 @@ protected:
|
|||
};
|
||||
|
||||
UnscaledFontCache mUnscaledFontCache;
|
||||
|
||||
// Because of FreeType bug 52955, we keep the FT_MM_Var struct when it is
|
||||
// first loaded, rather than releasing it and re-fetching it as needed.
|
||||
FT_MM_Var* mMMVar = nullptr;
|
||||
bool mMMVarInitialized = false;
|
||||
};
|
||||
|
||||
class gfxFontconfigFontFamily : public gfxFontFamily {
|
||||
|
|
|
@ -354,6 +354,9 @@ public:
|
|||
virtual void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes)
|
||||
{
|
||||
}
|
||||
virtual void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances)
|
||||
{
|
||||
}
|
||||
|
||||
nsString mName;
|
||||
nsString mFamilyName;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "mozilla/BinarySearch.h"
|
||||
|
||||
#include "gfxFontUtils.h"
|
||||
#include "gfxFontEntry.h"
|
||||
#include "gfxFontVariations.h"
|
||||
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
|
@ -1803,6 +1805,145 @@ gfxFontUtils::GetColorGlyphLayers(hb_blob_t* aCOLR,
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFontUtils::GetVariationInstances(gfxFontEntry* aFontEntry,
|
||||
nsTArray<gfxFontVariationInstance>& aInstances)
|
||||
{
|
||||
MOZ_ASSERT(aInstances.IsEmpty());
|
||||
|
||||
if (!aFontEntry->HasVariations()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Some platforms don't offer a simple API to return the list of instances,
|
||||
// so we have to interpret the 'fvar' table ourselves.
|
||||
|
||||
// https://www.microsoft.com/typography/otspec/fvar.htm#fvarHeader
|
||||
struct FvarHeader {
|
||||
AutoSwap_PRUint16 majorVersion;
|
||||
AutoSwap_PRUint16 minorVersion;
|
||||
AutoSwap_PRUint16 axesArrayOffset;
|
||||
AutoSwap_PRUint16 reserved;
|
||||
AutoSwap_PRUint16 axisCount;
|
||||
AutoSwap_PRUint16 axisSize;
|
||||
AutoSwap_PRUint16 instanceCount;
|
||||
AutoSwap_PRUint16 instanceSize;
|
||||
};
|
||||
|
||||
// https://www.microsoft.com/typography/otspec/fvar.htm#variationAxisRecord
|
||||
struct AxisRecord {
|
||||
AutoSwap_PRUint32 axisTag;
|
||||
AutoSwap_PRInt32 minValue;
|
||||
AutoSwap_PRInt32 defaultValue;
|
||||
AutoSwap_PRInt32 maxValue;
|
||||
AutoSwap_PRUint16 flags;
|
||||
AutoSwap_PRUint16 axisNameID;
|
||||
};
|
||||
|
||||
// https://www.microsoft.com/typography/otspec/fvar.htm#instanceRecord
|
||||
struct InstanceRecord {
|
||||
AutoSwap_PRUint16 subfamilyNameID;
|
||||
AutoSwap_PRUint16 flags;
|
||||
AutoSwap_PRInt32 coordinates[1]; // variable-size array [axisCount]
|
||||
// The variable-length 'coordinates' array may be followed by an
|
||||
// optional extra field 'postScriptNameID'. We can't directly
|
||||
// represent this in the struct, because its offset varies depending
|
||||
// on the number of axes present.
|
||||
// (Not currently used by our code here anyhow.)
|
||||
// AutoSwap_PRUint16 postScriptNameID;
|
||||
};
|
||||
|
||||
// Helper to ensure we free a font table when we return.
|
||||
class AutoHBBlob {
|
||||
public:
|
||||
explicit AutoHBBlob(hb_blob_t* aBlob) : mBlob(aBlob)
|
||||
{ }
|
||||
|
||||
~AutoHBBlob() {
|
||||
if (mBlob) {
|
||||
hb_blob_destroy(mBlob);
|
||||
}
|
||||
}
|
||||
|
||||
operator hb_blob_t* () { return mBlob; }
|
||||
|
||||
private:
|
||||
hb_blob_t* const mBlob;
|
||||
};
|
||||
|
||||
// Load the two font tables we need as harfbuzz blobs; if either is absent,
|
||||
// just bail out.
|
||||
AutoHBBlob fvarTable(aFontEntry->GetFontTable(TRUETYPE_TAG('f','v','a','r')));
|
||||
AutoHBBlob nameTable(aFontEntry->GetFontTable(TRUETYPE_TAG('n','a','m','e')));
|
||||
if (!fvarTable || !nameTable) {
|
||||
return;
|
||||
}
|
||||
unsigned int len;
|
||||
const char* data = hb_blob_get_data(fvarTable, &len);
|
||||
if (len < sizeof(FvarHeader)) {
|
||||
return;
|
||||
}
|
||||
// Read the fields of the table header; bail out if it looks broken.
|
||||
auto fvar = reinterpret_cast<const FvarHeader*>(data);
|
||||
if (uint16_t(fvar->majorVersion) != 1 ||
|
||||
uint16_t(fvar->minorVersion) != 0 ||
|
||||
uint16_t(fvar->reserved) != 2) {
|
||||
return;
|
||||
}
|
||||
uint16_t axisCount = fvar->axisCount;
|
||||
uint16_t axisSize = fvar->axisSize;
|
||||
uint16_t instanceCount = fvar->instanceCount;
|
||||
uint16_t instanceSize = fvar->instanceSize;
|
||||
if (axisCount == 0 || // no axes?
|
||||
// https://www.microsoft.com/typography/otspec/fvar.htm#axisSize
|
||||
axisSize != 20 || // required value for current table version
|
||||
instanceCount == 0 || // no instances?
|
||||
// https://www.microsoft.com/typography/otspec/fvar.htm#instanceSize
|
||||
(instanceSize != axisCount * sizeof(int32_t) + 4 &&
|
||||
instanceSize != axisCount * sizeof(int32_t) + 6)) {
|
||||
return;
|
||||
}
|
||||
// Check that axis array will not exceed table size
|
||||
uint16_t axesOffset = fvar->axesArrayOffset;
|
||||
if (axesOffset + uint32_t(axisCount) * axisSize > len) {
|
||||
return;
|
||||
}
|
||||
// Get pointer to the array of axis records
|
||||
auto axes = reinterpret_cast<const AxisRecord*>(data + axesOffset);
|
||||
// Get address of instance array, and check it doesn't overflow table size.
|
||||
// https://www.microsoft.com/typography/otspec/fvar.htm#axisAndInstanceArrays
|
||||
auto instData = data + axesOffset + axisCount * axisSize;
|
||||
if (instData + uint32_t(instanceCount) * instanceSize > data + len) {
|
||||
return;
|
||||
}
|
||||
aInstances.SetCapacity(instanceCount);
|
||||
for (unsigned i = 0; i < instanceCount; ++i, instData += instanceSize) {
|
||||
// Typed pointer to the current instance record, to read its fields.
|
||||
auto inst = reinterpret_cast<const InstanceRecord*>(instData);
|
||||
// Pointer to the coordinates array within the instance record.
|
||||
// This array has axisCount elements, and is included in instanceSize
|
||||
// (which depends on axisCount, and was validated above) so we know
|
||||
// access to coords[j] below will not be outside the table bounds.
|
||||
auto coords = &inst->coordinates[0];
|
||||
gfxFontVariationInstance instance;
|
||||
uint16_t nameID = inst->subfamilyNameID;
|
||||
nsresult rv =
|
||||
ReadCanonicalName(nameTable, nameID, instance.mName);
|
||||
if (NS_FAILED(rv)) {
|
||||
// If no name was available for the instance, ignore it.
|
||||
continue;
|
||||
}
|
||||
instance.mValues.SetCapacity(axisCount);
|
||||
for (unsigned j = 0; j < axisCount; ++j) {
|
||||
gfxFontVariationValue value;
|
||||
value.mAxis = axes[j].axisTag;
|
||||
value.mValue = int32_t(coords[j]) / 65536.0;
|
||||
instance.mValues.AppendElement(value);
|
||||
}
|
||||
aInstances.AppendElement(instance);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
|
||||
typedef struct hb_blob_t hb_blob_t;
|
||||
|
||||
struct gfxFontVariationInstance;
|
||||
|
||||
class gfxSparseBitSet {
|
||||
private:
|
||||
enum { BLOCK_SIZE = 32 }; // ==> 256 codepoints per block
|
||||
|
@ -1000,6 +1002,15 @@ public:
|
|||
nsTArray<uint16_t> &aGlyphs,
|
||||
nsTArray<mozilla::gfx::Color> &aColors);
|
||||
|
||||
// Helper used to implement gfxFontEntry::GetVariationInstances for
|
||||
// platforms where the native font APIs don't provide the info we want
|
||||
// in a convenient form.
|
||||
// (Not used on platforms -- currently, freetype -- where the font APIs
|
||||
// expose variation instance details directly.)
|
||||
static void
|
||||
GetVariationInstances(gfxFontEntry* aFontEntry,
|
||||
nsTArray<gfxFontVariationInstance>& aInstances);
|
||||
|
||||
protected:
|
||||
friend struct MacCharsetMappingComparator;
|
||||
|
||||
|
|
|
@ -11,12 +11,29 @@
|
|||
|
||||
typedef mozilla::gfx::FontVariation gfxFontVariation;
|
||||
|
||||
// Structure that describes a single axis of variation in an
|
||||
// OpenType Variation or Multiple-Master font.
|
||||
struct gfxFontVariationAxis {
|
||||
uint32_t mTag;
|
||||
nsString mName;
|
||||
nsString mName; // may be empty
|
||||
float mMinValue;
|
||||
float mMaxValue;
|
||||
float mDefaultValue;
|
||||
};
|
||||
|
||||
// A single <axis, value> pair that may be applied to a variation font.
|
||||
struct gfxFontVariationValue {
|
||||
uint32_t mAxis;
|
||||
float mValue;
|
||||
};
|
||||
|
||||
// Structure that describes a named instance of a variation font:
|
||||
// a name like "Light Condensed" or "Black Ultra Extended" etc.,
|
||||
// and a list of the corresponding <variation-axis, value> pairs
|
||||
// to be used.
|
||||
struct gfxFontVariationInstance {
|
||||
nsString mName;
|
||||
nsTArray<gfxFontVariationValue> mValues;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
|
||||
bool HasVariations() override;
|
||||
void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes) override;
|
||||
void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) override;
|
||||
|
||||
bool IsCFF();
|
||||
|
||||
|
|
|
@ -342,6 +342,12 @@ MacOSFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacOSFontEntry::GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances)
|
||||
{
|
||||
gfxFontUtils::GetVariationInstances(this, aInstances);
|
||||
}
|
||||
|
||||
bool
|
||||
MacOSFontEntry::IsCFF()
|
||||
{
|
||||
|
|
|
@ -135,11 +135,13 @@ private:
|
|||
TransactionBuilder::TransactionBuilder()
|
||||
{
|
||||
mTxn = wr_transaction_new();
|
||||
mResourceUpdates = wr_resource_updates_new();
|
||||
}
|
||||
|
||||
TransactionBuilder::~TransactionBuilder()
|
||||
{
|
||||
wr_transaction_delete(mTxn);
|
||||
wr_resource_updates_delete(mResourceUpdates);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -211,15 +213,18 @@ TransactionBuilder::IsEmpty() const
|
|||
}
|
||||
|
||||
void
|
||||
TransactionBuilder::SetWindowParameters(LayoutDeviceIntSize size)
|
||||
TransactionBuilder::SetWindowParameters(const LayoutDeviceIntSize& aWindowSize,
|
||||
const LayoutDeviceIntRect& aDocumentRect)
|
||||
{
|
||||
wr_transaction_set_window_parameters(mTxn, size.width, size.height);
|
||||
}
|
||||
|
||||
void
|
||||
TransactionBuilder::UpdateResources(ResourceUpdateQueue& aUpdates)
|
||||
{
|
||||
wr_transaction_update_resources(mTxn, aUpdates.Raw());
|
||||
wr::DeviceUintSize wrWindowSize;
|
||||
wrWindowSize.width = aWindowSize.width;
|
||||
wrWindowSize.height = aWindowSize.height;
|
||||
wr::DeviceUintRect wrDocRect;
|
||||
wrDocRect.origin.x = aDocumentRect.x;
|
||||
wrDocRect.origin.y = aDocumentRect.y;
|
||||
wrDocRect.size.width = aDocumentRect.width;
|
||||
wrDocRect.size.height = aDocumentRect.height;
|
||||
wr_transaction_set_window_parameters(mTxn, &wrWindowSize, &wrDocRect);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -287,9 +292,28 @@ WebRenderAPI::Clone()
|
|||
|
||||
RefPtr<WebRenderAPI> renderApi = new WebRenderAPI(docHandle, mId, mMaxTextureSize, mUseANGLE, mSyncHandle);
|
||||
renderApi->mRootApi = this; // Hold root api
|
||||
renderApi->mRootDocumentApi = this;
|
||||
return renderApi.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<WebRenderAPI>
|
||||
WebRenderAPI::CreateDocument(LayoutDeviceIntSize aSize, int8_t aLayerIndex)
|
||||
{
|
||||
wr::DeviceUintSize wrSize;
|
||||
wrSize.width = aSize.width;
|
||||
wrSize.height = aSize.height;
|
||||
wr::DocumentHandle* newDoc;
|
||||
|
||||
wr_api_create_document(mDocHandle, &newDoc, wrSize, aLayerIndex);
|
||||
|
||||
RefPtr<WebRenderAPI> api(new WebRenderAPI(newDoc, mId,
|
||||
mMaxTextureSize,
|
||||
mUseANGLE,
|
||||
mSyncHandle));
|
||||
api->mRootApi = this;
|
||||
return api.forget();
|
||||
}
|
||||
|
||||
wr::WrIdNamespace
|
||||
WebRenderAPI::GetNamespace() {
|
||||
return wr_api_get_namespace(mDocHandle);
|
||||
|
@ -299,14 +323,19 @@ extern void ClearBlobImageResources(WrIdNamespace aNamespace);
|
|||
|
||||
WebRenderAPI::~WebRenderAPI()
|
||||
{
|
||||
if (!mRootApi) {
|
||||
if (!mRootDocumentApi) {
|
||||
wr_api_delete_document(mDocHandle);
|
||||
}
|
||||
|
||||
if (!mRootApi) {
|
||||
RenderThread::Get()->SetDestroyed(GetId());
|
||||
|
||||
layers::SynchronousTask task("Destroy WebRenderAPI");
|
||||
auto event = MakeUnique<RemoveRenderer>(&task);
|
||||
RunOnRenderThread(Move(event));
|
||||
task.Wait();
|
||||
|
||||
wr_api_shut_down(mDocHandle);
|
||||
}
|
||||
|
||||
// wr_api_get_namespace cannot be marked destructor-safe because it has a
|
||||
|
@ -330,6 +359,7 @@ WebRenderAPI::~WebRenderAPI()
|
|||
void
|
||||
WebRenderAPI::SendTransaction(TransactionBuilder& aTxn)
|
||||
{
|
||||
wr_transaction_update_resources(aTxn.Raw(), aTxn.RawUpdates());
|
||||
wr_api_send_transaction(mDocHandle, aTxn.Raw());
|
||||
}
|
||||
|
||||
|
@ -513,66 +543,40 @@ WebRenderAPI::Capture()
|
|||
}
|
||||
|
||||
|
||||
ResourceUpdateQueue::ResourceUpdateQueue()
|
||||
void
|
||||
TransactionBuilder::Clear()
|
||||
{
|
||||
mUpdates = wr_resource_updates_new();
|
||||
}
|
||||
|
||||
ResourceUpdateQueue::ResourceUpdateQueue(ResourceUpdateQueue&& aFrom)
|
||||
{
|
||||
mUpdates = aFrom.mUpdates;
|
||||
aFrom.mUpdates = nullptr;
|
||||
}
|
||||
|
||||
ResourceUpdateQueue&
|
||||
ResourceUpdateQueue::operator=(ResourceUpdateQueue&& aFrom)
|
||||
{
|
||||
mUpdates = aFrom.mUpdates;
|
||||
aFrom.mUpdates = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ResourceUpdateQueue::~ResourceUpdateQueue()
|
||||
{
|
||||
if (mUpdates) {
|
||||
wr_resource_updates_delete(mUpdates);
|
||||
}
|
||||
wr_resource_updates_clear(mResourceUpdates);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::Clear()
|
||||
TransactionBuilder::AddImage(ImageKey key, const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes)
|
||||
{
|
||||
wr_resource_updates_clear(mUpdates);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddImage(ImageKey key, const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes)
|
||||
{
|
||||
wr_resource_updates_add_image(mUpdates,
|
||||
wr_resource_updates_add_image(mResourceUpdates,
|
||||
key,
|
||||
&aDescriptor,
|
||||
&aBytes.inner);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddBlobImage(ImageKey key, const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes)
|
||||
TransactionBuilder::AddBlobImage(ImageKey key, const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes)
|
||||
{
|
||||
wr_resource_updates_add_blob_image(mUpdates,
|
||||
wr_resource_updates_add_blob_image(mResourceUpdates,
|
||||
key,
|
||||
&aDescriptor,
|
||||
&aBytes.inner);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddExternalImage(ImageKey key,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
ExternalImageId aExtID,
|
||||
wr::WrExternalImageBufferType aBufferType,
|
||||
uint8_t aChannelIndex)
|
||||
TransactionBuilder::AddExternalImage(ImageKey key,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
ExternalImageId aExtID,
|
||||
wr::WrExternalImageBufferType aBufferType,
|
||||
uint8_t aChannelIndex)
|
||||
{
|
||||
wr_resource_updates_add_external_image(mUpdates,
|
||||
wr_resource_updates_add_external_image(mResourceUpdates,
|
||||
key,
|
||||
&aDescriptor,
|
||||
aExtID,
|
||||
|
@ -581,9 +585,9 @@ ResourceUpdateQueue::AddExternalImage(ImageKey key,
|
|||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddExternalImageBuffer(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
ExternalImageId aHandle)
|
||||
TransactionBuilder::AddExternalImageBuffer(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
ExternalImageId aHandle)
|
||||
{
|
||||
auto channelIndex = 0;
|
||||
AddExternalImage(aKey, aDescriptor, aHandle,
|
||||
|
@ -592,23 +596,23 @@ ResourceUpdateQueue::AddExternalImageBuffer(ImageKey aKey,
|
|||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::UpdateImageBuffer(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes)
|
||||
TransactionBuilder::UpdateImageBuffer(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes)
|
||||
{
|
||||
wr_resource_updates_update_image(mUpdates,
|
||||
wr_resource_updates_update_image(mResourceUpdates,
|
||||
aKey,
|
||||
&aDescriptor,
|
||||
&aBytes.inner);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::UpdateBlobImage(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes,
|
||||
const wr::DeviceUintRect& aDirtyRect)
|
||||
TransactionBuilder::UpdateBlobImage(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
wr::Vec<uint8_t>& aBytes,
|
||||
const wr::DeviceUintRect& aDirtyRect)
|
||||
{
|
||||
wr_resource_updates_update_blob_image(mUpdates,
|
||||
wr_resource_updates_update_blob_image(mResourceUpdates,
|
||||
aKey,
|
||||
&aDescriptor,
|
||||
&aBytes.inner,
|
||||
|
@ -616,13 +620,13 @@ ResourceUpdateQueue::UpdateBlobImage(ImageKey aKey,
|
|||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::UpdateExternalImage(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
ExternalImageId aExtID,
|
||||
wr::WrExternalImageBufferType aBufferType,
|
||||
uint8_t aChannelIndex)
|
||||
TransactionBuilder::UpdateExternalImage(ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
ExternalImageId aExtID,
|
||||
wr::WrExternalImageBufferType aBufferType,
|
||||
uint8_t aChannelIndex)
|
||||
{
|
||||
wr_resource_updates_update_external_image(mUpdates,
|
||||
wr_resource_updates_update_external_image(mResourceUpdates,
|
||||
aKey,
|
||||
&aDescriptor,
|
||||
aExtID,
|
||||
|
@ -631,46 +635,46 @@ ResourceUpdateQueue::UpdateExternalImage(ImageKey aKey,
|
|||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::DeleteImage(ImageKey aKey)
|
||||
TransactionBuilder::DeleteImage(ImageKey aKey)
|
||||
{
|
||||
wr_resource_updates_delete_image(mUpdates, aKey);
|
||||
wr_resource_updates_delete_image(mResourceUpdates, aKey);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddRawFont(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex)
|
||||
TransactionBuilder::AddRawFont(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex)
|
||||
{
|
||||
wr_resource_updates_add_raw_font(mUpdates, aKey, &aBytes.inner, aIndex);
|
||||
wr_resource_updates_add_raw_font(mResourceUpdates, aKey, &aBytes.inner, aIndex);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddFontDescriptor(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex)
|
||||
TransactionBuilder::AddFontDescriptor(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex)
|
||||
{
|
||||
wr_resource_updates_add_font_descriptor(mUpdates, aKey, &aBytes.inner, aIndex);
|
||||
wr_resource_updates_add_font_descriptor(mResourceUpdates, aKey, &aBytes.inner, aIndex);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::DeleteFont(wr::FontKey aKey)
|
||||
TransactionBuilder::DeleteFont(wr::FontKey aKey)
|
||||
{
|
||||
wr_resource_updates_delete_font(mUpdates, aKey);
|
||||
wr_resource_updates_delete_font(mResourceUpdates, aKey);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::AddFontInstance(wr::FontInstanceKey aKey,
|
||||
wr::FontKey aFontKey,
|
||||
float aGlyphSize,
|
||||
const wr::FontInstanceOptions* aOptions,
|
||||
const wr::FontInstancePlatformOptions* aPlatformOptions,
|
||||
wr::Vec<uint8_t>& aVariations)
|
||||
TransactionBuilder::AddFontInstance(wr::FontInstanceKey aKey,
|
||||
wr::FontKey aFontKey,
|
||||
float aGlyphSize,
|
||||
const wr::FontInstanceOptions* aOptions,
|
||||
const wr::FontInstancePlatformOptions* aPlatformOptions,
|
||||
wr::Vec<uint8_t>& aVariations)
|
||||
{
|
||||
wr_resource_updates_add_font_instance(mUpdates, aKey, aFontKey, aGlyphSize,
|
||||
wr_resource_updates_add_font_instance(mResourceUpdates, aKey, aFontKey, aGlyphSize,
|
||||
aOptions, aPlatformOptions,
|
||||
&aVariations.inner);
|
||||
}
|
||||
|
||||
void
|
||||
ResourceUpdateQueue::DeleteFontInstance(wr::FontInstanceKey aKey)
|
||||
TransactionBuilder::DeleteFontInstance(wr::FontInstanceKey aKey)
|
||||
{
|
||||
wr_resource_updates_delete_font_instance(mUpdates, aKey);
|
||||
wr_resource_updates_delete_font_instance(mResourceUpdates, aKey);
|
||||
}
|
||||
|
||||
class FrameStartTime : public RendererEvent
|
||||
|
|
|
@ -50,16 +50,42 @@ struct Line {
|
|||
wr::LineStyle style;
|
||||
};
|
||||
|
||||
/// Updates to retained resources such as images and fonts, applied within the
|
||||
/// same transaction.
|
||||
class ResourceUpdateQueue {
|
||||
|
||||
class TransactionBuilder {
|
||||
public:
|
||||
ResourceUpdateQueue();
|
||||
~ResourceUpdateQueue();
|
||||
ResourceUpdateQueue(ResourceUpdateQueue&&);
|
||||
ResourceUpdateQueue(const ResourceUpdateQueue&) = delete;
|
||||
ResourceUpdateQueue& operator=(ResourceUpdateQueue&&);
|
||||
ResourceUpdateQueue& operator=(const ResourceUpdateQueue&) = delete;
|
||||
TransactionBuilder();
|
||||
|
||||
~TransactionBuilder();
|
||||
|
||||
void UpdateEpoch(PipelineId aPipelineId, Epoch aEpoch);
|
||||
|
||||
void SetRootPipeline(PipelineId aPipelineId);
|
||||
|
||||
void RemovePipeline(PipelineId aPipelineId);
|
||||
|
||||
void SetDisplayList(gfx::Color aBgColor,
|
||||
Epoch aEpoch,
|
||||
mozilla::LayerSize aViewportSize,
|
||||
wr::WrPipelineId pipeline_id,
|
||||
const wr::LayoutSize& content_size,
|
||||
wr::BuiltDisplayListDescriptor dl_descriptor,
|
||||
wr::Vec<uint8_t>& dl_data);
|
||||
|
||||
void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);
|
||||
|
||||
void GenerateFrame();
|
||||
|
||||
void UpdateDynamicProperties(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
|
||||
const nsTArray<wr::WrTransformProperty>& aTransformArray);
|
||||
|
||||
void SetWindowParameters(const LayoutDeviceIntSize& aWindowSize,
|
||||
const LayoutDeviceIntRect& aDocRect);
|
||||
|
||||
void UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
|
||||
const layers::FrameMetrics::ViewID& aScrollId,
|
||||
const wr::LayoutPoint& aScrollPosition);
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
||||
void AddImage(wr::ImageKey aKey,
|
||||
const ImageDescriptor& aDescriptor,
|
||||
|
@ -113,56 +139,11 @@ public:
|
|||
|
||||
void Clear();
|
||||
|
||||
// Try to avoid using this when possible.
|
||||
wr::ResourceUpdates* Raw() { return mUpdates; }
|
||||
|
||||
protected:
|
||||
explicit ResourceUpdateQueue(wr::ResourceUpdates* aUpdates)
|
||||
: mUpdates(aUpdates) {}
|
||||
|
||||
wr::ResourceUpdates* mUpdates;
|
||||
};
|
||||
|
||||
class TransactionBuilder {
|
||||
public:
|
||||
TransactionBuilder();
|
||||
|
||||
~TransactionBuilder();
|
||||
|
||||
void UpdateEpoch(PipelineId aPipelineId, Epoch aEpoch);
|
||||
|
||||
void SetRootPipeline(PipelineId aPipelineId);
|
||||
|
||||
void RemovePipeline(PipelineId aPipelineId);
|
||||
|
||||
void SetDisplayList(gfx::Color aBgColor,
|
||||
Epoch aEpoch,
|
||||
mozilla::LayerSize aViewportSize,
|
||||
wr::WrPipelineId pipeline_id,
|
||||
const wr::LayoutSize& content_size,
|
||||
wr::BuiltDisplayListDescriptor dl_descriptor,
|
||||
wr::Vec<uint8_t>& dl_data);
|
||||
|
||||
void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);
|
||||
|
||||
void GenerateFrame();
|
||||
|
||||
void UpdateDynamicProperties(const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
|
||||
const nsTArray<wr::WrTransformProperty>& aTransformArray);
|
||||
|
||||
void SetWindowParameters(LayoutDeviceIntSize size);
|
||||
|
||||
void UpdateResources(ResourceUpdateQueue& aUpdates);
|
||||
|
||||
void UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
|
||||
const layers::FrameMetrics::ViewID& aScrollId,
|
||||
const wr::LayoutPoint& aScrollPosition);
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
||||
Transaction* Raw() { return mTxn; }
|
||||
wr::ResourceUpdates* RawUpdates() { return mResourceUpdates; }
|
||||
protected:
|
||||
Transaction* mTxn;
|
||||
wr::ResourceUpdates* mResourceUpdates;
|
||||
};
|
||||
|
||||
class WebRenderAPI
|
||||
|
@ -175,6 +156,8 @@ public:
|
|||
RefPtr<widget::CompositorWidget>&& aWidget,
|
||||
LayoutDeviceIntSize aSize);
|
||||
|
||||
already_AddRefed<WebRenderAPI> CreateDocument(LayoutDeviceIntSize aSize, int8_t aLayerIndex);
|
||||
|
||||
// Redirect the WR's log to gfxCriticalError/Note.
|
||||
static void InitExternalLogHandler();
|
||||
static void ShutdownExternalLogHandler();
|
||||
|
@ -224,7 +207,17 @@ protected:
|
|||
uint32_t mMaxTextureSize;
|
||||
bool mUseANGLE;
|
||||
layers::SyncHandle mSyncHandle;
|
||||
|
||||
// We maintain alive the root api to know when to shut the render backend down,
|
||||
// and the root api for the document to know when to delete the document.
|
||||
// mRootApi is null for the api object that owns the channel (and is responsible
|
||||
// for shutting it down), and mRootDocumentApi is null for the api object owning
|
||||
// (and responsible for destroying) a given document.
|
||||
// All api objects in the same window use the same channel, and some api objects
|
||||
// write to the same document (but there is only one owner for each channel and
|
||||
// for each document).
|
||||
RefPtr<wr::WebRenderAPI> mRootApi;
|
||||
RefPtr<wr::WebRenderAPI> mRootDocumentApi;
|
||||
|
||||
friend class DisplayListBuilder;
|
||||
friend class layers::WebRenderBridgeParent;
|
||||
|
|
|
@ -100,8 +100,7 @@ pub struct DocumentHandle {
|
|||
}
|
||||
|
||||
impl DocumentHandle {
|
||||
pub fn new(api: RenderApi, size: DeviceUintSize) -> DocumentHandle {
|
||||
let layer = 0; //TODO
|
||||
pub fn new(api: RenderApi, size: DeviceUintSize, layer: i8) -> DocumentHandle {
|
||||
let doc = api.add_document(size, layer);
|
||||
DocumentHandle {
|
||||
api: api,
|
||||
|
@ -782,16 +781,41 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
|
|||
*out_max_texture_size = renderer.get_max_texture_size();
|
||||
}
|
||||
let window_size = DeviceUintSize::new(window_width, window_height);
|
||||
let layer = 0;
|
||||
*out_handle = Box::into_raw(Box::new(
|
||||
DocumentHandle::new(sender.create_api(), window_size)));
|
||||
DocumentHandle::new(sender.create_api(), window_size, layer)));
|
||||
*out_renderer = Box::into_raw(Box::new(renderer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_api_clone(dh: &mut DocumentHandle,
|
||||
out_handle: &mut *mut DocumentHandle) {
|
||||
pub extern "C" fn wr_api_create_document(
|
||||
root_dh: &mut DocumentHandle,
|
||||
out_handle: &mut *mut DocumentHandle,
|
||||
doc_size: DeviceUintSize,
|
||||
layer: i8,
|
||||
) {
|
||||
assert!(unsafe { is_in_compositor_thread() });
|
||||
|
||||
*out_handle = Box::into_raw(Box::new(DocumentHandle::new(
|
||||
root_dh.api.clone_sender().create_api(),
|
||||
doc_size,
|
||||
layer
|
||||
)));
|
||||
}
|
||||
|
||||
/// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wr_api_delete_document(dh: &mut DocumentHandle) {
|
||||
dh.api.delete_document(dh.document_id);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_api_clone(
|
||||
dh: &mut DocumentHandle,
|
||||
out_handle: &mut *mut DocumentHandle
|
||||
) {
|
||||
assert!(unsafe { is_in_compositor_thread() });
|
||||
|
||||
let handle = DocumentHandle {
|
||||
|
@ -804,11 +828,13 @@ pub extern "C" fn wr_api_clone(dh: &mut DocumentHandle,
|
|||
/// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wr_api_delete(dh: *mut DocumentHandle) {
|
||||
let handle = Box::from_raw(dh);
|
||||
if handle.document_id.0 == handle.api.get_namespace_id() {
|
||||
handle.api.delete_document(handle.document_id);
|
||||
handle.api.shut_down();
|
||||
}
|
||||
let _ = Box::from_raw(dh);
|
||||
}
|
||||
|
||||
/// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn wr_api_shut_down(dh: &mut DocumentHandle) {
|
||||
dh.api.shut_down();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -888,19 +914,21 @@ pub extern "C" fn wr_transaction_update_resources(
|
|||
txn: &mut Transaction,
|
||||
resource_updates: &mut ResourceUpdates
|
||||
) {
|
||||
if resource_updates.updates.is_empty() {
|
||||
return;
|
||||
}
|
||||
txn.update_resources(mem::replace(resource_updates, ResourceUpdates::new()));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_transaction_set_window_parameters(
|
||||
txn: &mut Transaction,
|
||||
window_width: i32,
|
||||
window_height: i32,
|
||||
window_size: &DeviceUintSize,
|
||||
doc_rect: &DeviceUintRect,
|
||||
) {
|
||||
let size = DeviceUintSize::new(window_width as u32, window_height as u32);
|
||||
txn.set_window_parameters(
|
||||
size,
|
||||
DeviceUintRect::new(DeviceUintPoint::new(0, 0), size),
|
||||
*window_size,
|
||||
*doc_rect,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -325,6 +325,8 @@ struct TypedSize2D {
|
|||
}
|
||||
};
|
||||
|
||||
using DeviceUintSize = TypedSize2D<uint32_t, DevicePixel>;
|
||||
|
||||
using LayerSize = TypedSize2D<float, LayerPixel>;
|
||||
|
||||
using LayoutSize = LayerSize;
|
||||
|
@ -986,10 +988,21 @@ void wr_api_clone(DocumentHandle *aDh,
|
|||
DocumentHandle **aOutHandle)
|
||||
WR_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
void wr_api_create_document(DocumentHandle *aRootDh,
|
||||
DocumentHandle **aOutHandle,
|
||||
DeviceUintSize aDocSize,
|
||||
int8_t aLayer)
|
||||
WR_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
void wr_api_delete(DocumentHandle *aDh)
|
||||
WR_DESTRUCTOR_SAFE_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
void wr_api_delete_document(DocumentHandle *aDh)
|
||||
WR_DESTRUCTOR_SAFE_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
void wr_api_finalize_builder(WrState *aState,
|
||||
LayoutSize *aContentSize,
|
||||
|
@ -1019,6 +1032,10 @@ void wr_api_send_transaction(DocumentHandle *aDh,
|
|||
Transaction *aTransaction)
|
||||
WR_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
void wr_api_shut_down(DocumentHandle *aDh)
|
||||
WR_DESTRUCTOR_SAFE_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
void wr_clear_item_tag(WrState *aState)
|
||||
WR_FUNC;
|
||||
|
@ -1587,8 +1604,8 @@ WR_FUNC;
|
|||
|
||||
WR_INLINE
|
||||
void wr_transaction_set_window_parameters(Transaction *aTxn,
|
||||
int32_t aWindowWidth,
|
||||
int32_t aWindowHeight)
|
||||
const DeviceUintSize *aWindowSize,
|
||||
const DeviceUintRect *aDocRect)
|
||||
WR_FUNC;
|
||||
|
||||
WR_INLINE
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
|
||||
#include "threading/ConditionVariable.h"
|
||||
#include "vm/MutexIDs.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class AtomicsObject : public JSObject
|
||||
class AtomicsObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
|
|
@ -4963,6 +4963,24 @@ IsLegacyIterator(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EnableExpressionClosures(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS::ContextOptionsRef(cx).setExpressionClosures(true);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DisableExpressionClosures(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS::ContextOptionsRef(cx).setExpressionClosures(false);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'zone' [, 'shrinking'])",
|
||||
|
@ -5579,6 +5597,14 @@ gc::ZealModeHelpText),
|
|||
"getTimeZone()",
|
||||
" Get the current time zone.\n"),
|
||||
|
||||
JS_FN_HELP("enableExpressionClosures", EnableExpressionClosures, 0, 0,
|
||||
"enableExpressionClosures()",
|
||||
" Enables the deprecated, non-standard expression closures.\n"),
|
||||
|
||||
JS_FN_HELP("disableExpressionClosures", DisableExpressionClosures, 0, 0,
|
||||
"disableExpressionClosures()",
|
||||
" Disables the deprecated, non-standard expression closures.\n"),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
|
|
@ -1449,6 +1449,8 @@ OutlineTypedObject::createUnattachedWithClass(JSContext* cx,
|
|||
MOZ_ASSERT(clasp == &OutlineTransparentTypedObject::class_ ||
|
||||
clasp == &OutlineOpaqueTypedObject::class_);
|
||||
|
||||
AutoSetNewObjectMetadata metadata(cx);
|
||||
|
||||
RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp,
|
||||
TaggedProto(&descr->typedProto()),
|
||||
descr));
|
||||
|
@ -2225,7 +2227,7 @@ const ObjectOps TypedObject::objectOps_ = {
|
|||
nullptr, /* thisValue */
|
||||
};
|
||||
|
||||
#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved, flag) \
|
||||
#define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved) \
|
||||
static const ClassOps Name##ClassOps = { \
|
||||
nullptr, /* addProperty */ \
|
||||
nullptr, /* delProperty */ \
|
||||
|
@ -2245,7 +2247,8 @@ const ObjectOps TypedObject::objectOps_ = {
|
|||
}; \
|
||||
const Class Name::class_ = { \
|
||||
# Name, \
|
||||
Class::NON_NATIVE | flag, \
|
||||
Class::NON_NATIVE | \
|
||||
JSCLASS_DELAY_METADATA_BUILDER, \
|
||||
&Name##ClassOps, \
|
||||
JS_NULL_CLASS_SPEC, \
|
||||
&Name##ClassExt, \
|
||||
|
@ -2254,20 +2257,16 @@ const ObjectOps TypedObject::objectOps_ = {
|
|||
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineTransparentTypedObject,
|
||||
OutlineTypedObject::obj_trace,
|
||||
nullptr,
|
||||
0);
|
||||
nullptr);
|
||||
DEFINE_TYPEDOBJ_CLASS(OutlineOpaqueTypedObject,
|
||||
OutlineTypedObject::obj_trace,
|
||||
nullptr,
|
||||
0);
|
||||
nullptr);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineTransparentTypedObject,
|
||||
InlineTypedObject::obj_trace,
|
||||
InlineTypedObject::obj_moved,
|
||||
JSCLASS_DELAY_METADATA_BUILDER);
|
||||
InlineTypedObject::obj_moved);
|
||||
DEFINE_TYPEDOBJ_CLASS(InlineOpaqueTypedObject,
|
||||
InlineTypedObject::obj_trace,
|
||||
InlineTypedObject::obj_moved,
|
||||
JSCLASS_DELAY_METADATA_BUILDER);
|
||||
InlineTypedObject::obj_moved);
|
||||
|
||||
static int32_t
|
||||
LengthForType(TypeDescr& descr)
|
||||
|
@ -2410,15 +2409,11 @@ TypedObject::create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap h
|
|||
return cx->alreadyReportedOOM();
|
||||
|
||||
TypedObject* tobj = static_cast<TypedObject*>(obj);
|
||||
tobj->group_.init(group);
|
||||
tobj->initGroup(group);
|
||||
tobj->initShape(shape);
|
||||
|
||||
tobj->setInitialElementsMaybeNonNative(js::emptyObjectElements);
|
||||
|
||||
if (clasp->shouldDelayMetadataBuilder())
|
||||
cx->compartment()->setObjectPendingMetadata(cx, tobj);
|
||||
else
|
||||
tobj = SetNewObjectMetadata(cx, tobj);
|
||||
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
|
||||
cx->compartment()->setObjectPendingMetadata(cx, tobj);
|
||||
|
||||
js::gc::TraceCreateObject(tobj);
|
||||
|
||||
|
|
|
@ -752,7 +752,7 @@ class InlineOpaqueTypedObject : public InlineTypedObject
|
|||
};
|
||||
|
||||
// Class for the global SIMD object.
|
||||
class SimdObject : public JSObject
|
||||
class SimdObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
|
|
@ -3260,15 +3260,15 @@ Parser<FullParseHandler, CharT>::skipLazyInnerFunction(ParseNode* funcNode, uint
|
|||
if (!tokenStream.advance(fun->lazyScript()->end()))
|
||||
return false;
|
||||
|
||||
#if JS_HAS_EXPR_CLOSURES
|
||||
// Only expression closure can be Statement kind.
|
||||
// If we remove expression closure, we can remove isExprBody flag from
|
||||
// LazyScript and JSScript.
|
||||
if (kind == Statement && funbox->isExprBody()) {
|
||||
if (!matchOrInsertSemicolon())
|
||||
return false;
|
||||
if (allowExpressionClosures()) {
|
||||
// Only expression closure can be Statement kind.
|
||||
// If we remove expression closure, we can remove isExprBody flag from
|
||||
// LazyScript and JSScript.
|
||||
if (kind == Statement && funbox->isExprBody()) {
|
||||
if (!matchOrInsertSemicolon())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Append possible Annex B function box only upon successfully parsing.
|
||||
if (tryAnnexB && !pc->innermostScope()->addPossibleAnnexBFunctionBox(pc, funbox))
|
||||
|
@ -3778,14 +3778,14 @@ GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling i
|
|||
return false;
|
||||
}
|
||||
|
||||
#if JS_HAS_EXPR_CLOSURES
|
||||
this->addTelemetry(DeprecatedLanguageExtension::ExpressionClosure);
|
||||
if (!warnOnceAboutExprClosure())
|
||||
if (allowExpressionClosures()) {
|
||||
this->addTelemetry(DeprecatedLanguageExtension::ExpressionClosure);
|
||||
if (!warnOnceAboutExprClosure())
|
||||
return false;
|
||||
} else {
|
||||
error(JSMSG_CURLY_BEFORE_BODY);
|
||||
return false;
|
||||
#else
|
||||
error(JSMSG_CURLY_BEFORE_BODY);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
anyChars.ungetToken();
|
||||
|
@ -3847,9 +3847,8 @@ GeneralParser<ParseHandler, CharT>::functionFormalParametersAndBody(InHandling i
|
|||
JSMSG_CURLY_OPENED, openedPos));
|
||||
funbox->setEnd(anyChars);
|
||||
} else {
|
||||
#if !JS_HAS_EXPR_CLOSURES
|
||||
MOZ_ASSERT(kind == Arrow);
|
||||
#endif
|
||||
MOZ_ASSERT_IF(!allowExpressionClosures(), kind == Arrow);
|
||||
|
||||
if (anyChars.hadError())
|
||||
return false;
|
||||
funbox->setEnd(anyChars);
|
||||
|
|
|
@ -340,7 +340,9 @@ class ParserBase
|
|||
|
||||
bool hasValidSimpleStrictParameterNames();
|
||||
|
||||
|
||||
bool allowExpressionClosures() const {
|
||||
return options().expressionClosuresOption;
|
||||
}
|
||||
/*
|
||||
* Create a new function object given a name (which is optional if this is
|
||||
* a function expression).
|
||||
|
@ -703,6 +705,7 @@ class GeneralParser
|
|||
using Base::isValidSimpleAssignmentTarget;
|
||||
using Base::pc;
|
||||
using Base::usedNames;
|
||||
using Base::allowExpressionClosures;
|
||||
|
||||
private:
|
||||
using Base::checkAndMarkSuperScope;
|
||||
|
@ -1403,6 +1406,7 @@ class Parser<FullParseHandler, CharT> final
|
|||
using Base::pos;
|
||||
using Base::ss;
|
||||
using Base::tokenStream;
|
||||
using Base::allowExpressionClosures;
|
||||
|
||||
private:
|
||||
using Base::alloc;
|
||||
|
|
|
@ -38,7 +38,7 @@ js::Allocate(JSContext* cx, AllocKind kind, size_t nDynamicSlots, InitialHeap he
|
|||
static_assert(sizeof(JSObject_Slots0) >= MinCellSize,
|
||||
"All allocations must be at least the allocator-imposed minimum size.");
|
||||
|
||||
MOZ_ASSERT_IF(nDynamicSlots != 0, clasp->isNative() || clasp->isProxy());
|
||||
MOZ_ASSERT_IF(nDynamicSlots != 0, clasp->isNative());
|
||||
|
||||
// Off-thread alloc cannot trigger GC or make runtime assertions.
|
||||
if (cx->helperThread()) {
|
||||
|
@ -119,10 +119,12 @@ GCRuntime::tryNewTenuredObject(JSContext* cx, AllocKind kind, size_t thingSize,
|
|||
|
||||
JSObject* obj = tryNewTenuredThing<JSObject, allowGC>(cx, kind, thingSize);
|
||||
|
||||
if (obj)
|
||||
obj->setInitialSlotsMaybeNonNative(slots);
|
||||
else
|
||||
if (obj) {
|
||||
if (nDynamicSlots)
|
||||
static_cast<NativeObject*>(obj)->initSlots(slots);
|
||||
} else {
|
||||
js_free(slots);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ struct Class;
|
|||
//
|
||||
// Note that JSObject allocation must use the longer signature below that
|
||||
// includes slot, heap, and finalizer information in support of various
|
||||
// object-specific optimizations.
|
||||
// object-specific optimizations. If dynamic slots are requested they will be
|
||||
// allocated and the pointer stored directly in |NativeObject::slots_|.
|
||||
template <typename T, AllowGC allowGC = CanGC>
|
||||
T*
|
||||
Allocate(JSContext* cx);
|
||||
|
|
|
@ -265,7 +265,7 @@ js::Nursery::leaveZealMode() {
|
|||
#endif // JS_GC_ZEAL
|
||||
|
||||
JSObject*
|
||||
js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp)
|
||||
js::Nursery::allocateObject(JSContext* cx, size_t size, size_t nDynamicSlots, const js::Class* clasp)
|
||||
{
|
||||
/* Ensure there's enough space to replace the contents with a RelocationOverlay. */
|
||||
MOZ_ASSERT(size >= sizeof(RelocationOverlay));
|
||||
|
@ -281,9 +281,9 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
|
|||
|
||||
/* If we want external slots, add them. */
|
||||
HeapSlot* slots = nullptr;
|
||||
if (numDynamic) {
|
||||
if (nDynamicSlots) {
|
||||
MOZ_ASSERT(clasp->isNative());
|
||||
slots = static_cast<HeapSlot*>(allocateBuffer(cx->zone(), numDynamic * sizeof(HeapSlot)));
|
||||
slots = static_cast<HeapSlot*>(allocateBuffer(cx->zone(), nDynamicSlots * sizeof(HeapSlot)));
|
||||
if (!slots) {
|
||||
/*
|
||||
* It is safe to leave the allocated object uninitialized, since we
|
||||
|
@ -293,8 +293,11 @@ js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const
|
|||
}
|
||||
}
|
||||
|
||||
/* Always initialize the slots field to match the JIT behavior. */
|
||||
obj->setInitialSlotsMaybeNonNative(slots);
|
||||
/* Store slots pointer directly in new object. If no dynamic slots were
|
||||
* requested, caller must initialize slots_ field itself as needed. We
|
||||
* don't know if the caller was a native object or not. */
|
||||
if (nDynamicSlots)
|
||||
static_cast<NativeObject*>(obj)->initSlots(slots);
|
||||
|
||||
TraceNurseryAlloc(obj, size);
|
||||
return obj;
|
||||
|
|
|
@ -1104,12 +1104,11 @@ function test_syntax(postfixes, check_error, ignore_opts) {
|
|||
|
||||
// Expression closures
|
||||
|
||||
if (getBuildConfiguration().release_or_beta) {
|
||||
test("function f() 1 ");
|
||||
test("function f() 1; ");
|
||||
test("(function () 1 ");
|
||||
test("(function () 1); ");
|
||||
}
|
||||
enableExpressionClosures();
|
||||
test("function f() 1 ");
|
||||
test("function f() 1; ");
|
||||
test("(function () 1 ");
|
||||
test("(function () 1); ");
|
||||
|
||||
// ==== asm.js ====
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
load(libdir + "asm.js");
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
if (getBuildConfiguration().release_or_beta) {
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0');
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0; return 0');
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0; return f');
|
||||
}
|
||||
|
||||
enableExpressionClosures();
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0');
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0; return 0');
|
||||
assertAsmTypeFail(USE_ASM + 'function f() 0; return f');
|
||||
|
||||
assertAsmTypeFail(USE_ASM);
|
||||
assertAsmTypeFail(USE_ASM + 'return');
|
||||
|
|
|
@ -10,7 +10,7 @@ f = Function("");
|
|||
assertEq(f.toString(), "function anonymous(\n) {\n\n}");
|
||||
f = Function("", "(abc)");
|
||||
assertEq(f.toString(), "function anonymous(\n) {\n(abc)\n}");
|
||||
if (getBuildConfiguration().release_or_beta) {
|
||||
f = Function("", "return function (a,b) a + b;")();
|
||||
assertEq(f.toString(), "function (a,b) a + b");
|
||||
}
|
||||
|
||||
enableExpressionClosures();
|
||||
f = Function("", "return function (a,b) a + b;")();
|
||||
assertEq(f.toString(), "function (a,b) a + b");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
if (getBuildConfiguration().release_or_beta)
|
||||
enableExpressionClosures();
|
||||
eval(`
|
||||
function f1(foo, bar) foo + bar;
|
||||
assertEq(f1.toString(), "function f1(foo, bar) foo + bar");
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Testing InstanceOf IC.
|
||||
|
||||
Array.prototype.sum = function() {
|
||||
return this.reduce(( acc, cur ) => acc + cur, 0);
|
||||
}
|
||||
|
||||
|
||||
Iters = 20;
|
||||
|
||||
function resultArray(fn, obj) {
|
||||
res = new Array();
|
||||
for (var x = 0; x < Iters; x++) {
|
||||
res.push(fn(obj) ? 1 : 0);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Ensure alteration of .prototype invalidates IC
|
||||
function basic() {};
|
||||
|
||||
protoA = { prop1: "1"};
|
||||
basic.prototype = protoA;
|
||||
|
||||
io1 = x => { return x instanceof basic; }
|
||||
|
||||
var x = new basic();
|
||||
beforePrototypeModification = resultArray(io1,x).sum(); //Attach and test IC
|
||||
assertEq(beforePrototypeModification,Iters);
|
||||
|
||||
basic.prototype = {}; // Invalidate IC
|
||||
afterPrototypeModification = resultArray(io1,x).sum(); //Test
|
||||
assertEq(afterPrototypeModification,0);
|
||||
|
||||
//Primitive LHS returns false.
|
||||
assertEq(resultArray(io1,0).sum(),0);
|
|
@ -1,4 +1,4 @@
|
|||
if (getBuildConfiguration().release_or_beta)
|
||||
enableExpressionClosures();
|
||||
eval(`
|
||||
m = {
|
||||
i() {},
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
if (getBuildConfiguration().release_or_beta) {
|
||||
eval(`
|
||||
function f() {
|
||||
// The expression closure is deliberate here, testing the semicolon after
|
||||
// one when it appears as a FunctionDeclaration from B.3.4
|
||||
// FunctionDeclarations in IfStatement Statement Clauses.
|
||||
if (0)
|
||||
function g() x;
|
||||
else;
|
||||
}
|
||||
f();`)
|
||||
enableExpressionClosures();
|
||||
eval(`
|
||||
function f() {
|
||||
// The expression closure is deliberate here, testing the semicolon after
|
||||
// one when it appears as a FunctionDeclaration from B.3.4
|
||||
// FunctionDeclarations in IfStatement Statement Clauses.
|
||||
if (0)
|
||||
function g() x;
|
||||
else;
|
||||
}
|
||||
f();`)
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
var release_or_beta = getBuildConfiguration().release_or_beta;
|
||||
|
||||
enableExpressionClosures();
|
||||
|
||||
function testWarn(code) {
|
||||
if (release_or_beta) {
|
||||
// Warning for expression closure is non-release-only (not Release/Beta).
|
||||
testPass(code);
|
||||
return;
|
||||
} else {
|
||||
// !!! expression closures are currently completely disabled in Nightly
|
||||
return;
|
||||
}
|
||||
|
||||
enableLastWarning();
|
||||
|
|
|
@ -449,6 +449,34 @@ BaselineCacheIRCompiler::emitGuardXrayExpandoShapeAndDefaultProto()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitGuardFunctionPrototype()
|
||||
{
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
Register prototypeObject = allocator.useRegister(masm, reader.objOperandId());
|
||||
|
||||
// Allocate registers before the failure path to make sure they're registered
|
||||
// by addFailurePath.
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
// Guard on the .prototype object.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1);
|
||||
masm.load32(Address(stubAddress(reader.stubOffset())), scratch2);
|
||||
BaseValueIndex prototypeSlot(scratch1, scratch2);
|
||||
masm.branchTestObject(Assembler::NotEqual, prototypeSlot, failure->label());
|
||||
masm.unboxObject(prototypeSlot, scratch1);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
prototypeObject,
|
||||
scratch1, failure->label());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
|
||||
{
|
||||
|
@ -2076,6 +2104,7 @@ BaselineCacheIRCompiler::init(CacheKind kind)
|
|||
case CacheKind::SetProp:
|
||||
case CacheKind::In:
|
||||
case CacheKind::HasOwn:
|
||||
case CacheKind::InstanceOf:
|
||||
MOZ_ASSERT(numInputs == 2);
|
||||
allocator.initInputLocation(0, R0);
|
||||
allocator.initInputLocation(1, R1);
|
||||
|
|
|
@ -4292,46 +4292,34 @@ ICIteratorClose_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
|
||||
static bool
|
||||
TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
|
||||
HandleFunction fun, bool* attached)
|
||||
HandleValue lhs, HandleObject rhs, bool* attached)
|
||||
{
|
||||
MOZ_ASSERT(!*attached);
|
||||
if (fun->isBoundFunction())
|
||||
return true;
|
||||
FallbackICSpew(cx, stub, "InstanceOf");
|
||||
|
||||
// If the user has supplied their own @@hasInstance method we shouldn't
|
||||
// clobber it.
|
||||
if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols()))
|
||||
return true;
|
||||
if (stub->state().maybeTransition())
|
||||
stub->discardStubs(cx);
|
||||
|
||||
// Refuse to optimize any function whose [[Prototype]] isn't
|
||||
// Function.prototype.
|
||||
if (!fun->hasStaticPrototype() || fun->hasUncacheableProto())
|
||||
return true;
|
||||
if (stub->state().canAttachStub()) {
|
||||
RootedScript script(cx, frame->script());
|
||||
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||
|
||||
Value funProto = cx->global()->getPrototype(JSProto_Function);
|
||||
if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject())
|
||||
return true;
|
||||
ICStubEngine engine = ICStubEngine::Baseline;
|
||||
InstanceOfIRGenerator gen(cx, script, pc, stub->state().mode(),
|
||||
lhs,
|
||||
rhs);
|
||||
|
||||
Shape* shape = fun->lookupPure(cx->names().prototype);
|
||||
if (!shape || !shape->isDataProperty())
|
||||
return true;
|
||||
if (gen.tryAttachStub()) {
|
||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||
BaselineCacheIRStubKind::Regular,
|
||||
engine, script, stub, attached);
|
||||
if (newStub)
|
||||
JitSpew(JitSpew_BaselineIC, " Attached InstanceOf CacheIR stub, attached is now %d", *attached);
|
||||
}
|
||||
if (!attached)
|
||||
stub->state().trackNotAttached();
|
||||
}
|
||||
|
||||
uint32_t slot = shape->slot();
|
||||
MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this");
|
||||
|
||||
if (!fun->getSlot(slot).isObject())
|
||||
return true;
|
||||
|
||||
JSObject* protoObject = &fun->getSlot(slot).toObject();
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating InstanceOf(Function) stub");
|
||||
ICInstanceOf_Function::Compiler compiler(cx, fun->lastProperty(), protoObject, slot);
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4362,12 +4350,8 @@ DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback*
|
|||
// for use during Ion compilation.
|
||||
EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
|
||||
|
||||
if (stub->numOptimizedStubs() >= ICInstanceOf_Fallback::MAX_OPTIMIZED_STUBS)
|
||||
return true;
|
||||
|
||||
RootedFunction fun(cx, &obj->as<JSFunction>());
|
||||
bool attached = false;
|
||||
if (!TryAttachInstanceOfStub(cx, frame, stub, fun, &attached))
|
||||
if (!TryAttachInstanceOfStub(cx, frame, stub, lhs, obj, &attached))
|
||||
return false;
|
||||
if (!attached)
|
||||
stub->noteUnoptimizableAccess();
|
||||
|
@ -4399,82 +4383,6 @@ ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
return tailCallVM(DoInstanceOfFallbackInfo, masm);
|
||||
}
|
||||
|
||||
bool
|
||||
ICInstanceOf_Function::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
|
||||
Label failure;
|
||||
|
||||
// Ensure RHS is an object.
|
||||
masm.branchTestObject(Assembler::NotEqual, R1, &failure);
|
||||
Register rhsObj = masm.extractObject(R1, ExtractTemp0);
|
||||
|
||||
// Allow using R1's type register as scratch. We have to restore it when
|
||||
// we want to jump to the next stub.
|
||||
Label failureRestoreR1;
|
||||
AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
|
||||
regs.takeUnchecked(rhsObj);
|
||||
|
||||
Register scratch1 = regs.takeAny();
|
||||
Register scratch2 = regs.takeAny();
|
||||
|
||||
// Shape guard.
|
||||
masm.loadPtr(Address(ICStubReg, ICInstanceOf_Function::offsetOfShape()), scratch1);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, rhsObj, scratch1, &failureRestoreR1);
|
||||
|
||||
// Guard on the .prototype object.
|
||||
masm.loadPtr(Address(rhsObj, NativeObject::offsetOfSlots()), scratch1);
|
||||
masm.load32(Address(ICStubReg, ICInstanceOf_Function::offsetOfSlot()), scratch2);
|
||||
BaseValueIndex prototypeSlot(scratch1, scratch2);
|
||||
masm.branchTestObject(Assembler::NotEqual, prototypeSlot, &failureRestoreR1);
|
||||
masm.unboxObject(prototypeSlot, scratch1);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(ICStubReg, ICInstanceOf_Function::offsetOfPrototypeObject()),
|
||||
scratch1, &failureRestoreR1);
|
||||
|
||||
// If LHS is a primitive, return false.
|
||||
Label returnFalse, returnTrue;
|
||||
masm.branchTestObject(Assembler::NotEqual, R0, &returnFalse);
|
||||
|
||||
// LHS is an object. Load its proto.
|
||||
masm.unboxObject(R0, scratch2);
|
||||
masm.loadObjProto(scratch2, scratch2);
|
||||
|
||||
{
|
||||
// Walk the proto chain until we either reach the target object,
|
||||
// nullptr or LazyProto.
|
||||
Label loop;
|
||||
masm.bind(&loop);
|
||||
|
||||
masm.branchPtr(Assembler::Equal, scratch2, scratch1, &returnTrue);
|
||||
masm.branchTestPtr(Assembler::Zero, scratch2, scratch2, &returnFalse);
|
||||
|
||||
MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
|
||||
masm.branchPtr(Assembler::Equal, scratch2, ImmWord(1), &failureRestoreR1);
|
||||
|
||||
masm.loadObjProto(scratch2, scratch2);
|
||||
masm.jump(&loop);
|
||||
}
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&returnFalse);
|
||||
masm.moveValue(BooleanValue(false), R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&returnTrue);
|
||||
masm.moveValue(BooleanValue(true), R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&failureRestoreR1);
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, rhsObj, R1);
|
||||
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// TypeOf_Fallback
|
||||
//
|
||||
|
@ -4667,14 +4575,6 @@ ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value&
|
|||
ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant()
|
||||
{ }
|
||||
|
||||
ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape,
|
||||
JSObject* prototypeObj, uint32_t slot)
|
||||
: ICStub(InstanceOf_Function, stubCode),
|
||||
shape_(shape),
|
||||
prototypeObj_(prototypeObj),
|
||||
slot_(slot)
|
||||
{ }
|
||||
|
||||
ICCall_Scripted::ICCall_Scripted(JitCode* stubCode, ICStub* firstMonitorStub,
|
||||
JSFunction* callee, JSObject* templateObject,
|
||||
uint32_t pcOffset)
|
||||
|
|
|
@ -1454,7 +1454,6 @@ class ICInstanceOf_Fallback : public ICFallbackStub
|
|||
static const uint16_t UNOPTIMIZABLE_ACCESS_BIT = 0x1;
|
||||
|
||||
public:
|
||||
static const uint32_t MAX_OPTIMIZED_STUBS = 4;
|
||||
|
||||
void noteUnoptimizableAccess() {
|
||||
extra_ |= UNOPTIMIZABLE_ACCESS_BIT;
|
||||
|
@ -1478,59 +1477,6 @@ class ICInstanceOf_Fallback : public ICFallbackStub
|
|||
};
|
||||
};
|
||||
|
||||
class ICInstanceOf_Function : public ICStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
GCPtrShape shape_;
|
||||
GCPtrObject prototypeObj_;
|
||||
uint32_t slot_;
|
||||
|
||||
ICInstanceOf_Function(JitCode* stubCode, Shape* shape, JSObject* prototypeObj, uint32_t slot);
|
||||
|
||||
public:
|
||||
GCPtrShape& shape() {
|
||||
return shape_;
|
||||
}
|
||||
GCPtrObject& prototypeObject() {
|
||||
return prototypeObj_;
|
||||
}
|
||||
uint32_t slot() const {
|
||||
return slot_;
|
||||
}
|
||||
static size_t offsetOfShape() {
|
||||
return offsetof(ICInstanceOf_Function, shape_);
|
||||
}
|
||||
static size_t offsetOfPrototypeObject() {
|
||||
return offsetof(ICInstanceOf_Function, prototypeObj_);
|
||||
}
|
||||
static size_t offsetOfSlot() {
|
||||
return offsetof(ICInstanceOf_Function, slot_);
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
RootedShape shape_;
|
||||
RootedObject prototypeObj_;
|
||||
uint32_t slot_;
|
||||
|
||||
protected:
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, Shape* shape, JSObject* prototypeObj, uint32_t slot)
|
||||
: ICStubCompiler(cx, ICStub::InstanceOf_Function, Engine::Baseline),
|
||||
shape_(cx, shape),
|
||||
prototypeObj_(cx, prototypeObj),
|
||||
slot_(slot)
|
||||
{}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) override {
|
||||
return newStub<ICInstanceOf_Function>(space, getStubCode(), shape_, prototypeObj_,
|
||||
slot_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// TypeOf
|
||||
// JSOP_TYPEOF
|
||||
// JSOP_TYPEOFEXPR
|
||||
|
|
|
@ -1383,27 +1383,49 @@ BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot,
|
|||
JSObject** prototypeObject)
|
||||
{
|
||||
MOZ_ASSERT(*pc == JSOP_INSTANCEOF);
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
||||
const ICEntry& entry = icEntryFromPC(pc);
|
||||
ICStub* firstStub = entry.firstStub();
|
||||
|
||||
ICStub* stub = entry.firstStub();
|
||||
if (!stub->isInstanceOf_Function() ||
|
||||
!stub->next()->isInstanceOf_Fallback() ||
|
||||
stub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess())
|
||||
{
|
||||
// Ensure singleton instanceof stub
|
||||
if (!firstStub->next() ||
|
||||
!firstStub->isCacheIR_Regular() ||
|
||||
!firstStub->next()->isInstanceOf_Fallback() ||
|
||||
firstStub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ICCacheIR_Regular* stub = entry.firstStub()->toCacheIR_Regular();
|
||||
CacheIRReader reader(stub->stubInfo());
|
||||
|
||||
ObjOperandId rhsId = ObjOperandId(1);
|
||||
ObjOperandId resId = ObjOperandId(2);
|
||||
|
||||
if (!reader.matchOp(CacheOp::GuardIsObject, rhsId))
|
||||
return false;
|
||||
}
|
||||
|
||||
ICInstanceOf_Function* optStub = stub->toInstanceOf_Function();
|
||||
*shape = optStub->shape();
|
||||
*prototypeObject = optStub->prototypeObject();
|
||||
*slot = optStub->slot();
|
||||
if (!reader.matchOp(CacheOp::GuardShape, rhsId))
|
||||
return false;
|
||||
|
||||
*shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
|
||||
|
||||
if (!reader.matchOp(CacheOp::LoadObject, resId))
|
||||
return false;
|
||||
|
||||
*prototypeObject = stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset()).get();
|
||||
|
||||
if (IsInsideNursery(*prototypeObject))
|
||||
return false;
|
||||
|
||||
if (!reader.matchOp(CacheOp::GuardFunctionPrototype, rhsId))
|
||||
return false;
|
||||
|
||||
reader.skip(); // Skip over the protoID;
|
||||
|
||||
*slot = stub->stubInfo()->getStubRawWord(stub, reader.stubOffset());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -4001,6 +4001,120 @@ SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape
|
|||
return true;
|
||||
}
|
||||
|
||||
InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
ICState::Mode mode, HandleValue lhs, HandleObject rhs)
|
||||
: IRGenerator(cx, script, pc, CacheKind::InstanceOf, mode),
|
||||
lhsVal_(lhs),
|
||||
rhsObj_(rhs)
|
||||
{ }
|
||||
|
||||
bool
|
||||
InstanceOfIRGenerator::tryAttachStub()
|
||||
{
|
||||
MOZ_ASSERT(cacheKind_ == CacheKind::InstanceOf);
|
||||
AutoAssertNoPendingException aanpe(cx_);
|
||||
|
||||
// Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared to handle.
|
||||
if (!rhsObj_->is<JSFunction>()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
HandleFunction fun = rhsObj_.as<JSFunction>();
|
||||
|
||||
if (fun->isBoundFunction()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the user has supplied their own @@hasInstance method we shouldn't
|
||||
// clobber it.
|
||||
if (!js::FunctionHasDefaultHasInstance(fun, cx_->wellKnownSymbols())) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refuse to optimize any function whose [[Prototype]] isn't
|
||||
// Function.prototype.
|
||||
if (!fun->hasStaticPrototype() || fun->hasUncacheableProto()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
Value funProto = cx_->global()->getPrototype(JSProto_Function);
|
||||
if (!funProto.isObject() || fun->staticPrototype() != &funProto.toObject()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure that the function's prototype slot is the same.
|
||||
Shape* shape = fun->lookupPure(cx_->names().prototype);
|
||||
if (!shape || !shape->isDataProperty()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t slot = shape->slot();
|
||||
|
||||
MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this");
|
||||
if (!fun->getSlot(slot).isObject()) {
|
||||
trackNotAttached();
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject* prototypeObject = &fun->getSlot(slot).toObject();
|
||||
|
||||
// Abstract Objects
|
||||
ValOperandId lhs(writer.setInputOperandId(0));
|
||||
ValOperandId rhs(writer.setInputOperandId(1));
|
||||
|
||||
ObjOperandId rhsId = writer.guardIsObject(rhs);
|
||||
writer.guardShape(rhsId, fun->lastProperty());
|
||||
|
||||
// Load prototypeObject into the cache -- consumed twice in the IC
|
||||
ObjOperandId protoId = writer.loadObject(prototypeObject);
|
||||
// Ensure that rhs[slot] == prototypeObject.
|
||||
writer.guardFunctionPrototype(rhsId, slot, protoId);
|
||||
|
||||
// Needn't guard LHS is object, because the actual stub can handle that
|
||||
// and correctly return false.
|
||||
writer.loadInstanceOfObjectResult(lhs, protoId, slot);
|
||||
writer.returnFromIC();
|
||||
trackAttached("InstanceOf");
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
InstanceOfIRGenerator::trackAttached(const char* name)
|
||||
{
|
||||
#ifdef JS_CACHEIR_SPEW
|
||||
CacheIRSpewer& sp = CacheIRSpewer::singleton();
|
||||
if (sp.enabled()) {
|
||||
LockGuard<Mutex> guard(sp.lock());
|
||||
sp.beginCache(guard, *this);
|
||||
sp.valueProperty(guard, "lhs", lhsVal_);
|
||||
sp.valueProperty(guard, "rhs", ObjectValue(*rhsObj_));
|
||||
sp.attached(guard, name);
|
||||
sp.endCache(guard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
InstanceOfIRGenerator::trackNotAttached()
|
||||
{
|
||||
#ifdef JS_CACHEIR_SPEW
|
||||
CacheIRSpewer& sp = CacheIRSpewer::singleton();
|
||||
if (sp.enabled()) {
|
||||
LockGuard<Mutex> guard(sp.lock());
|
||||
sp.beginCache(guard, *this);
|
||||
sp.valueProperty(guard, "lhs", lhsVal_);
|
||||
sp.valueProperty(guard, "rhs", ObjectValue(*rhsObj_));
|
||||
sp.endCache(guard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
ICState::Mode mode, HandleValue value)
|
||||
: IRGenerator(cx, script, pc, CacheKind::TypeOf, mode),
|
||||
|
|
|
@ -148,6 +148,7 @@ class TypedOperandId : public OperandId
|
|||
_(In) \
|
||||
_(HasOwn) \
|
||||
_(TypeOf) \
|
||||
_(InstanceOf) \
|
||||
_(GetIterator) \
|
||||
_(Compare) \
|
||||
_(Call)
|
||||
|
@ -195,6 +196,7 @@ extern const char* CacheKindNames[];
|
|||
_(GuardGroupHasUnanalyzedNewScript) \
|
||||
_(GuardIndexIsNonNegative) \
|
||||
_(GuardXrayExpandoShapeAndDefaultProto) \
|
||||
_(GuardFunctionPrototype) \
|
||||
_(LoadStackValue) \
|
||||
_(LoadObject) \
|
||||
_(LoadProto) \
|
||||
|
@ -264,6 +266,7 @@ extern const char* CacheKindNames[];
|
|||
_(LoadUndefinedResult) \
|
||||
_(LoadBooleanResult) \
|
||||
_(LoadStringResult) \
|
||||
_(LoadInstanceOfObjectResult) \
|
||||
_(LoadTypeOfObjectResult) \
|
||||
\
|
||||
_(CallStringSplitResult) \
|
||||
|
@ -533,6 +536,12 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
|||
writeOpWithOperandId(CacheOp::GuardXrayExpandoShapeAndDefaultProto, obj);
|
||||
buffer_.writeByte(uint32_t(!!shapeWrapper)); addStubField(uintptr_t(shapeWrapper), StubField::Type::JSObject);
|
||||
}
|
||||
// Guard rhs[slot] == prototypeObject
|
||||
void guardFunctionPrototype(ObjOperandId rhs, uint32_t slot, ObjOperandId protoId) {
|
||||
writeOpWithOperandId(CacheOp::GuardFunctionPrototype, rhs);
|
||||
writeOperandId(protoId);
|
||||
addStubField(slot, StubField::Type::RawWord);
|
||||
}
|
||||
void guardGroup(ObjOperandId obj, ObjectGroup* group) {
|
||||
writeOpWithOperandId(CacheOp::GuardGroup, obj);
|
||||
addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
||||
|
@ -992,10 +1001,14 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
|||
void loadObjectResult(ObjOperandId obj) {
|
||||
writeOpWithOperandId(CacheOp::LoadObjectResult, obj);
|
||||
}
|
||||
void loadInstanceOfObjectResult(ValOperandId lhs, ObjOperandId protoId, uint32_t slot) {
|
||||
writeOp(CacheOp::LoadInstanceOfObjectResult);
|
||||
writeOperandId(lhs);
|
||||
writeOperandId(protoId);
|
||||
}
|
||||
void loadTypeOfObjectResult(ObjOperandId obj) {
|
||||
writeOpWithOperandId(CacheOp::LoadTypeOfObjectResult, obj);
|
||||
}
|
||||
|
||||
void callStringSplitResult(StringOperandId str, StringOperandId sep, ObjectGroup* group) {
|
||||
writeOp(CacheOp::CallStringSplitResult);
|
||||
writeOperandId(str);
|
||||
|
@ -1063,6 +1076,9 @@ class MOZ_RAII CacheIRReader
|
|||
return CacheOp(buffer_.readByte());
|
||||
}
|
||||
|
||||
// Skip data not currently used.
|
||||
void skip() { buffer_.readByte(); }
|
||||
|
||||
ValOperandId valOperandId() { return ValOperandId(buffer_.readByte()); }
|
||||
ObjOperandId objOperandId() { return ObjOperandId(buffer_.readByte()); }
|
||||
StringOperandId stringOperandId() { return StringOperandId(buffer_.readByte()); }
|
||||
|
@ -1485,6 +1501,20 @@ class MOZ_RAII HasPropIRGenerator : public IRGenerator
|
|||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
class MOZ_RAII InstanceOfIRGenerator : public IRGenerator
|
||||
{
|
||||
HandleValue lhsVal_;
|
||||
HandleObject rhsObj_;
|
||||
|
||||
void trackAttached(const char* name);
|
||||
void trackNotAttached();
|
||||
public:
|
||||
InstanceOfIRGenerator(JSContext*, HandleScript, jsbytecode*, ICState::Mode,
|
||||
HandleValue, HandleObject);
|
||||
|
||||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
class MOZ_RAII TypeOfIRGenerator : public IRGenerator
|
||||
{
|
||||
HandleValue val_;
|
||||
|
|
|
@ -863,6 +863,13 @@ AsGCPtr(uintptr_t* ptr)
|
|||
return reinterpret_cast<GCPtr<T>*>(ptr);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
CacheIRStubInfo::getStubRawWord(ICStub* stub, uint32_t offset) const {
|
||||
uint8_t* stubData = (uint8_t*)stub + stubDataOffset_;
|
||||
MOZ_ASSERT(uintptr_t(stubData) % sizeof(uintptr_t) == 0);
|
||||
return *(uintptr_t*)(stubData + offset);
|
||||
}
|
||||
|
||||
template<class Stub, class T>
|
||||
GCPtr<T>&
|
||||
CacheIRStubInfo::getStubField(Stub* stub, uint32_t offset) const
|
||||
|
@ -2631,3 +2638,50 @@ CacheIRCompiler::emitCallObjectHasSparseElementResult()
|
|||
masm.adjustStack(sizeof(Value));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIRCompiler::emitLoadInstanceOfObjectResult()
|
||||
{
|
||||
AutoOutputRegister output(*this);
|
||||
ValueOperand lhs = allocator.useValueRegister(masm, reader.valOperandId());
|
||||
Register proto = allocator.useRegister(masm, reader.objOperandId());
|
||||
|
||||
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
Label returnFalse, returnTrue, done;
|
||||
masm.branchTestObject(Assembler::NotEqual, lhs, &returnFalse);
|
||||
|
||||
// LHS is an object. Load its proto.
|
||||
masm.unboxObject(lhs, scratch);
|
||||
masm.loadObjProto(scratch, scratch);
|
||||
{
|
||||
// Walk the proto chain until we either reach the target object,
|
||||
// nullptr or LazyProto.
|
||||
Label loop;
|
||||
masm.bind(&loop);
|
||||
|
||||
masm.branchPtr(Assembler::Equal, scratch, proto, &returnTrue);
|
||||
masm.branchTestPtr(Assembler::Zero, scratch, scratch, &returnFalse);
|
||||
|
||||
MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1);
|
||||
masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), failure->label());
|
||||
|
||||
masm.loadObjProto(scratch, scratch);
|
||||
masm.jump(&loop);
|
||||
}
|
||||
|
||||
|
||||
masm.bind(&returnFalse);
|
||||
EmitStoreBoolean(masm, false, output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&returnTrue);
|
||||
EmitStoreBoolean(masm, true, output);
|
||||
//fallthrough
|
||||
masm.bind(&done);
|
||||
return true;
|
||||
}
|
|
@ -47,6 +47,7 @@ namespace jit {
|
|||
_(LoadStringLengthResult) \
|
||||
_(LoadStringCharResult) \
|
||||
_(LoadArgumentsObjectArgResult) \
|
||||
_(LoadInstanceOfObjectResult) \
|
||||
_(LoadDenseElementResult) \
|
||||
_(LoadDenseElementHoleResult) \
|
||||
_(LoadDenseElementExistsResult) \
|
||||
|
@ -712,6 +713,8 @@ class CacheIRStubInfo
|
|||
return getStubField<ICStub, T>(stub, field);
|
||||
}
|
||||
|
||||
uintptr_t getStubRawWord(ICStub* stub, uint32_t field) const;
|
||||
|
||||
void copyStubData(ICStub* src, ICStub* dest) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -173,6 +173,12 @@ typedef bool (*IonInICFn)(JSContext*, HandleScript, IonInIC*, HandleValue, Handl
|
|||
static const VMFunction IonInICInfo =
|
||||
FunctionInfo<IonInICFn>(IonInIC::update, "IonInIC::update");
|
||||
|
||||
|
||||
typedef bool (*IonInstanceOfICFn)(JSContext*, HandleScript, IonInstanceOfIC*,
|
||||
HandleValue lhs, HandleObject rhs, bool* res);
|
||||
static const VMFunction IonInstanceOfInfo =
|
||||
FunctionInfo<IonInstanceOfICFn>(IonInstanceOfIC::update, "IonInstanceOfIC::update");
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
|
||||
{
|
||||
|
@ -331,6 +337,24 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
|
|||
masm.jump(ool->rejoin());
|
||||
return;
|
||||
}
|
||||
case CacheKind::InstanceOf: {
|
||||
IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC();
|
||||
|
||||
saveLive(lir);
|
||||
|
||||
pushArg(hasInstanceOfIC->rhs());
|
||||
pushArg(hasInstanceOfIC->lhs());
|
||||
icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
|
||||
callVM(IonInstanceOfInfo, lir);
|
||||
|
||||
StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
|
||||
restoreLiveIgnore(lir, StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
return;
|
||||
}
|
||||
case CacheKind::Call:
|
||||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
|
@ -11611,15 +11635,16 @@ typedef bool (*HasInstanceFn)(JSContext*, HandleObject, HandleValue, bool*);
|
|||
static const VMFunction HasInstanceInfo = FunctionInfo<HasInstanceFn>(js::HasInstance, "HasInstance");
|
||||
|
||||
void
|
||||
CodeGenerator::visitCallInstanceOf(LCallInstanceOf* ins)
|
||||
CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins)
|
||||
{
|
||||
ValueOperand lhs = ToValue(ins, LCallInstanceOf::LHS);
|
||||
// The Lowering ensures that RHS is an object, and that LHS is a value.
|
||||
LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
|
||||
TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(ins, LInstanceOfCache::LHS));
|
||||
Register rhs = ToRegister(ins->rhs());
|
||||
MOZ_ASSERT(ToRegister(ins->output()) == ReturnReg);
|
||||
Register output = ToRegister(ins->output());
|
||||
|
||||
pushArg(lhs);
|
||||
pushArg(rhs);
|
||||
callVM(HasInstanceInfo, ins);
|
||||
IonInstanceOfIC ic(liveRegs, lhs, rhs, output);
|
||||
addIC(ins, allocateIC(ic));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -371,7 +371,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitInArray(LInArray* ins) override;
|
||||
void visitInstanceOfO(LInstanceOfO* ins) override;
|
||||
void visitInstanceOfV(LInstanceOfV* ins) override;
|
||||
void visitCallInstanceOf(LCallInstanceOf* ins) override;
|
||||
void visitInstanceOfCache(LInstanceOfCache* ins) override;
|
||||
void visitGetDOMProperty(LGetDOMProperty* lir) override;
|
||||
void visitGetDOMMemberV(LGetDOMMemberV* lir) override;
|
||||
void visitGetDOMMemberT(LGetDOMMemberT* lir) override;
|
||||
|
|
|
@ -13214,7 +13214,7 @@ IonBuilder::jsop_instanceof()
|
|||
return resumeAfter(ins);
|
||||
} while (false);
|
||||
|
||||
MCallInstanceOf* ins = MCallInstanceOf::New(alloc(), obj, rhs);
|
||||
MInstanceOfCache* ins = MInstanceOfCache::New(alloc(), obj, rhs);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
|
|
@ -530,6 +530,19 @@ IonCacheIRCompiler::init()
|
|||
allocator.initInputLocation(1, ic->value());
|
||||
break;
|
||||
}
|
||||
case CacheKind::InstanceOf: {
|
||||
IonInstanceOfIC* ic = ic_->asInstanceOfIC();
|
||||
Register output = ic->output();
|
||||
available.add(output);
|
||||
liveRegs_.emplace(ic->liveRegs());
|
||||
outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Boolean, AnyRegister(output)));
|
||||
|
||||
MOZ_ASSERT(numInputs == 2);
|
||||
allocator.initInputLocation(0, ic->lhs());
|
||||
allocator.initInputLocation(1, TypedOrValueRegister(MIRType::Object,
|
||||
AnyRegister(ic->rhs())));
|
||||
break;
|
||||
}
|
||||
case CacheKind::Call:
|
||||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
|
@ -844,6 +857,35 @@ IonCacheIRCompiler::emitGuardXrayExpandoShapeAndDefaultProto()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitGuardFunctionPrototype()
|
||||
{
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
Register prototypeObject = allocator.useRegister(masm, reader.objOperandId());
|
||||
|
||||
// Allocate registers before the failure path to make sure they're registered
|
||||
// by addFailurePath.
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
// Guard on the .prototype object.
|
||||
masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1);
|
||||
uintptr_t slot = readStubWord(reader.stubOffset(), StubField::Type::RawWord);
|
||||
masm.move32(Imm32(slot), scratch2);
|
||||
BaseValueIndex prototypeSlot(scratch1, scratch2);
|
||||
masm.branchTestObject(Assembler::NotEqual, prototypeSlot, failure->label());
|
||||
masm.unboxObject(prototypeSlot, scratch1);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
prototypeObject,
|
||||
scratch1, failure->label());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitLoadFixedSlotResult()
|
||||
{
|
||||
|
|
|
@ -58,6 +58,8 @@ IonIC::scratchRegisterForEntryJump()
|
|||
return asHasOwnIC()->output();
|
||||
case CacheKind::GetIterator:
|
||||
return asGetIteratorIC()->temp1();
|
||||
case CacheKind::InstanceOf:
|
||||
return asInstanceOfIC()->output();
|
||||
case CacheKind::Call:
|
||||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
|
@ -474,6 +476,32 @@ IonInIC::update(JSContext* cx, HandleScript outerScript, IonInIC* ic,
|
|||
|
||||
return OperatorIn(cx, key, obj, res);
|
||||
}
|
||||
/* static */ bool
|
||||
IonInstanceOfIC::update(JSContext* cx, HandleScript outerScript, IonInstanceOfIC* ic,
|
||||
HandleValue lhs, HandleObject rhs, bool* res)
|
||||
{
|
||||
IonScript* ionScript = outerScript->ionScript();
|
||||
|
||||
if (ic->state().maybeTransition())
|
||||
ic->discardStubs(cx->zone());
|
||||
|
||||
if (ic->state().canAttachStub()) {
|
||||
bool attached = false;
|
||||
RootedScript script(cx, ic->script());
|
||||
jsbytecode* pc = ic->pc();
|
||||
|
||||
InstanceOfIRGenerator gen(cx, script, pc, ic->state().mode(),
|
||||
lhs, rhs);
|
||||
|
||||
if (gen.tryAttachStub())
|
||||
ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
|
||||
|
||||
if (!attached)
|
||||
ic->state().trackNotAttached();
|
||||
}
|
||||
|
||||
return HasInstance(cx, rhs, lhs, res);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
IonICStub::stubDataStart()
|
||||
|
|
|
@ -64,6 +64,7 @@ class IonBindNameIC;
|
|||
class IonGetIteratorIC;
|
||||
class IonHasOwnIC;
|
||||
class IonInIC;
|
||||
class IonInstanceOfIC;
|
||||
|
||||
class IonIC
|
||||
{
|
||||
|
@ -167,6 +168,10 @@ class IonIC
|
|||
MOZ_ASSERT(kind_ == CacheKind::In);
|
||||
return (IonInIC*)this;
|
||||
}
|
||||
IonInstanceOfIC* asInstanceOfIC() {
|
||||
MOZ_ASSERT(kind_ == CacheKind::InstanceOf);
|
||||
return (IonInstanceOfIC*)this;
|
||||
}
|
||||
|
||||
void updateBaseAddress(JitCode* code, MacroAssembler& masm);
|
||||
|
||||
|
@ -441,6 +446,35 @@ class IonInIC : public IonIC
|
|||
HandleValue key, HandleObject obj, bool* res);
|
||||
};
|
||||
|
||||
class IonInstanceOfIC : public IonIC
|
||||
{
|
||||
LiveRegisterSet liveRegs_;
|
||||
|
||||
TypedOrValueRegister lhs_;
|
||||
Register rhs_;
|
||||
Register output_;
|
||||
|
||||
public:
|
||||
|
||||
IonInstanceOfIC(LiveRegisterSet liveRegs, TypedOrValueRegister lhs, Register rhs,
|
||||
Register output)
|
||||
: IonIC(CacheKind::InstanceOf),
|
||||
liveRegs_(liveRegs),
|
||||
lhs_(lhs),
|
||||
rhs_(rhs),
|
||||
output_(output)
|
||||
{ }
|
||||
|
||||
LiveRegisterSet liveRegs() const { return liveRegs_; }
|
||||
TypedOrValueRegister lhs() const { return lhs_; }
|
||||
Register rhs() const { return rhs_; }
|
||||
Register output() const { return output_; }
|
||||
|
||||
// This signature mimics that of TryAttachInstanceOfStub in baseline
|
||||
static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonInstanceOfIC* ic,
|
||||
HandleValue lhs, HandleObject rhs, bool* attached);
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -4335,7 +4335,7 @@ LIRGenerator::visitInstanceOf(MInstanceOf* ins)
|
|||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitCallInstanceOf(MCallInstanceOf* ins)
|
||||
LIRGenerator::visitInstanceOfCache(MInstanceOfCache* ins)
|
||||
{
|
||||
MDefinition* lhs = ins->lhs();
|
||||
MDefinition* rhs = ins->rhs();
|
||||
|
@ -4343,9 +4343,9 @@ LIRGenerator::visitCallInstanceOf(MCallInstanceOf* ins)
|
|||
MOZ_ASSERT(lhs->type() == MIRType::Value);
|
||||
MOZ_ASSERT(rhs->type() == MIRType::Object);
|
||||
|
||||
LCallInstanceOf* lir = new(alloc()) LCallInstanceOf(useBoxAtStart(lhs),
|
||||
useRegisterAtStart(rhs));
|
||||
defineReturn(lir, ins);
|
||||
LInstanceOfCache* lir = new(alloc()) LInstanceOfCache(useBox(lhs),
|
||||
useRegister(rhs));
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitInArray(MInArray* ins) override;
|
||||
void visitHasOwnCache(MHasOwnCache* ins) override;
|
||||
void visitInstanceOf(MInstanceOf* ins) override;
|
||||
void visitCallInstanceOf(MCallInstanceOf* ins) override;
|
||||
void visitInstanceOfCache(MInstanceOfCache* ins) override;
|
||||
void visitIsCallable(MIsCallable* ins) override;
|
||||
void visitIsConstructor(MIsConstructor* ins) override;
|
||||
void visitIsArray(MIsArray* ins) override;
|
||||
|
|
|
@ -12923,18 +12923,18 @@ class MInstanceOf
|
|||
};
|
||||
|
||||
// Implementation for instanceof operator with unknown rhs.
|
||||
class MCallInstanceOf
|
||||
class MInstanceOfCache
|
||||
: public MBinaryInstruction,
|
||||
public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
|
||||
{
|
||||
MCallInstanceOf(MDefinition* obj, MDefinition* proto)
|
||||
MInstanceOfCache(MDefinition* obj, MDefinition* proto)
|
||||
: MBinaryInstruction(classOpcode, obj, proto)
|
||||
{
|
||||
setResultType(MIRType::Boolean);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(CallInstanceOf)
|
||||
INSTRUCTION_HEADER(InstanceOfCache)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
};
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ namespace jit {
|
|||
_(InCache) \
|
||||
_(HasOwnCache) \
|
||||
_(InstanceOf) \
|
||||
_(CallInstanceOf) \
|
||||
_(InstanceOfCache) \
|
||||
_(InterruptCheck) \
|
||||
_(GetDOMProperty) \
|
||||
_(GetDOMMember) \
|
||||
|
|
|
@ -875,6 +875,8 @@ MacroAssembler::allocateObject(Register result, Register temp, gc::AllocKind all
|
|||
if (!nDynamicSlots)
|
||||
return freeListAllocate(result, temp, allocKind, fail);
|
||||
|
||||
// Only NativeObject can have nDynamicSlots > 0 and reach here.
|
||||
|
||||
callMallocStub(nDynamicSlots * sizeof(GCPtrValue), temp, fail);
|
||||
|
||||
Label failAlloc;
|
||||
|
@ -1205,8 +1207,8 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
|
|||
|
||||
storePtr(ImmGCPtr(templateObj->group()), Address(obj, JSObject::offsetOfGroup()));
|
||||
|
||||
if (Shape* shape = templateObj->maybeShape())
|
||||
storePtr(ImmGCPtr(shape), Address(obj, ShapedObject::offsetOfShape()));
|
||||
if (templateObj->is<ShapedObject>())
|
||||
storePtr(ImmGCPtr(templateObj->maybeShape()), Address(obj, ShapedObject::offsetOfShape()));
|
||||
|
||||
MOZ_ASSERT_IF(convertDoubleElements, templateObj->is<ArrayObject>());
|
||||
|
||||
|
@ -1284,7 +1286,8 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
|
|||
offset += sizeof(uintptr_t);
|
||||
}
|
||||
} else if (templateObj->is<UnboxedPlainObject>()) {
|
||||
storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
|
||||
MOZ_ASSERT(!templateObj->as<UnboxedPlainObject>().maybeExpando());
|
||||
storePtr(ImmPtr(nullptr), Address(obj, UnboxedPlainObject::offsetOfExpando()));
|
||||
if (initContents)
|
||||
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
|
||||
} else {
|
||||
|
|
|
@ -297,12 +297,6 @@ ICStub::trace(JSTracer* trc)
|
|||
TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
|
||||
break;
|
||||
}
|
||||
case ICStub::InstanceOf_Function: {
|
||||
ICInstanceOf_Function* instanceofStub = toInstanceOf_Function();
|
||||
TraceEdge(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape");
|
||||
TraceEdge(trc, &instanceofStub->prototypeObject(), "baseline-instanceof-fun-prototype");
|
||||
break;
|
||||
}
|
||||
case ICStub::NewArray_Fallback: {
|
||||
ICNewArray_Fallback* stub = toNewArray_Fallback();
|
||||
TraceNullableEdge(trc, &stub->templateObject(), "baseline-newarray-template");
|
||||
|
|
|
@ -328,9 +328,16 @@ template <typename L>
|
|||
void
|
||||
MacroAssemblerMIPSShared::ma_addTestCarry(Register rd, Register rs, Register rt, L overflow)
|
||||
{
|
||||
as_addu(rd, rs, rt);
|
||||
as_sltu(SecondScratchReg, rd, rs);
|
||||
ma_b(SecondScratchReg, SecondScratchReg, overflow, Assembler::NonZero);
|
||||
if (rd != rs) {
|
||||
as_addu(rd, rs, rt);
|
||||
as_sltu(SecondScratchReg, rd, rs);
|
||||
ma_b(SecondScratchReg, SecondScratchReg, overflow, Assembler::NonZero);
|
||||
} else {
|
||||
ma_move(SecondScratchReg, rs);
|
||||
as_addu(rd, rs, rt);
|
||||
as_sltu(SecondScratchReg, rd, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, SecondScratchReg, overflow, Assembler::NonZero);
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
|
@ -709,7 +716,7 @@ MacroAssemblerMIPSShared::ma_store_unaligned(const wasm::MemoryAccessDesc& acces
|
|||
int16_t lowOffset, hiOffset;
|
||||
Register base;
|
||||
|
||||
asMasm().computeEffectiveAddress(dest, SecondScratchReg);
|
||||
asMasm().computeScaledAddress(dest, SecondScratchReg);
|
||||
|
||||
if (Imm16::IsInSignedRange(dest.offset) && Imm16::IsInSignedRange(dest.offset + size / 8 - 1)) {
|
||||
base = SecondScratchReg;
|
||||
|
|
|
@ -65,8 +65,6 @@ struct EnterJITRegs
|
|||
static void
|
||||
GenerateReturn(MacroAssembler& masm, int returnCode)
|
||||
{
|
||||
MOZ_ASSERT(masm.framePushed() == sizeof(EnterJITRegs));
|
||||
|
||||
if (isLoongson()) {
|
||||
// Restore non-volatile registers
|
||||
masm.as_ld(s0, StackPointer, offsetof(EnterJITRegs, s0));
|
||||
|
|
|
@ -8016,11 +8016,11 @@ class LInstanceOfV : public LInstructionHelper<1, BOX_PIECES, 0>
|
|||
static const size_t LHS = 0;
|
||||
};
|
||||
|
||||
class LCallInstanceOf : public LCallInstructionHelper<1, BOX_PIECES+1, 0>
|
||||
class LInstanceOfCache : public LInstructionHelper<1, BOX_PIECES+1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CallInstanceOf)
|
||||
LCallInstanceOf(const LBoxAllocation& lhs, const LAllocation& rhs) {
|
||||
LIR_HEADER(InstanceOfCache)
|
||||
LInstanceOfCache(const LBoxAllocation& lhs, const LAllocation& rhs) {
|
||||
setBoxOperand(LHS, lhs);
|
||||
setOperand(RHS, rhs);
|
||||
}
|
||||
|
|
|
@ -383,7 +383,7 @@
|
|||
_(HasOwnCache) \
|
||||
_(InstanceOfO) \
|
||||
_(InstanceOfV) \
|
||||
_(CallInstanceOf) \
|
||||
_(InstanceOfCache) \
|
||||
_(InterruptCheck) \
|
||||
_(Rotate) \
|
||||
_(RotateI64) \
|
||||
|
|
|
@ -3991,6 +3991,7 @@ JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOp
|
|||
canLazilyParse = rhs.canLazilyParse;
|
||||
strictOption = rhs.strictOption;
|
||||
extraWarningsOption = rhs.extraWarningsOption;
|
||||
expressionClosuresOption = rhs.expressionClosuresOption;
|
||||
werrorOption = rhs.werrorOption;
|
||||
asmJSOption = rhs.asmJSOption;
|
||||
throwOnAsmJSValidationFailureOption = rhs.throwOnAsmJSValidationFailureOption;
|
||||
|
@ -4112,6 +4113,7 @@ JS::CompileOptions::CompileOptions(JSContext* cx)
|
|||
{
|
||||
strictOption = cx->options().strictMode();
|
||||
extraWarningsOption = cx->compartment()->behaviors().extraWarnings(cx);
|
||||
expressionClosuresOption = cx->options().expressionClosures();
|
||||
isProbablySystemOrAddonCode = cx->compartment()->isProbablySystemOrAddonCode();
|
||||
werrorOption = cx->options().werror();
|
||||
if (!cx->options().asmJS())
|
||||
|
|
|
@ -1117,6 +1117,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
|||
#ifdef FUZZING
|
||||
, fuzzing_(false)
|
||||
#endif
|
||||
, expressionClosures_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1272,6 +1273,12 @@ class JS_PUBLIC_API(ContextOptions) {
|
|||
}
|
||||
#endif
|
||||
|
||||
bool expressionClosures() const { return expressionClosures_; }
|
||||
ContextOptions& setExpressionClosures(bool flag) {
|
||||
expressionClosures_ = flag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void disableOptionsForSafeMode() {
|
||||
setBaseline(false);
|
||||
setIon(false);
|
||||
|
@ -1302,6 +1309,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
|||
#ifdef FUZZING
|
||||
bool fuzzing_ : 1;
|
||||
#endif
|
||||
bool expressionClosures_ : 1;
|
||||
|
||||
};
|
||||
|
||||
|
@ -3668,6 +3676,7 @@ class JS_FRIEND_API(TransitiveCompileOptions)
|
|||
canLazilyParse(true),
|
||||
strictOption(false),
|
||||
extraWarningsOption(false),
|
||||
expressionClosuresOption(false),
|
||||
werrorOption(false),
|
||||
asmJSOption(AsmJSOption::Disabled),
|
||||
throwOnAsmJSValidationFailureOption(false),
|
||||
|
@ -3702,6 +3711,7 @@ class JS_FRIEND_API(TransitiveCompileOptions)
|
|||
bool canLazilyParse;
|
||||
bool strictOption;
|
||||
bool extraWarningsOption;
|
||||
bool expressionClosuresOption;
|
||||
bool werrorOption;
|
||||
AsmJSOption asmJSOption;
|
||||
bool throwOnAsmJSValidationFailureOption;
|
||||
|
|
|
@ -132,7 +132,7 @@ class PropertyIteratorObject : public NativeObject
|
|||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
};
|
||||
|
||||
class ArrayIteratorObject : public JSObject
|
||||
class ArrayIteratorObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
@ -141,7 +141,7 @@ class ArrayIteratorObject : public JSObject
|
|||
ArrayIteratorObject*
|
||||
NewArrayIteratorObject(JSContext* cx, NewObjectKind newKind = GenericObject);
|
||||
|
||||
class StringIteratorObject : public JSObject
|
||||
class StringIteratorObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
|
|
@ -128,6 +128,10 @@ class JSObject : public js::gc::Cell
|
|||
return group_;
|
||||
}
|
||||
|
||||
void initGroup(js::ObjectGroup* group) {
|
||||
group_.init(group);
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether this is the only object which has its specified group. This
|
||||
* object will have its group constructed lazily as needed by analysis.
|
||||
|
@ -150,13 +154,6 @@ class JSObject : public js::gc::Cell
|
|||
inline js::Shape* maybeShape() const;
|
||||
inline js::Shape* ensureShape(JSContext* cx);
|
||||
|
||||
// Set the initial slots and elements of an object. These pointers are only
|
||||
// valid for native objects, but during initialization are set for all
|
||||
// objects. For non-native objects, these must not be dynamically allocated
|
||||
// pointers which leak when the non-native object finishes initialization.
|
||||
inline void setInitialSlotsMaybeNonNative(js::HeapSlot* slots);
|
||||
inline void setInitialElementsMaybeNonNative(js::HeapSlot* elements);
|
||||
|
||||
enum GenerateShape {
|
||||
GENERATE_NONE,
|
||||
GENERATE_SHAPE
|
||||
|
|
|
@ -397,18 +397,6 @@ SetNewObjectMetadata(JSContext* cx, T* obj)
|
|||
|
||||
} // namespace js
|
||||
|
||||
inline void
|
||||
JSObject::setInitialSlotsMaybeNonNative(js::HeapSlot* slots)
|
||||
{
|
||||
static_cast<js::NativeObject*>(this)->slots_ = slots;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::setInitialElementsMaybeNonNative(js::HeapSlot* elements)
|
||||
{
|
||||
static_cast<js::NativeObject*>(this)->elements_ = elements;
|
||||
}
|
||||
|
||||
inline js::GlobalObject&
|
||||
JSObject::global() const
|
||||
{
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
|
||||
#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */
|
||||
|
||||
#ifndef NIGHTLY_BUILD
|
||||
#define JS_HAS_EXPR_CLOSURES 1 /* has function (formals) listexpr */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Feature for Object.prototype.__{define,lookup}{G,S}etter__ legacy support;
|
||||
* support likely to be made opt-in at some future time.
|
||||
|
|
|
@ -394,7 +394,8 @@ RCFile::release()
|
|||
return true;
|
||||
}
|
||||
|
||||
class FileObject : public JSObject {
|
||||
class FileObject : public NativeObject
|
||||
{
|
||||
enum : uint32_t {
|
||||
FILE_SLOT = 0,
|
||||
NUM_SLOTS
|
||||
|
|
|
@ -259,9 +259,6 @@ class JitTest:
|
|||
elif name == 'test-also-wasm-tiering':
|
||||
if options.wasm_enabled:
|
||||
test.test_also.append(['--test-wasm-await-tier2'])
|
||||
elif name == 'test-also-wasm-check-bce':
|
||||
if options.wasm_enabled:
|
||||
test.test_also.append(['--wasm-check-bce'])
|
||||
elif name.startswith('test-also='):
|
||||
test.test_also.append([name[len('test-also='):]])
|
||||
elif name.startswith('test-join='):
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell) -- needs getBuildConfiguration
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
/* 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/. */
|
||||
|
@ -8,34 +8,33 @@
|
|||
// closures, where subsequent token-examination must use the Operand modifier
|
||||
// to avoid an assertion.
|
||||
|
||||
if (getBuildConfiguration().release_or_beta)
|
||||
{
|
||||
eval(`
|
||||
var ec1 = function() 0 ? 1 : a => {};
|
||||
assertEq(typeof ec1, "function");
|
||||
assertEq(typeof ec1(), "function");
|
||||
assertEq(ec1()(), undefined);
|
||||
|
||||
var ec2 = function() 0 ? 1 : a => {} // deliberately exercise ASI here
|
||||
assertEq(typeof ec2, "function");
|
||||
assertEq(typeof ec2(), "function");
|
||||
assertEq(ec2()(), undefined);
|
||||
enableExpressionClosures();
|
||||
eval(`
|
||||
var ec1 = function() 0 ? 1 : a => {};
|
||||
assertEq(typeof ec1, "function");
|
||||
assertEq(typeof ec1(), "function");
|
||||
assertEq(ec1()(), undefined);
|
||||
|
||||
function ec3() 0 ? 1 : a => {} // exercise ASI here
|
||||
assertEq(typeof ec3(), "function");
|
||||
var ec2 = function() 0 ? 1 : a => {} // deliberately exercise ASI here
|
||||
assertEq(typeof ec2, "function");
|
||||
assertEq(typeof ec2(), "function");
|
||||
assertEq(ec2()(), undefined);
|
||||
|
||||
function ec4() 0 ? 1 : a => {};
|
||||
assertEq(typeof ec4(), "function");
|
||||
function ec3() 0 ? 1 : a => {} // exercise ASI here
|
||||
assertEq(typeof ec3(), "function");
|
||||
|
||||
var needle = "@";
|
||||
var x = 42;
|
||||
var g = { test() { assertEq(true, false, "shouldn't be called"); } };
|
||||
function ec4() 0 ? 1 : a => {};
|
||||
assertEq(typeof ec4(), "function");
|
||||
|
||||
function ec5() 0 ? 1 : a => {} // ASI
|
||||
/x/g.test((needle = "x"));
|
||||
assertEq(needle, "x");
|
||||
`);
|
||||
}
|
||||
var needle = "@";
|
||||
var x = 42;
|
||||
var g = { test() { assertEq(true, false, "shouldn't be called"); } };
|
||||
|
||||
function ec5() 0 ? 1 : a => {} // ASI
|
||||
/x/g.test((needle = "x"));
|
||||
assertEq(needle, "x");
|
||||
`);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// |reftest| skip-if(!release_or_beta)
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
|
@ -8,6 +8,9 @@
|
|||
|
||||
var summary = "Flat expression closure source coordinate fencepost test";
|
||||
|
||||
enableExpressionClosures();
|
||||
|
||||
eval(`
|
||||
function f(a) {
|
||||
if (a) {
|
||||
let b = 42;
|
||||
|
@ -20,5 +23,6 @@ function f(a) {
|
|||
|
||||
var expect = 44;
|
||||
var actual = f(1)();
|
||||
`);
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// |reftest| skip-if(!release_or_beta)
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
|
@ -8,6 +8,9 @@
|
|||
|
||||
var summary = "Partial flat expression closure upvar order test";
|
||||
|
||||
enableExpressionClosures();
|
||||
|
||||
eval(`
|
||||
function f(a) {
|
||||
if (a) {
|
||||
let b = 42;
|
||||
|
@ -20,5 +23,6 @@ function f(a) {
|
|||
|
||||
var expect = 44;
|
||||
var actual = f(1)();
|
||||
`);
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell||!release_or_beta)
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
function test() {
|
||||
|
||||
// expression closures
|
||||
|
||||
enableExpressionClosures();
|
||||
|
||||
assertDecl("function inc(x) (x + 1)", funDecl(ident("inc"), [ident("x")], binExpr("+", ident("x"), lit(1))));
|
||||
assertExpr("(function(x) (x+1))", funExpr(null, [ident("x")], binExpr("+", ident("x"), lit(1))));
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// |reftest| skip-if(!release_or_beta)
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 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
|
||||
|
@ -20,6 +20,8 @@ function test()
|
|||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
enableExpressionClosures();
|
||||
|
||||
expect = 'SyntaxError: unexpected token: identifier';
|
||||
try
|
||||
{
|
||||
|
|
|
@ -435,7 +435,7 @@ bool CreateWasmBuffer(JSContext* cx, const wasm::Limits& memory,
|
|||
* Common definitions shared by all array buffer views.
|
||||
*/
|
||||
|
||||
class ArrayBufferViewObject : public JSObject
|
||||
class ArrayBufferViewObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static ArrayBufferObjectMaybeShared* bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*> obj);
|
||||
|
|
|
@ -37,29 +37,34 @@ ArrayObject::createArrayInternal(JSContext* cx, gc::AllocKind kind, gc::InitialH
|
|||
HandleShape shape, HandleObjectGroup group,
|
||||
AutoSetNewObjectMetadata&)
|
||||
{
|
||||
// Create a new array and initialize everything except for its elements.
|
||||
const js::Class* clasp = group->clasp();
|
||||
MOZ_ASSERT(shape && group);
|
||||
MOZ_ASSERT(group->clasp() == shape->getObjectClass());
|
||||
MOZ_ASSERT(group->clasp() == &ArrayObject::class_);
|
||||
MOZ_ASSERT_IF(group->clasp()->hasFinalize(), heap == gc::TenuredHeap);
|
||||
MOZ_ASSERT(clasp == shape->getObjectClass());
|
||||
MOZ_ASSERT(clasp == &ArrayObject::class_);
|
||||
MOZ_ASSERT_IF(clasp->hasFinalize(), heap == gc::TenuredHeap);
|
||||
MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(),
|
||||
heap == js::gc::TenuredHeap);
|
||||
MOZ_ASSERT(group->clasp()->shouldDelayMetadataBuilder());
|
||||
|
||||
// Arrays can use their fixed slots to store elements, so can't have shapes
|
||||
// which allow named properties to be stored in the fixed slots.
|
||||
MOZ_ASSERT(shape->numFixedSlots() == 0);
|
||||
|
||||
size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), group->clasp());
|
||||
JSObject* obj = Allocate<JSObject>(cx, kind, nDynamicSlots, heap, group->clasp());
|
||||
size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), clasp);
|
||||
JSObject* obj = js::Allocate<JSObject>(cx, kind, nDynamicSlots, heap, clasp);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
static_cast<ArrayObject*>(obj)->shape_.init(shape);
|
||||
static_cast<ArrayObject*>(obj)->group_.init(group);
|
||||
ArrayObject* aobj = static_cast<ArrayObject*>(obj);
|
||||
aobj->initGroup(group);
|
||||
aobj->initShape(shape);
|
||||
// NOTE: Dynamic slots are created internally by Allocate<JSObject>.
|
||||
if (!nDynamicSlots)
|
||||
aobj->initSlots(nullptr);
|
||||
|
||||
cx->compartment()->setObjectPendingMetadata(cx, obj);
|
||||
return &obj->as<ArrayObject>();
|
||||
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
|
||||
cx->compartment()->setObjectPendingMetadata(cx, aobj);
|
||||
|
||||
return aobj;
|
||||
}
|
||||
|
||||
/* static */ inline ArrayObject*
|
||||
|
|
|
@ -60,7 +60,8 @@ NewObjectCache::newObjectFromHit(JSContext* cx, EntryIndex entryIndex, gc::Initi
|
|||
if (cx->runtime()->gc.upcomingZealousGC())
|
||||
return nullptr;
|
||||
|
||||
NativeObject* obj = static_cast<NativeObject*>(Allocate<JSObject, NoGC>(cx, entry->kind, 0,
|
||||
NativeObject* obj = static_cast<NativeObject*>(Allocate<JSObject, NoGC>(cx, entry->kind,
|
||||
/* nDynamicSlots = */ 0,
|
||||
heap, group->clasp()));
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "gc/Tracer.h"
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/UniquePtr.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -200,6 +201,9 @@ class NewObjectCache
|
|||
MOZ_ASSERT(entry_ == makeIndex(clasp, key, kind));
|
||||
Entry* entry = &entries[entry_];
|
||||
|
||||
MOZ_ASSERT(!obj->hasDynamicSlots());
|
||||
MOZ_ASSERT(obj->hasEmptyElements() || obj->is<ArrayObject>());
|
||||
|
||||
entry->clasp = clasp;
|
||||
entry->key = key;
|
||||
entry->kind = kind;
|
||||
|
|
|
@ -542,14 +542,15 @@ NativeObject::create(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap
|
|||
return cx->alreadyReportedOOM();
|
||||
|
||||
NativeObject* nobj = static_cast<NativeObject*>(obj);
|
||||
nobj->group_.init(group);
|
||||
nobj->initGroup(group);
|
||||
nobj->initShape(shape);
|
||||
|
||||
// Note: slots are created and assigned internally by Allocate<JSObject>.
|
||||
nobj->setInitialElementsMaybeNonNative(js::emptyObjectElements);
|
||||
// NOTE: Dynamic slots are created internally by Allocate<JSObject>.
|
||||
if (!nDynamicSlots)
|
||||
nobj->initSlots(nullptr);
|
||||
nobj->setEmptyElements();
|
||||
|
||||
if (clasp->hasPrivate())
|
||||
nobj->privateRef(shape->numFixedSlots()) = nullptr;
|
||||
nobj->initPrivate(nullptr);
|
||||
|
||||
if (size_t span = shape->slotSpan())
|
||||
nobj->initializeSlotRange(0, span);
|
||||
|
|
|
@ -680,6 +680,12 @@ class NativeObject : public ShapedObject
|
|||
}
|
||||
|
||||
public:
|
||||
|
||||
/* Object allocation may directly initialize slots so this is public. */
|
||||
void initSlots(HeapSlot* slots) {
|
||||
slots_ = slots;
|
||||
}
|
||||
|
||||
static MOZ_MUST_USE bool generateOwnShape(JSContext* cx, HandleNativeObject obj,
|
||||
Shape* newShape = nullptr)
|
||||
{
|
||||
|
@ -1322,6 +1328,10 @@ class NativeObject : public ShapedObject
|
|||
bool canHaveNonEmptyElements();
|
||||
#endif
|
||||
|
||||
void setEmptyElements() {
|
||||
elements_ = emptyObjectElements;
|
||||
}
|
||||
|
||||
void setFixedElements(uint32_t numShifted = 0) {
|
||||
MOZ_ASSERT(canHaveNonEmptyElements());
|
||||
elements_ = fixedElements() + numShifted;
|
||||
|
|
|
@ -182,12 +182,12 @@ ProxyObject::create(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto
|
|||
gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
|
||||
debugCheckNewObject(group, shape, allocKind, heap);
|
||||
|
||||
JSObject* obj = js::Allocate<JSObject>(cx, allocKind, /* numDynamicSlots = */ 0, heap, clasp);
|
||||
JSObject* obj = js::Allocate<JSObject>(cx, allocKind, /* nDynamicSlots = */ 0, heap, clasp);
|
||||
if (!obj)
|
||||
return cx->alreadyReportedOOM();
|
||||
|
||||
ProxyObject* pobj = static_cast<ProxyObject*>(obj);
|
||||
pobj->group_.init(group);
|
||||
pobj->initGroup(group);
|
||||
pobj->initShape(shape);
|
||||
|
||||
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
|
||||
|
|
|
@ -627,12 +627,10 @@ UnboxedObject::createInternal(JSContext* cx, js::gc::AllocKind kind, js::gc::Ini
|
|||
return cx->alreadyReportedOOM();
|
||||
|
||||
UnboxedObject* uobj = static_cast<UnboxedObject*>(obj);
|
||||
uobj->group_.init(group);
|
||||
uobj->initGroup(group);
|
||||
|
||||
if (clasp->shouldDelayMetadataBuilder())
|
||||
cx->compartment()->setObjectPendingMetadata(cx, uobj);
|
||||
else
|
||||
uobj = SetNewObjectMetadata(cx, uobj);
|
||||
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
|
||||
cx->compartment()->setObjectPendingMetadata(cx, uobj);
|
||||
|
||||
js::gc::TraceCreateObject(uobj);
|
||||
|
||||
|
@ -651,18 +649,17 @@ UnboxedPlainObject::create(JSContext* cx, HandleObjectGroup group, NewObjectKind
|
|||
|
||||
MOZ_ASSERT(newKind != SingletonObject);
|
||||
|
||||
UnboxedObject* res_;
|
||||
JS_TRY_VAR_OR_RETURN_NULL(cx, res_, createInternal(cx, allocKind, heap, group));
|
||||
UnboxedPlainObject* res = &res_->as<UnboxedPlainObject>();
|
||||
JSObject* obj;
|
||||
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, createInternal(cx, allocKind, heap, group));
|
||||
|
||||
// Overwrite the dummy shape which was written to the object's expando field.
|
||||
res->initExpando();
|
||||
UnboxedPlainObject* uobj = static_cast<UnboxedPlainObject*>(obj);
|
||||
uobj->initExpando();
|
||||
|
||||
// Initialize reference fields of the object. All fields in the object will
|
||||
// be overwritten shortly, but references need to be safe for the GC.
|
||||
const int32_t* list = res->layout().traceList();
|
||||
const int32_t* list = uobj->layout().traceList();
|
||||
if (list) {
|
||||
uint8_t* data = res->data();
|
||||
uint8_t* data = uobj->data();
|
||||
while (*list != -1) {
|
||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
|
||||
heap->init(cx->names().empty);
|
||||
|
@ -678,7 +675,7 @@ UnboxedPlainObject::create(JSContext* cx, HandleObjectGroup group, NewObjectKind
|
|||
MOZ_ASSERT(*(list + 1) == -1);
|
||||
}
|
||||
|
||||
return res;
|
||||
return uobj;
|
||||
}
|
||||
|
||||
/* static */ JSObject*
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "brotli/decode.h"
|
||||
#include "zlib.h"
|
||||
#include "mozilla/dom/FontFaceSet.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -189,8 +190,19 @@ InspectorFontFace::GetMetadata(nsAString& aMetadata)
|
|||
}
|
||||
}
|
||||
|
||||
// Append an OpenType tag to a string as a 4-ASCII-character code.
|
||||
static void
|
||||
AppendTagAsASCII(nsAString& aString, uint32_t aTag)
|
||||
{
|
||||
aString.AppendPrintf("%c%c%c%c", (aTag >> 24) & 0xff,
|
||||
(aTag >> 16) & 0xff,
|
||||
(aTag >> 8) & 0xff,
|
||||
aTag & 0xff);
|
||||
}
|
||||
|
||||
void
|
||||
InspectorFontFace::GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult)
|
||||
InspectorFontFace::GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult,
|
||||
ErrorResult& aRV)
|
||||
{
|
||||
if (!mFontEntry->HasVariations()) {
|
||||
return;
|
||||
|
@ -198,15 +210,13 @@ InspectorFontFace::GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult)
|
|||
AutoTArray<gfxFontVariationAxis,4> axes;
|
||||
mFontEntry->GetVariationAxes(axes);
|
||||
MOZ_ASSERT(!axes.IsEmpty());
|
||||
if (!aResult.SetCapacity(axes.Length(), mozilla::fallible)) {
|
||||
aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
for (auto a : axes) {
|
||||
// Turn the uint32_t OpenType tag into a 4-ASCII-character string
|
||||
nsAutoStringN<4> tag;
|
||||
tag.AppendPrintf("%c%c%c%c", (a.mTag >> 24) & 0xff,
|
||||
(a.mTag >> 16) & 0xff,
|
||||
(a.mTag >> 8) & 0xff,
|
||||
a.mTag & 0xff);
|
||||
InspectorVariationAxis& axis = *aResult.AppendElement();
|
||||
axis.mTag = tag;
|
||||
AppendTagAsASCII(axis.mTag, a.mTag);
|
||||
axis.mName = a.mName;
|
||||
axis.mMinValue = a.mMinValue;
|
||||
axis.mMaxValue = a.mMaxValue;
|
||||
|
@ -214,5 +224,40 @@ InspectorFontFace::GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
InspectorFontFace::GetVariationInstances(
|
||||
nsTArray<InspectorVariationInstance>& aResult,
|
||||
ErrorResult& aRV)
|
||||
{
|
||||
if (!mFontEntry->HasVariations()) {
|
||||
return;
|
||||
}
|
||||
AutoTArray<gfxFontVariationInstance,16> instances;
|
||||
mFontEntry->GetVariationInstances(instances);
|
||||
if (!aResult.SetCapacity(instances.Length(), mozilla::fallible)) {
|
||||
aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
for (auto i : instances) {
|
||||
InspectorVariationInstance& inst = *aResult.AppendElement();
|
||||
inst.mName = i.mName;
|
||||
// inst.mValues is a webidl sequence<>, which is a fallible array,
|
||||
// so we are required to use fallible SetCapacity and AppendElement calls,
|
||||
// and check the result. In practice we don't expect failure here; the
|
||||
// list of values cannot get huge because of limits in the font format.
|
||||
if (!inst.mValues.SetCapacity(i.mValues.Length(), mozilla::fallible)) {
|
||||
aRV.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
for (auto v : i.mValues) {
|
||||
InspectorVariationValue value;
|
||||
AppendTagAsASCII(value.mAxis, v.mAxis);
|
||||
value.mValue = v.mValue;
|
||||
// This won't fail, because of SetCapacity above.
|
||||
Unused << inst.mValues.AppendElement(value, mozilla::fallible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -55,7 +55,10 @@ public:
|
|||
void GetFormat(nsAString& aFormat);
|
||||
void GetMetadata(nsAString& aMetadata);
|
||||
|
||||
void GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult);
|
||||
void GetVariationAxes(nsTArray<InspectorVariationAxis>& aResult,
|
||||
ErrorResult& aRV);
|
||||
void GetVariationInstances(nsTArray<InspectorVariationInstance>& aResult,
|
||||
ErrorResult& aRV);
|
||||
|
||||
bool WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto,
|
||||
|
|
|
@ -12,6 +12,103 @@
|
|||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Which platform are we on?
|
||||
const kIsLinux = navigator.platform.indexOf("Linux") == 0;
|
||||
const kIsMac = navigator.platform.indexOf("Mac") == 0;
|
||||
const kIsWin = navigator.platform.indexOf("Win") == 0;
|
||||
|
||||
// If it's an older macOS version (pre-Sierra), we don't expect the
|
||||
// @font-face examples to work, so just skip them.
|
||||
const kIsOldMac = navigator.oscpu.substr(navigator.oscpu.lastIndexOf(".") + 1) < 12;
|
||||
|
||||
// We allow for some "fuzz" on axis values, given the conversions between
|
||||
// fixed-point and floating-point representations.
|
||||
const kEpsilon = 0.001;
|
||||
|
||||
|
||||
const LibreFranklinAxes = [
|
||||
{ tag: "wght", name: "Weight", minValue: 40, maxValue: 200, defaultValue: 40 },
|
||||
];
|
||||
|
||||
const GinghamAxes = [
|
||||
{ tag: "wght", name: "Weight", minValue: 300, maxValue: 700, defaultValue: 300 },
|
||||
{ tag: "wdth", name: "Width", minValue: 1, maxValue: 150, defaultValue: 1 },
|
||||
];
|
||||
|
||||
const SkiaAxes = [
|
||||
{ tag: "wght", name: "Weight", minValue: 0.48, maxValue: 3.2, defaultValue: 1.0 },
|
||||
{ tag: "wdth", name: "Width", minValue: 0.62, maxValue: 1.3, defaultValue: 1.0 },
|
||||
];
|
||||
|
||||
function checkAxes(axes, reference) {
|
||||
is(axes.length, reference.length, "number of variation axes");
|
||||
for (var i = 0; i < Math.min(axes.length, reference.length); ++i) {
|
||||
var v = axes[i];
|
||||
var ref = reference[i];
|
||||
is(v.tag, ref.tag, "axis tag");
|
||||
is(v.name, ref.name, "axis name");
|
||||
isfuzzy(v.minValue, ref.minValue, kEpsilon, "minimum value");
|
||||
isfuzzy(v.maxValue, ref.maxValue, kEpsilon, "maximum value");
|
||||
is(v.defaultValue, ref.defaultValue, "default value");
|
||||
}
|
||||
}
|
||||
|
||||
// Expected variation instances for each of our test fonts, sorted by name
|
||||
const LibreFranklinInstances = [
|
||||
{ name: "Black", values: [ { axis: "wght", value: 200 } ] },
|
||||
{ name: "Bold", values: [ { axis: "wght", value: 154 } ] },
|
||||
{ name: "ExtraBold", values: [ { axis: "wght", value: 178 } ] },
|
||||
{ name: "ExtraLight", values: [ { axis: "wght", value: 50 } ] },
|
||||
{ name: "Light", values: [ { axis: "wght", value: 66 } ] },
|
||||
{ name: "Medium", values: [ { axis: "wght", value: 106 } ] },
|
||||
{ name: "Regular", values: [ { axis: "wght", value: 84 } ] },
|
||||
{ name: "SemiBold", values: [ { axis: "wght", value: 130 } ] },
|
||||
{ name: "Thin", values: [ { axis: "wght", value: 40 } ] },
|
||||
];
|
||||
|
||||
const GinghamInstances = [
|
||||
{ name: "Bold", values: [ { axis: "wght", value: 700 }, { axis: "wdth", value: 100 } ] },
|
||||
{ name: "Condensed Bold", values: [ { axis: "wght", value: 700 }, { axis: "wdth", value: 1 } ] },
|
||||
{ name: "Condensed Light", values: [ { axis: "wght", value: 300 }, { axis: "wdth", value: 1 } ] },
|
||||
{ name: "Condensed Regular", values: [ { axis: "wght", value: 400 }, { axis: "wdth", value: 1 } ] },
|
||||
{ name: "Light", values: [ { axis: "wght", value: 300 }, { axis: "wdth", value: 100 } ] },
|
||||
{ name: "Regular", values: [ { axis: "wght", value: 400 }, { axis: "wdth", value: 100 } ] },
|
||||
{ name: "Wide Bold", values: [ { axis: "wght", value: 700 }, { axis: "wdth", value: 150 } ] },
|
||||
{ name: "Wide Light", values: [ { axis: "wght", value: 300 }, { axis: "wdth", value: 150 } ] },
|
||||
{ name: "Wide Regular", values: [ { axis: "wght", value: 400 }, { axis: "wdth", value: 150 } ] },
|
||||
];
|
||||
|
||||
const SkiaInstances = [
|
||||
{ name: "Black", values: [ { axis: "wght", value: 3.2 }, { axis: "wdth", value: 1 } ] },
|
||||
{ name: "Black Condensed", values: [ { axis: "wght", value: 3 }, { axis: "wdth", value: 0.7 } ] },
|
||||
{ name: "Black Extended", values: [ { axis: "wght", value: 3.2 }, { axis: "wdth", value: 1.3 } ] },
|
||||
{ name: "Bold", values: [ { axis: "wght", value: 1.95 }, { axis: "wdth", value: 1 } ] },
|
||||
{ name: "Condensed", values: [ { axis: "wght", value: 1 }, { axis: "wdth", value: 0.62 } ] },
|
||||
{ name: "Extended", values: [ { axis: "wght", value: 1 }, { axis: "wdth", value: 1.3 } ] },
|
||||
{ name: "Light", values: [ { axis: "wght", value: 0.48 }, { axis: "wdth", value: 1 } ] },
|
||||
{ name: "Light Condensed", values: [ { axis: "wght", value: 0.48 }, { axis: "wdth", value: 0.7 } ] },
|
||||
{ name: "Light Extended", values: [ { axis: "wght", value: 0.48 }, { axis: "wdth", value: 1.3 } ] },
|
||||
{ name: "Regular", values: [ { axis: "wght", value: 1 }, { axis: "wdth", value: 1 } ] },
|
||||
];
|
||||
|
||||
function checkInstances(instances, reference) {
|
||||
is(instances.length, reference.length, "number of variation instances");
|
||||
instances.sort(function (a, b) {
|
||||
return a.name.localeCompare(b.name, "en-US");
|
||||
});
|
||||
for (var i = 0; i < Math.min(instances.length, reference.length); ++i) {
|
||||
var ref = reference[i];
|
||||
var inst = instances[i];
|
||||
is(inst.name, ref.name, "instance name");
|
||||
is(inst.values.length, ref.values.length, "number of values");
|
||||
for (var j = 0; j < Math.min(inst.values.length, ref.values.length); ++j) {
|
||||
var v = inst.values[j];
|
||||
is(v.axis, ref.values[j].axis, "axis");
|
||||
isfuzzy(v.value, ref.values[j].value, kEpsilon, "value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function RunTest() {
|
||||
// Ensure we're running with font-variations enabled
|
||||
SpecialPowers.pushPrefEnv(
|
||||
|
@ -19,33 +116,17 @@ function RunTest() {
|
|||
['gfx.downloadable_fonts.keep_variation_tables', true],
|
||||
['gfx.downloadable_fonts.otl_validation', false]]},
|
||||
function() {
|
||||
// Which platform are we on?
|
||||
const kIsLinux = navigator.platform.indexOf("Linux") == 0;
|
||||
const kIsMac = navigator.platform.indexOf("Mac") == 0;
|
||||
const kIsWin = navigator.platform.indexOf("Win") == 0;
|
||||
|
||||
// Until bug 1433098 is fixed, Windows fails to return axis names
|
||||
const nameTest = kIsWin ? todo : is;
|
||||
|
||||
// If it's an older macOS version (pre-Sierra), we don't expect the
|
||||
// @font-face examples to work, so just skip them.
|
||||
const kIsOldMac = navigator.oscpu.substr(navigator.oscpu.lastIndexOf(".") + 1) < 12;
|
||||
|
||||
var rng = document.createRange();
|
||||
var elem, fonts, f;
|
||||
|
||||
// We allow for some "fuzz" on non-integer axis values, given the
|
||||
// conversions between fixed-point and floating-point representations.
|
||||
const kEpsilon = 0.001;
|
||||
|
||||
// First test element uses Arial, which has no variations
|
||||
// First test element uses Arial (or a substitute), which has no variations
|
||||
elem = document.getElementById("test1");
|
||||
rng.selectNode(elem);
|
||||
fonts = InspectorUtils.getUsedFontFaces(rng);
|
||||
is(fonts.length, 1, "number of fonts: " + fonts.length);
|
||||
is(fonts.length, 1, "number of fonts");
|
||||
f = fonts[0];
|
||||
var axes = f.getVariationAxes();
|
||||
is(axes.length, 0, "no variations");
|
||||
is(f.getVariationAxes().length, 0, "no variations");
|
||||
is(f.getVariationInstances().length, 0, "no instances");
|
||||
|
||||
if (!(kIsMac && kIsOldMac)) {
|
||||
// Libre Franklin font should have a single axis: Weight.
|
||||
|
@ -53,25 +134,11 @@ function RunTest() {
|
|||
elem.style.display = "block";
|
||||
rng.selectNode(elem);
|
||||
fonts = InspectorUtils.getUsedFontFaces(rng);
|
||||
is(fonts.length, 1, "number of fonts: " + fonts.length);
|
||||
is(fonts.length, 1, "number of fonts");
|
||||
f = fonts[0];
|
||||
is(f.name, "Libre Franklin", "font name: " + f.name);
|
||||
axes = f.getVariationAxes();
|
||||
is(axes.length, 1, "number of variation axes: " + axes.length);
|
||||
for (var i = 0; i < axes.length; ++i) {
|
||||
var v = axes[i];
|
||||
switch (v.tag) {
|
||||
case "wght":
|
||||
nameTest(v.name, "Weight", "name of wght axis: " + v.name);
|
||||
is(v.minValue, 40, "minimum weight: " + v.minValue);
|
||||
is(v.maxValue, 200, "maximum weight: " + v.maxValue);
|
||||
is(v.defaultValue, 40, "default weight: " + v.defaultValue);
|
||||
break;
|
||||
default:
|
||||
ok(false, "unexpected axis: " + v.tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
is(f.name, "Libre Franklin", "font name");
|
||||
checkAxes(f.getVariationAxes(), LibreFranklinAxes);
|
||||
checkInstances(f.getVariationInstances(), LibreFranklinInstances);
|
||||
|
||||
// Gingham font should have two axes: Weight and Width.
|
||||
elem = document.getElementById("test3");
|
||||
|
@ -80,29 +147,9 @@ function RunTest() {
|
|||
fonts = InspectorUtils.getUsedFontFaces(rng);
|
||||
is(fonts.length, 1, "number of fonts");
|
||||
f = fonts[0];
|
||||
is(f.name, "Gingham Regular", "font name: " + f.name);
|
||||
axes = f.getVariationAxes();
|
||||
is(axes.length, 2, "number of variation axes");
|
||||
for (var i = 0; i < axes.length; ++i) {
|
||||
var v = axes[i];
|
||||
switch (v.tag) {
|
||||
case "wght":
|
||||
nameTest(v.name, "Weight", "name of wght axis: " + v.name);
|
||||
is(v.minValue, 300, "minimum weight: " + v.minValue);
|
||||
is(v.maxValue, 700, "maximum weight: " + v.maxValue);
|
||||
is(v.defaultValue, 300, "default weight: " + v.defaultValue);
|
||||
break;
|
||||
case "wdth":
|
||||
nameTest(v.name, "Width", "name of wdth axis: " + v.name);
|
||||
is(v.minValue, 1, "minimum width: " + v.minValue);
|
||||
is(v.maxValue, 150, "maximum width: " + v.maxValue);
|
||||
is(v.defaultValue, 1, "default width: " + v.defaultValue);
|
||||
break;
|
||||
default:
|
||||
ok(false, "unexpected axis: " + v.tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
is(f.name, "Gingham Regular", "font name");
|
||||
checkAxes(f.getVariationAxes(), GinghamAxes);
|
||||
checkInstances(f.getVariationInstances(), GinghamInstances);
|
||||
}
|
||||
|
||||
if (kIsMac) {
|
||||
|
@ -111,31 +158,11 @@ function RunTest() {
|
|||
elem = document.getElementById("test4");
|
||||
rng.selectNode(elem);
|
||||
fonts = InspectorUtils.getUsedFontFaces(rng);
|
||||
is(fonts.length, 1, "number of fonts: " + fonts.length);
|
||||
is(fonts.length, 1, "number of fonts");
|
||||
f = fonts[0];
|
||||
is(f.name, "Skia", "font name: " + f.name);
|
||||
axes = f.getVariationAxes();
|
||||
is(axes.length, 2, "number of variation axes: " + axes.length);
|
||||
for (var i = 0; i < axes.length; ++i) {
|
||||
var v = axes[i];
|
||||
switch (v.tag) {
|
||||
case "wght":
|
||||
is(v.name, "Weight", "name of wght axis: " + v.name);
|
||||
isfuzzy(v.minValue, 0.48, kEpsilon, "minimum weight: " + v.minValue);
|
||||
isfuzzy(v.maxValue, 3.20, kEpsilon, "maximum weight: " + v.maxValue);
|
||||
is(v.defaultValue, 1.0, "default weight: " + v.defaultValue);
|
||||
break;
|
||||
case "wdth":
|
||||
is(v.name, "Width", "name of wdth axis: " + v.name);
|
||||
isfuzzy(v.minValue, 0.62, kEpsilon, "minimum width: " + v.minValue);
|
||||
isfuzzy(v.maxValue, 1.30, kEpsilon, "maximum width: " + v.maxValue);
|
||||
is(v.defaultValue, 1.0, "default width: " + v.defaultValue);
|
||||
break;
|
||||
default:
|
||||
ok(false, "unexpected axis: " + v.tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
is(f.name, "Skia", "font name");
|
||||
checkAxes(f.getVariationAxes(), SkiaAxes);
|
||||
checkInstances(f.getVariationInstances(), SkiaInstances);
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче