merge mozilla-inbound to mozilla-central a=merge
|
@ -428,8 +428,8 @@ description#identity-popup-content-verifier,
|
|||
margin: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
list-style-image: url(chrome://browser/skin/panel-icons.svg#cancel);
|
||||
filter: url(chrome://global/skin/filters.svg#fill);
|
||||
list-style-image: url(chrome://browser/skin/panel-icon-cancel.svg);
|
||||
-moz-context-properties: fill;
|
||||
fill: graytext;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
filter: url("chrome://global/skin/filters.svg#fill");
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
|
@ -123,19 +123,19 @@
|
|||
/*** Button icons ***/
|
||||
|
||||
.downloadIconCancel > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#cancel");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-cancel.svg");
|
||||
}
|
||||
|
||||
.downloadIconShow > .button-box > .button-icon {
|
||||
%ifdef XP_MACOSX
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#magnifier");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-magnifier.svg");
|
||||
%else
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#folder");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-folder.svg");
|
||||
%endif
|
||||
}
|
||||
|
||||
.downloadIconRetry > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#retry");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-retry.svg");
|
||||
}
|
||||
|
||||
/*** Progressmeter ***/
|
||||
|
|
|
@ -249,7 +249,7 @@ richlistitem[type="download"]:last-child {
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 1px;
|
||||
filter: url("chrome://global/skin/filters.svg#fill");
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
|
@ -287,27 +287,27 @@ richlistitem[type="download"]:last-child {
|
|||
/*** Button icons ***/
|
||||
|
||||
.downloadIconCancel > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#cancel");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-cancel.svg");
|
||||
}
|
||||
|
||||
.downloadIconShow > .button-box > .button-icon {
|
||||
%ifdef XP_MACOSX
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#magnifier");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-magnifier.svg");
|
||||
%else
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#folder");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-folder.svg");
|
||||
%endif
|
||||
}
|
||||
|
||||
.downloadIconRetry > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#retry");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-retry.svg");
|
||||
}
|
||||
|
||||
.downloadShowBlockedInfo > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-right");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-arrow-right.svg");
|
||||
}
|
||||
|
||||
.downloadShowBlockedInfo > .button-box > .button-icon:-moz-locale-dir(rtl) {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-left");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-arrow-left.svg");
|
||||
}
|
||||
|
||||
/*** Blocked subview ***/
|
||||
|
@ -325,11 +325,11 @@ richlistitem[type="download"]:last-child {
|
|||
}
|
||||
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton > .button-box > .button-icon {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-left");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-arrow-left.svg");
|
||||
}
|
||||
|
||||
#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton > .button-box > .button-icon:-moz-locale-dir(rtl) {
|
||||
list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-right");
|
||||
list-style-image: url("chrome://browser/skin/panel-icon-arrow-right.svg");
|
||||
}
|
||||
|
||||
#downloadsPanel-blockedSubview {
|
||||
|
|
|
@ -70,7 +70,15 @@
|
|||
skin/classic/browser/tracking-protection-16.svg (../shared/identity-block/tracking-protection-16.svg)
|
||||
skin/classic/browser/newtab/close.png (../shared/newtab/close.png)
|
||||
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
|
||||
skin/classic/browser/panel-icons.svg (../shared/panel-icons.svg)
|
||||
skin/classic/browser/panel-icon-arrow-left.svg (../shared/panel-icon-arrow-left.svg)
|
||||
skin/classic/browser/panel-icon-arrow-right.svg (../shared/panel-icon-arrow-right.svg)
|
||||
skin/classic/browser/panel-icon-cancel.svg (../shared/panel-icon-cancel.svg)
|
||||
#ifndef XP_MACOSX
|
||||
skin/classic/browser/panel-icon-folder.svg (../shared/panel-icon-folder.svg)
|
||||
#else
|
||||
skin/classic/browser/panel-icon-magnifier.svg (../shared/panel-icon-magnifier.svg)
|
||||
#endif
|
||||
skin/classic/browser/panel-icon-retry.svg (../shared/panel-icon-retry.svg)
|
||||
skin/classic/browser/preferences/in-content-new/favicon.ico (../shared/incontentprefs/favicon.ico)
|
||||
skin/classic/browser/preferences/in-content-new/icons.svg (../shared/incontentprefs/icons.svg)
|
||||
skin/classic/browser/preferences/in-content-new/search.css (../shared/incontentprefs/search.css)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="context-fill" d="M23.5,25l-9-9l9-9l-3-3l-12,12l12,12L23.5,25z" />
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 389 B |
|
@ -0,0 +1,8 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="context-fill" d="M11.6,28l12-12l-12-12l-3,3l9,9l-9,9L11.6,28z" />
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 389 B |
|
@ -0,0 +1,8 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="context-fill" d="m 6,9.5 6.5,6.5 -6.5,6.5 3.5,3.5 6.5,-6.5 6.5,6.5 3.5,-3.5 -6.5,-6.5 6.5,-6.5 -3.5,-3.5 -6.5,6.5 -6.5,-6.5 z" />
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 453 B |
|
@ -0,0 +1,8 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="context-fill" d="M17.3,9.4c0,0,1.1,0,3.7,0c1.7,0,2,0,5.6,0c0.6,0,0.6,0,1.1,0V9.2c0-1.5-0.9-2.6-2-2.6h-5.8V6.3c0-0.6-1.5-2-2.8-2h-7.1 H7.6H4.9v2.4v2.4v2.2c2.8,0,8.5,0,8.5,0C16.4,11.3,17.3,9.4,17.3,9.4z M29,13c0-0.6-0.6-1.1-1.5-1.7l0,0c-0.2,0-0.6,0-0.9,0 c-2.8,0-3,0-4.8,0c-1.9,0-3.3,0-3.3,0s-1.5,2.4-3.7,2.4c0,0-6.5,0-9.1,0H5.4C3,13.7,3,15.9,3,15.9l1.1,9.7C4.1,27.1,5,28,6.5,28 h19.1c1.5,0,2.4-0.9,2.4-2.4L29,13.7l0,0l0,0C29,13.7,29,13,29,13z" />
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 769 B |
|
@ -0,0 +1,8 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="context-fill" d="M12.9,2c6,0,11,5,11,11c0,2.2-0.6,4.2-1.8,6l7.2,7c0.8,0.8,0.8,2.4,0,3.2c-0.6,0.6-1.2,0.8-1.6,0.8s-1.2-0.2-1.6-0.6l-7-7 c-1.8,1.2-3.8,1.8-6,1.8c-6,0-11-5-11-11C2.1,7.2,6.9,2,12.9,2z M12.9,20c3.8,0,7-3.2,7-7s-3.2-7-7-7s-7,3.2-7,7S9.1,20,12.9,20z" />
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 587 B |
|
@ -0,0 +1,8 @@
|
|||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<path fill="context-fill" d="M28,16.5v-14l-5,4.8c-1.8-1.4-4.4-2.4-7-2.4c-6.4,0-11.8,5.2-11.8,11.8c0,6.4,5.2,11.8,11.8,11.8c3.4,0,6.2-1.4,8.2-3.6 l-3.4-3.4c-1.2,1.2-3,1.8-5,1.8c-3.6,0.2-6.8-2.8-6.8-6.8c0-3.8,3-7.2,7-7.2c1.4,0,2.6,0.4,3.6,1l-6,6.2H28z"/>
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 564 B |
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="32" height="32" viewBox="0 0 32 32">
|
||||
<style>
|
||||
path:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<path id="cancel" d="m 6,9.5 6.5,6.5 -6.5,6.5 3.5,3.5 6.5,-6.5 6.5,6.5 3.5,-3.5 -6.5,-6.5 6.5,-6.5 -3.5,-3.5 -6.5,6.5 -6.5,-6.5 z" />
|
||||
<path id="folder" d="M17.3,9.4c0,0,1.1,0,3.7,0c1.7,0,2,0,5.6,0c0.6,0,0.6,0,1.1,0V9.2c0-1.5-0.9-2.6-2-2.6h-5.8V6.3c0-0.6-1.5-2-2.8-2h-7.1 H7.6H4.9v2.4v2.4v2.2c2.8,0,8.5,0,8.5,0C16.4,11.3,17.3,9.4,17.3,9.4z M29,13c0-0.6-0.6-1.1-1.5-1.7l0,0c-0.2,0-0.6,0-0.9,0 c-2.8,0-3,0-4.8,0c-1.9,0-3.3,0-3.3,0s-1.5,2.4-3.7,2.4c0,0-6.5,0-9.1,0H5.4C3,13.7,3,15.9,3,15.9l1.1,9.7C4.1,27.1,5,28,6.5,28 h19.1c1.5,0,2.4-0.9,2.4-2.4L29,13.7l0,0l0,0C29,13.7,29,13,29,13z" />
|
||||
<path id="magnifier" d="M12.9,2c6,0,11,5,11,11c0,2.2-0.6,4.2-1.8,6l7.2,7c0.8,0.8,0.8,2.4,0,3.2c-0.6,0.6-1.2,0.8-1.6,0.8s-1.2-0.2-1.6-0.6l-7-7 c-1.8,1.2-3.8,1.8-6,1.8c-6,0-11-5-11-11C2.1,7.2,6.9,2,12.9,2z M12.9,20c3.8,0,7-3.2,7-7s-3.2-7-7-7s-7,3.2-7,7S9.1,20,12.9,20z" />
|
||||
<path id="retry" d="M28,16.5v-14l-5,4.8c-1.8-1.4-4.4-2.4-7-2.4c-6.4,0-11.8,5.2-11.8,11.8c0,6.4,5.2,11.8,11.8,11.8c3.4,0,6.2-1.4,8.2-3.6 l-3.4-3.4c-1.2,1.2-3,1.8-5,1.8c-3.6,0.2-6.8-2.8-6.8-6.8c0-3.8,3-7.2,7-7.2c1.4,0,2.6,0.4,3.6,1l-6,6.2H28z"/>
|
||||
<path id="arrow-left" d="M23.5,25l-9-9l9-9l-3-3l-12,12l12,12L23.5,25z" />
|
||||
<path id="arrow-right" d="M11.6,28l12-12l-12-12l-3,3l9,9l-9,9L11.6,28z" />
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.6 KiB |
|
@ -14,6 +14,7 @@
|
|||
#include "BasePoint3D.h"
|
||||
#include "BasePoint4D.h"
|
||||
#include "BaseSize.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <cmath>
|
||||
|
@ -292,6 +293,7 @@ struct IntSizeTyped :
|
|||
}
|
||||
};
|
||||
typedef IntSizeTyped<UnknownUnits> IntSize;
|
||||
typedef Maybe<IntSize> MaybeIntSize;
|
||||
|
||||
template<class units, class F = Float>
|
||||
struct SizeTyped :
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "Units.h"
|
||||
#include "mozilla/gfx/Point.h" // for IntPoint
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/TypedEnumBits.h"
|
||||
#include "nsRegion.h"
|
||||
|
||||
|
@ -214,6 +215,8 @@ typedef Array<LayerSize, 4> BorderCorners;
|
|||
typedef Array<LayerCoord, 4> BorderWidths;
|
||||
typedef Array<uint8_t, 4> BorderStyles;
|
||||
|
||||
typedef Maybe<LayerRect> MaybeLayerRect;
|
||||
|
||||
// This is used to communicate Layers across IPC channels. The Handle is valid
|
||||
// for layers in the same PLayerTransaction. Handles are created by ClientLayerManager,
|
||||
// and are cached in LayerTransactionParent on first use.
|
||||
|
|
|
@ -532,6 +532,11 @@ CompositorBridgeParent::RecvWaitOnTransactionProcessed()
|
|||
mozilla::ipc::IPCResult
|
||||
CompositorBridgeParent::RecvFlushRendering()
|
||||
{
|
||||
if (gfxVars::UseWebRender()) {
|
||||
mWrBridge->FlushRendering(/* aSync */ true);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
if (mCompositorScheduler->NeedsComposite()) {
|
||||
CancelCurrentCompositeTask();
|
||||
ForceComposeToTarget(nullptr);
|
||||
|
@ -542,6 +547,11 @@ CompositorBridgeParent::RecvFlushRendering()
|
|||
mozilla::ipc::IPCResult
|
||||
CompositorBridgeParent::RecvFlushRenderingAsync()
|
||||
{
|
||||
if (gfxVars::UseWebRender()) {
|
||||
mWrBridge->FlushRendering(/* aSync */ false);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
return RecvFlushRendering();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
|
|||
using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
|
||||
using WrBuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
|
||||
using WrSize from "mozilla/webrender/webrender_ffi.h";
|
||||
using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -60,7 +60,8 @@ parent:
|
|||
WrSize aContentSize, ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
|
||||
WebRenderScrollData aScrollData);
|
||||
sync DPGetSnapshot(PTexture texture);
|
||||
async AddExternalImageId(ExternalImageId aImageId, CompositableHandle aHandle);
|
||||
async AddPipelineIdForAsyncCompositable(PipelineId aImageId, CompositableHandle aHandle);
|
||||
async RemovePipelineIdForAsyncCompositable(PipelineId aPipelineId);
|
||||
async AddExternalImageIdForCompositable(ExternalImageId aImageId, CompositableHandle aHandle);
|
||||
async RemoveExternalImageId(ExternalImageId aImageId);
|
||||
async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
|
||||
|
|
|
@ -9,8 +9,17 @@ include LayersSurfaces;
|
|||
include LayersMessages;
|
||||
include protocol PTexture;
|
||||
|
||||
using WrSize from "mozilla/webrender/webrender_ffi.h";
|
||||
using WrImageRendering from "mozilla/webrender/webrender_ffi.h";
|
||||
using WrMixBlendMode from "mozilla/webrender/webrender_ffi.h";
|
||||
using MaybeImageMask from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
|
||||
using mozilla::gfx::MaybeIntSize from "mozilla/gfx/Point.h";
|
||||
using mozilla::LayerPoint from "Units.h";
|
||||
using mozilla::layers::MaybeLayerRect from "mozilla/layers/LayersTypes.h";
|
||||
using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -31,20 +40,26 @@ struct OpAddExternalImage {
|
|||
ImageKey key;
|
||||
};
|
||||
|
||||
struct OpAddExternalVideoImage {
|
||||
ExternalImageId externalImageId;
|
||||
ImageKey[] keys;
|
||||
};
|
||||
|
||||
struct OpAddCompositorAnimations {
|
||||
CompositorAnimations data;
|
||||
OptionalTransform transform;
|
||||
OptionalOpacity opacity;
|
||||
};
|
||||
|
||||
struct OpUpdateAsyncImagePipeline {
|
||||
PipelineId pipelineId;
|
||||
LayerRect scBounds;
|
||||
Matrix4x4 scTransform;
|
||||
MaybeIntSize scaleToSize;
|
||||
MaybeLayerRect clipRect;
|
||||
MaybeImageMask mask;
|
||||
WrImageRendering filter;
|
||||
WrMixBlendMode mixBlendMode;
|
||||
};
|
||||
|
||||
union WebRenderParentCommand {
|
||||
OpAddExternalImage;
|
||||
OpAddExternalVideoImage;
|
||||
OpUpdateAsyncImagePipeline;
|
||||
CompositableOperation;
|
||||
OpAddCompositorAnimations;
|
||||
};
|
||||
|
|
|
@ -120,6 +120,19 @@ WebRenderBridgeChild::DPEnd(wr::DisplayListBuilder &aBuilder,
|
|||
mIsInTransaction = false;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeChild::AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
|
||||
const CompositableHandle& aHandle)
|
||||
{
|
||||
SendAddPipelineIdForAsyncCompositable(aPipelineId, aHandle);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeChild::RemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId)
|
||||
{
|
||||
SendRemovePipelineIdForAsyncCompositable(aPipelineId);
|
||||
}
|
||||
|
||||
wr::ExternalImageId
|
||||
WebRenderBridgeChild::GetNextExternalImageId()
|
||||
{
|
||||
|
@ -128,16 +141,6 @@ WebRenderBridgeChild::GetNextExternalImageId()
|
|||
return id.value();
|
||||
}
|
||||
|
||||
wr::ExternalImageId
|
||||
WebRenderBridgeChild::AllocExternalImageId(const CompositableHandle& aHandle)
|
||||
{
|
||||
MOZ_ASSERT(!mDestroyed);
|
||||
|
||||
wr::ExternalImageId imageId = GetNextExternalImageId();
|
||||
SendAddExternalImageId(imageId, aHandle);
|
||||
return imageId;
|
||||
}
|
||||
|
||||
wr::ExternalImageId
|
||||
WebRenderBridgeChild::AllocExternalImageIdForCompositable(CompositableClient* aCompositable)
|
||||
{
|
||||
|
|
|
@ -73,7 +73,10 @@ public:
|
|||
TextureForwarder* GetTextureForwarder() override;
|
||||
LayersIPCActor* GetLayersIPCActor() override;
|
||||
|
||||
wr::ExternalImageId AllocExternalImageId(const CompositableHandle& aHandle);
|
||||
void AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
|
||||
const CompositableHandle& aHandlee);
|
||||
void RemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId);
|
||||
|
||||
wr::ExternalImageId AllocExternalImageIdForCompositable(CompositableClient* aCompositable);
|
||||
void DeallocExternalImageId(wr::ExternalImageId& aImageId);
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompos
|
|||
, mIdNameSpace(AllocIdNameSpace())
|
||||
, mPaused(false)
|
||||
, mDestroyed(false)
|
||||
, mIsSnapshotting(false)
|
||||
, mForceRendering(false)
|
||||
{
|
||||
MOZ_ASSERT(mCompositableHolder);
|
||||
mCompositableHolder->AddPipeline(mPipelineId);
|
||||
|
@ -133,6 +133,9 @@ WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompos
|
|||
}
|
||||
}
|
||||
|
||||
WebRenderBridgeParent::~WebRenderBridgeParent()
|
||||
{
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvCreate(const gfx::IntSize& aSize)
|
||||
|
@ -454,7 +457,6 @@ WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
|
|||
NS_ERROR("CompositableHost does not exist");
|
||||
break;
|
||||
}
|
||||
// XXX select Texture for video in CompositeToTarget().
|
||||
TextureHost* texture = host->GetAsTextureHostForComposite();
|
||||
if (!texture) {
|
||||
NS_ERROR("TextureHost does not exist");
|
||||
|
@ -467,11 +469,13 @@ WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
|
|||
}
|
||||
RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
|
||||
if (!dSurf) {
|
||||
NS_ERROR("TextureHost does not return DataSourceSurface");
|
||||
break;
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
|
||||
NS_ERROR("DataSourceSurface failed to map");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -483,49 +487,16 @@ WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
|
|||
dSurf->Unmap();
|
||||
break;
|
||||
}
|
||||
case WebRenderParentCommand::TOpAddExternalVideoImage: {
|
||||
const OpAddExternalVideoImage& op = cmd.get_OpAddExternalVideoImage();
|
||||
MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(op.externalImageId())).get());
|
||||
MOZ_ASSERT(op.keys().Length() > 0);
|
||||
Range<const wr::ImageKey> keys(&(op.keys())[0], op.keys().Length());
|
||||
for (auto key : keys) {
|
||||
MOZ_ASSERT(!mActiveKeys.Get(wr::AsUint64(key), nullptr));
|
||||
mActiveKeys.Put(wr::AsUint64(key), key);
|
||||
}
|
||||
|
||||
RefPtr<WebRenderImageHost> host = mExternalImageIds.Get(wr::AsUint64(op.externalImageId()));
|
||||
if (!host) {
|
||||
NS_ERROR("CompositableHost does not exist");
|
||||
break;
|
||||
}
|
||||
// XXX select Texture for video in CompositeToTarget().
|
||||
TextureHost* texture = host->GetAsTextureHostForComposite();
|
||||
if (!texture) {
|
||||
NS_ERROR("TextureHost does not exist");
|
||||
break;
|
||||
}
|
||||
WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
|
||||
if (wrTexture) {
|
||||
wrTexture->AddWRImage(mApi, keys, wrTexture->GetExternalImageKey());
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(keys.length() == 1);
|
||||
RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
|
||||
if (!dSurf) {
|
||||
break;
|
||||
}
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
|
||||
break;
|
||||
}
|
||||
|
||||
IntSize size = dSurf->GetSize();
|
||||
wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
|
||||
auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
|
||||
mApi->AddImage(keys[0], descriptor, slice);
|
||||
|
||||
dSurf->Unmap();
|
||||
case WebRenderParentCommand::TOpUpdateAsyncImagePipeline: {
|
||||
const OpUpdateAsyncImagePipeline& op = cmd.get_OpUpdateAsyncImagePipeline();
|
||||
mCompositableHolder->UpdateAsyncImagePipeline(op.pipelineId(),
|
||||
op.scBounds(),
|
||||
op.scTransform(),
|
||||
op.scaleToSize(),
|
||||
op.clipRect(),
|
||||
op.mask(),
|
||||
op.filter(),
|
||||
op.mixBlendMode());
|
||||
break;
|
||||
}
|
||||
case WebRenderParentCommand::TCompositableOperation: {
|
||||
|
@ -618,7 +589,7 @@ WebRenderBridgeParent::RecvDPGetSnapshot(PTextureParent* aTexture)
|
|||
// Assert the stride of the buffer is what webrender expects
|
||||
MOZ_ASSERT((uint32_t)(size.width * 4) == stride);
|
||||
|
||||
mIsSnapshotting = true;
|
||||
mForceRendering = true;
|
||||
|
||||
if (mCompositorScheduler->NeedsComposite()) {
|
||||
mCompositorScheduler->CancelCurrentCompositeTask();
|
||||
|
@ -627,20 +598,20 @@ WebRenderBridgeParent::RecvDPGetSnapshot(PTextureParent* aTexture)
|
|||
|
||||
mApi->Readback(size, buffer, buffer_size);
|
||||
|
||||
mIsSnapshotting = false;
|
||||
mForceRendering = false;
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvAddExternalImageId(const ExternalImageId& aImageId,
|
||||
const CompositableHandle& aHandle)
|
||||
WebRenderBridgeParent::RecvAddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
|
||||
const CompositableHandle& aHandle)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mExternalImageIds.Get(wr::AsUint64(aImageId)).get());
|
||||
MOZ_ASSERT(!mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get());
|
||||
|
||||
RefPtr<ImageBridgeParent> imageBridge = ImageBridgeParent::GetInstance(OtherPid());
|
||||
if (!imageBridge) {
|
||||
|
@ -659,11 +630,29 @@ WebRenderBridgeParent::RecvAddExternalImageId(const ExternalImageId& aImageId,
|
|||
}
|
||||
|
||||
wrHost->SetWrBridge(this);
|
||||
mExternalImageIds.Put(wr::AsUint64(aImageId), wrHost);
|
||||
mAsyncCompositables.Put(wr::AsUint64(aPipelineId), wrHost);
|
||||
mCompositableHolder->AddAsyncImagePipeline(aPipelineId, wrHost);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvRemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get());
|
||||
WebRenderImageHost* wrHost = mAsyncCompositables.Get(wr::AsUint64(aPipelineId)).get();
|
||||
if (wrHost) {
|
||||
wrHost->ClearWrBridge();
|
||||
mCompositableHolder->RemoveAsyncImagePipeline(mApi, aPipelineId);
|
||||
}
|
||||
mAsyncCompositables.Remove(wr::AsUint64(aPipelineId));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
|
||||
const CompositableHandle& aHandle)
|
||||
|
@ -822,7 +811,7 @@ WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::In
|
|||
|
||||
const uint32_t maxPendingFrameCount = 2;
|
||||
|
||||
if (!mIsSnapshotting &&
|
||||
if (!mForceRendering &&
|
||||
wr::RenderThread::Get()->GetPendingFrameCount(mApi->GetId()) > maxPendingFrameCount) {
|
||||
// Render thread is busy, try next time.
|
||||
ScheduleComposition();
|
||||
|
@ -833,6 +822,9 @@ WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::In
|
|||
nsTArray<WrOpacityProperty> opacityArray;
|
||||
nsTArray<WrTransformProperty> transformArray;
|
||||
|
||||
mCompositableHolder->SetCompositionTime(TimeStamp::Now());
|
||||
mCompositableHolder->ApplyAsyncImages(mApi);
|
||||
|
||||
if (gfxPrefs::WebRenderOMTAEnabled()) {
|
||||
SampleAnimations(opacityArray, transformArray);
|
||||
|
||||
|
@ -851,10 +843,9 @@ WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::In
|
|||
mApi->GenerateFrame();
|
||||
}
|
||||
|
||||
// XXX Enable it when async video is supported.
|
||||
// if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
|
||||
// scheduleComposite = true;
|
||||
// }
|
||||
if (!mCompositableHolder->GetCompositeUntilTime().IsNull()) {
|
||||
scheduleComposite = true;
|
||||
}
|
||||
|
||||
if (scheduleComposite) {
|
||||
ScheduleComposition();
|
||||
|
@ -911,10 +902,6 @@ WebRenderBridgeParent::FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch)
|
|||
return id;
|
||||
}
|
||||
|
||||
WebRenderBridgeParent::~WebRenderBridgeParent()
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t
|
||||
WebRenderBridgeParent::GetLayersId() const
|
||||
{
|
||||
|
@ -938,6 +925,26 @@ WebRenderBridgeParent::ScheduleComposition()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeParent::FlushRendering(bool aIsSync)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mCompositorScheduler->NeedsComposite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mForceRendering = true;
|
||||
mCompositorScheduler->CancelCurrentCompositeTask();
|
||||
mCompositorScheduler->ForceComposeToTarget(nullptr, nullptr);
|
||||
if (aIsSync) {
|
||||
mApi->WaitFlushed();
|
||||
}
|
||||
mForceRendering = false;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderBridgeParent::Pause()
|
||||
{
|
||||
|
@ -988,6 +995,15 @@ WebRenderBridgeParent::ClearResources()
|
|||
iter.Data()->ClearWrBridge();
|
||||
}
|
||||
mExternalImageIds.Clear();
|
||||
for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
|
||||
wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
|
||||
RefPtr<WebRenderImageHost> host = iter.Data();
|
||||
MOZ_ASSERT(host->GetAsyncRef());
|
||||
host->ClearWrBridge();
|
||||
mCompositableHolder->RemoveAsyncImagePipeline(mApi, pipelineId);
|
||||
}
|
||||
mAsyncCompositables.Clear();
|
||||
|
||||
mCompositableHolder->RemovePipeline(mPipelineId, wr::NewEpoch(mWrEpoch));
|
||||
|
||||
if (mWidget) {
|
||||
|
|
|
@ -106,8 +106,10 @@ public:
|
|||
const WebRenderScrollData& aScrollData) override;
|
||||
mozilla::ipc::IPCResult RecvDPGetSnapshot(PTextureParent* aTexture) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvAddExternalImageId(const ExternalImageId& aImageId,
|
||||
const CompositableHandle& aHandle) override;
|
||||
mozilla::ipc::IPCResult RecvAddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineIds,
|
||||
const CompositableHandle& aHandle) override;
|
||||
mozilla::ipc::IPCResult RecvRemovePipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
|
||||
const CompositableHandle& aHandle) override;
|
||||
mozilla::ipc::IPCResult RecvRemoveExternalImageId(const ExternalImageId& aImageId) override;
|
||||
|
@ -177,6 +179,10 @@ public:
|
|||
return ++sIdNameSpace;
|
||||
}
|
||||
|
||||
void FlushRendering(bool aIsSync);
|
||||
|
||||
void ScheduleComposition();
|
||||
|
||||
private:
|
||||
virtual ~WebRenderBridgeParent();
|
||||
|
||||
|
@ -188,7 +194,6 @@ private:
|
|||
const WrSize& aContentSize,
|
||||
const ByteBuffer& dl,
|
||||
const WrBuiltDisplayListDescriptor& dlDesc);
|
||||
void ScheduleComposition();
|
||||
void ClearResources();
|
||||
uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
|
||||
bool ShouldParentObserveEpoch();
|
||||
|
@ -236,6 +241,7 @@ private:
|
|||
std::vector<wr::ImageKey> mKeysToDelete;
|
||||
// XXX How to handle active keys of non-ExternalImages?
|
||||
nsDataHashtable<nsUint64HashKey, wr::ImageKey> mActiveKeys;
|
||||
nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mAsyncCompositables;
|
||||
nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mExternalImageIds;
|
||||
nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
|
||||
|
||||
|
@ -252,7 +258,7 @@ private:
|
|||
|
||||
bool mPaused;
|
||||
bool mDestroyed;
|
||||
bool mIsSnapshotting;
|
||||
bool mForceRendering;
|
||||
|
||||
// Can only be accessed on the compositor thread.
|
||||
WebRenderScrollData mScrollData;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "WebRenderCompositableHolder.h"
|
||||
|
||||
#include "CompositableHost.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "mozilla/layers/WebRenderImageHost.h"
|
||||
#include "mozilla/layers/WebRenderTextureHost.h"
|
||||
#include "mozilla/webrender/WebRenderAPI.h"
|
||||
|
@ -16,9 +17,18 @@ using namespace gfx;
|
|||
|
||||
namespace layers {
|
||||
|
||||
WebRenderCompositableHolder::AsyncImagePipelineHolder::AsyncImagePipelineHolder()
|
||||
: mInitialised(false)
|
||||
, mIsChanged(false)
|
||||
, mFilter(WrImageRendering::Auto)
|
||||
, mMixBlendMode(WrMixBlendMode::Normal)
|
||||
{}
|
||||
|
||||
WebRenderCompositableHolder::WebRenderCompositableHolder(uint32_t aIdNamespace)
|
||||
: mIdNamespace(aIdNamespace)
|
||||
, mResourceId(0)
|
||||
, mAsyncImageEpoch(0)
|
||||
, mDestroyed(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(WebRenderCompositableHolder);
|
||||
}
|
||||
|
@ -28,9 +38,34 @@ WebRenderCompositableHolder::~WebRenderCompositableHolder()
|
|||
MOZ_COUNT_DTOR(WebRenderCompositableHolder);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::Destroy(wr::WebRenderAPI* aApi)
|
||||
{
|
||||
DeleteOldAsyncImages(aApi);
|
||||
mDestroyed = true;
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderCompositableHolder::HasKeysToDelete()
|
||||
{
|
||||
return !mKeysToDelete.IsEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::DeleteOldAsyncImages(wr::WebRenderAPI* aApi)
|
||||
{
|
||||
for (wr::ImageKey key : mKeysToDelete) {
|
||||
aApi->DeleteImage(key);
|
||||
}
|
||||
mKeysToDelete.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::AddPipeline(const wr::PipelineId& aPipelineId)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
uint64_t id = wr::AsUint64(aPipelineId);
|
||||
|
||||
MOZ_ASSERT(!mPipelineTexturesHolders.Get(id));
|
||||
|
@ -41,6 +76,10 @@ WebRenderCompositableHolder::AddPipeline(const wr::PipelineId& aPipelineId)
|
|||
void
|
||||
WebRenderCompositableHolder::RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
|
||||
MOZ_ASSERT(holder);
|
||||
if (!holder) {
|
||||
|
@ -50,9 +89,313 @@ WebRenderCompositableHolder::RemovePipeline(const wr::PipelineId& aPipelineId, c
|
|||
holder->mDestroyedEpoch = Some(aEpoch);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(aImageHost);
|
||||
uint64_t id = wr::AsUint64(aPipelineId);
|
||||
|
||||
MOZ_ASSERT(!mAsyncImagePipelineHolders.Get(id));
|
||||
AsyncImagePipelineHolder* holder = new AsyncImagePipelineHolder();
|
||||
holder->mImageHost = aImageHost;
|
||||
mAsyncImagePipelineHolders.Put(id, holder);
|
||||
AddPipeline(aPipelineId);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t id = wr::AsUint64(aPipelineId);
|
||||
AsyncImagePipelineHolder* holder = mAsyncImagePipelineHolders.Get(id);
|
||||
if (!holder) {
|
||||
return;
|
||||
}
|
||||
|
||||
++mAsyncImageEpoch; // Update webrender epoch
|
||||
aApi->ClearRootDisplayList(wr::NewEpoch(mAsyncImageEpoch), aPipelineId);
|
||||
for (wr::ImageKey key : holder->mKeys) {
|
||||
aApi->DeleteImage(key);
|
||||
}
|
||||
mAsyncImagePipelineHolders.Remove(id);
|
||||
RemovePipeline(aPipelineId, wr::NewEpoch(mAsyncImageEpoch));
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
|
||||
const LayerRect& aScBounds,
|
||||
const gfx::Matrix4x4& aScTransform,
|
||||
const gfx::MaybeIntSize& aScaleToSize,
|
||||
const MaybeLayerRect& aClipRect,
|
||||
const MaybeImageMask& aMask,
|
||||
const WrImageRendering& aFilter,
|
||||
const WrMixBlendMode& aMixBlendMode)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
AsyncImagePipelineHolder* holder = mAsyncImagePipelineHolders.Get(wr::AsUint64(aPipelineId));
|
||||
MOZ_ASSERT(holder);
|
||||
if (!holder) {
|
||||
return;
|
||||
}
|
||||
holder->mInitialised = true;
|
||||
holder->mIsChanged = true;
|
||||
holder->mScBounds = aScBounds;
|
||||
holder->mScTransform = aScTransform;
|
||||
holder->mScaleToSize = aScaleToSize;
|
||||
holder->mClipRect = aClipRect;
|
||||
holder->mMask = aMask;
|
||||
holder->mFilter = aFilter;
|
||||
holder->mMixBlendMode = aMixBlendMode;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::GetImageKeys(nsTArray<wr::ImageKey>& aKeys, size_t aChannelNumber)
|
||||
{
|
||||
MOZ_ASSERT(aChannelNumber > 0);
|
||||
for (size_t i = 0; i < aChannelNumber; ++i) {
|
||||
wr::ImageKey key = GetImageKey();
|
||||
aKeys.AppendElement(key);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::GetImageKeysForExternalImage(nsTArray<wr::ImageKey>& aKeys)
|
||||
{
|
||||
MOZ_ASSERT(aKeys.IsEmpty());
|
||||
|
||||
// XXX (Jerry): Remove the hardcode image format setting.
|
||||
#if defined(XP_WIN)
|
||||
// Use libyuv to convert the buffer to rgba format. So, use 1 image key here.
|
||||
GetImageKeys(aKeys, 1);
|
||||
#elif defined(XP_MACOSX)
|
||||
if (gfxVars::CanUseHardwareVideoDecoding()) {
|
||||
// Use the hardware MacIOSurface with YCbCr interleaved format. It uses 1
|
||||
// image key.
|
||||
GetImageKeys(aKeys, 1);
|
||||
} else {
|
||||
// Use libyuv.
|
||||
GetImageKeys(aKeys, 1);
|
||||
}
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
// Use libyuv.
|
||||
GetImageKeys(aKeys, 1);
|
||||
#elif defined(ANDROID)
|
||||
// Use libyuv.
|
||||
GetImageKeys(aKeys, 1);
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT(!aKeys.IsEmpty());
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderCompositableHolder::GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys)
|
||||
{
|
||||
MOZ_ASSERT(aKeys.IsEmpty());
|
||||
MOZ_ASSERT(aTexture);
|
||||
|
||||
WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
|
||||
|
||||
if (wrTexture) {
|
||||
GetImageKeysForExternalImage(aKeys);
|
||||
MOZ_ASSERT(!aKeys.IsEmpty());
|
||||
Range<const wr::ImageKey> keys(&aKeys[0], aKeys.Length());
|
||||
wrTexture->AddWRImage(aApi, keys, wrTexture->GetExternalImageKey());
|
||||
return true;
|
||||
} else {
|
||||
RefPtr<DataSourceSurface> dSurf = aTexture->GetAsSurface();
|
||||
if (!dSurf) {
|
||||
NS_ERROR("TextureHost does not return DataSourceSurface");
|
||||
return false;
|
||||
}
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
|
||||
NS_ERROR("DataSourceSurface failed to map");
|
||||
return false;
|
||||
}
|
||||
IntSize size = dSurf->GetSize();
|
||||
wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
|
||||
auto slice = Range<uint8_t>(map.mData, size.height * map.mStride);
|
||||
|
||||
wr::ImageKey key = GetImageKey();
|
||||
aKeys.AppendElement(key);
|
||||
aApi->AddImage(key, descriptor, slice);
|
||||
dSurf->Unmap();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::PushExternalImage(wr::DisplayListBuilder& aBuilder,
|
||||
const WrRect& aBounds,
|
||||
const WrClipRegionToken aClip,
|
||||
wr::ImageRendering aFilter,
|
||||
nsTArray<wr::ImageKey>& aKeys)
|
||||
{
|
||||
// XXX (Jerry): Remove the hardcode image format setting. The format of
|
||||
// textureClient could change from time to time. So, we just set the most
|
||||
// usable format here.
|
||||
#if defined(XP_WIN)
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(aKeys.Length() == 1);
|
||||
aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
|
||||
#elif defined(XP_MACOSX)
|
||||
if (gfx::gfxVars::CanUseHardwareVideoDecoding()) {
|
||||
// Use the hardware MacIOSurface with YCbCr interleaved format.
|
||||
MOZ_ASSERT(aKeys.Length() == 1);
|
||||
aBuilder.PushYCbCrInterleavedImage(aBounds, aClip, aKeys[0], WrYuvColorSpace::Rec601, aFilter);
|
||||
} else {
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(aKeys.Length() == 1);
|
||||
aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
|
||||
}
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(aKeys.Length() == 1);
|
||||
aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
|
||||
#elif defined(ANDROID)
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(aKeys.Length() == 1);
|
||||
aBuilder.PushImage(aBounds, aClip, aFilter, aKeys[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderCompositableHolder::UpdateImageKeys(wr::WebRenderAPI* aApi,
|
||||
bool& aUseExternalImage,
|
||||
AsyncImagePipelineHolder* aHolder,
|
||||
nsTArray<wr::ImageKey>& aKeys,
|
||||
nsTArray<wr::ImageKey>& aKeysToDelete)
|
||||
{
|
||||
MOZ_ASSERT(aKeys.IsEmpty());
|
||||
MOZ_ASSERT(aHolder);
|
||||
TextureHost* texture = aHolder->mImageHost->GetAsTextureHostForComposite();
|
||||
|
||||
if (!aHolder->mInitialised) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No change
|
||||
if (!aHolder->mIsChanged && texture == aHolder->mCurrentTexture) {
|
||||
// No need to update DisplayList.
|
||||
return false;
|
||||
}
|
||||
|
||||
aHolder->mIsChanged = false;
|
||||
|
||||
if (texture == aHolder->mCurrentTexture) {
|
||||
// Reuse previous ImageKeys.
|
||||
aKeys.AppendElements(aHolder->mKeys);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete old ImageKeys
|
||||
aKeysToDelete.AppendElements(aHolder->mKeys);
|
||||
aHolder->mKeys.Clear();
|
||||
aHolder->mCurrentTexture = nullptr;
|
||||
|
||||
// No txture to render
|
||||
if (!texture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
aUseExternalImage = GetImageKeyForTextureHost(aApi, texture, aKeys);
|
||||
MOZ_ASSERT(!aKeys.IsEmpty());
|
||||
aHolder->mKeys.AppendElements(aKeys);
|
||||
aHolder->mCurrentTexture = texture;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::ApplyAsyncImages(wr::WebRenderAPI* aApi)
|
||||
{
|
||||
if (mDestroyed || mAsyncImagePipelineHolders.Count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
++mAsyncImageEpoch; // Update webrender epoch
|
||||
wr::Epoch epoch = wr::NewEpoch(mAsyncImageEpoch);
|
||||
nsTArray<wr::ImageKey> keysToDelete;
|
||||
|
||||
for (auto iter = mAsyncImagePipelineHolders.Iter(); !iter.Done(); iter.Next()) {
|
||||
wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
|
||||
AsyncImagePipelineHolder* holder = iter.Data();
|
||||
|
||||
nsTArray<wr::ImageKey> keys;
|
||||
bool useExternalImage = false;
|
||||
bool updateDisplayList = UpdateImageKeys(aApi,
|
||||
useExternalImage,
|
||||
holder,
|
||||
keys,
|
||||
keysToDelete);
|
||||
if (!updateDisplayList) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WrSize contentSize { holder->mScBounds.width, holder->mScBounds.height };
|
||||
wr::DisplayListBuilder builder(pipelineId, contentSize);
|
||||
|
||||
if (!keys.IsEmpty()) {
|
||||
MOZ_ASSERT(holder->mCurrentTexture.get());
|
||||
|
||||
float opacity = 1.0f;
|
||||
builder.PushStackingContext(wr::ToWrRect(holder->mScBounds),
|
||||
0,
|
||||
&opacity,
|
||||
holder->mScTransform.IsIdentity() ? nullptr : &holder->mScTransform,
|
||||
holder->mMixBlendMode);
|
||||
|
||||
LayerRect rect(0, 0, holder->mCurrentTexture->GetSize().width, holder->mCurrentTexture->GetSize().height);
|
||||
if (holder->mScaleToSize.isSome()) {
|
||||
rect = LayerRect(0, 0, holder->mScaleToSize.value().width, holder->mScaleToSize.value().height);
|
||||
}
|
||||
LayerRect clipRect = holder->mClipRect.valueOr(rect);
|
||||
WrClipRegionToken clip = builder.PushClipRegion(
|
||||
wr::ToWrRect(clipRect),
|
||||
holder->mMask.ptrOr(nullptr));
|
||||
|
||||
if (useExternalImage) {
|
||||
MOZ_ASSERT(holder->mCurrentTexture->AsWebRenderTextureHost());
|
||||
PushExternalImage(builder,
|
||||
wr::ToWrRect(rect),
|
||||
clip,
|
||||
holder->mFilter,
|
||||
keys);
|
||||
HoldExternalImage(pipelineId, epoch, holder->mCurrentTexture->AsWebRenderTextureHost());
|
||||
} else {
|
||||
MOZ_ASSERT(keys.Length() == 1);
|
||||
builder.PushImage(wr::ToWrRect(rect),
|
||||
clip,
|
||||
holder->mFilter,
|
||||
keys[0]);
|
||||
}
|
||||
builder.PopStackingContext();
|
||||
}
|
||||
|
||||
wr::BuiltDisplayList dl;
|
||||
WrSize builderContentSize;
|
||||
builder.Finalize(builderContentSize, dl);
|
||||
aApi->SetRootDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(holder->mScBounds.width, holder->mScBounds.height),
|
||||
pipelineId, builderContentSize,
|
||||
dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length);
|
||||
}
|
||||
DeleteOldAsyncImages(aApi);
|
||||
mKeysToDelete.SwapElements(keysToDelete);
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderCompositableHolder::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(aTexture);
|
||||
|
||||
PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
|
||||
|
@ -67,6 +410,9 @@ WebRenderCompositableHolder::HoldExternalImage(const wr::PipelineId& aPipelineId
|
|||
void
|
||||
WebRenderCompositableHolder::Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
|
||||
{
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
|
||||
if (!holder) {
|
||||
return;
|
||||
|
@ -74,6 +420,7 @@ WebRenderCompositableHolder::Update(const wr::PipelineId& aPipelineId, const wr:
|
|||
|
||||
// Remove Pipeline
|
||||
if (holder->mDestroyedEpoch.isSome() && holder->mDestroyedEpoch.ref() <= aEpoch) {
|
||||
|
||||
mPipelineTexturesHolders.Remove(wr::AsUint64(aPipelineId));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <queue>
|
||||
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/layers/TextureHost.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/webrender/WebRenderTypes.h"
|
||||
|
@ -16,12 +17,15 @@
|
|||
namespace mozilla {
|
||||
|
||||
namespace wr {
|
||||
class DisplayListBuilder;
|
||||
class WebRenderAPI;
|
||||
}
|
||||
|
||||
namespace layers {
|
||||
|
||||
class CompositableHost;
|
||||
class CompositorVsyncScheduler;
|
||||
class WebRenderImageHost;
|
||||
class WebRenderTextureHost;
|
||||
|
||||
class WebRenderCompositableHolder final
|
||||
|
@ -35,8 +39,12 @@ protected:
|
|||
~WebRenderCompositableHolder();
|
||||
|
||||
public:
|
||||
void Destroy(wr::WebRenderAPI* aApi);
|
||||
bool HasKeysToDelete();
|
||||
|
||||
void AddPipeline(const wr::PipelineId& aPipelineId);
|
||||
void RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
|
||||
|
||||
void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
|
||||
void Update(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
|
||||
|
||||
|
@ -60,6 +68,22 @@ public:
|
|||
return mCompositeUntilTime;
|
||||
}
|
||||
|
||||
void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost);
|
||||
void RemoveAsyncImagePipeline(wr::WebRenderAPI* aApi, const wr::PipelineId& aPipelineId);
|
||||
|
||||
void UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
|
||||
const LayerRect& aScBounds,
|
||||
const gfx::Matrix4x4& aScTransform,
|
||||
const gfx::MaybeIntSize& aScaleToSize,
|
||||
const MaybeLayerRect& aClipRect,
|
||||
const MaybeImageMask& aMask,
|
||||
const WrImageRendering& aFilter,
|
||||
const WrMixBlendMode& aMixBlendMode);
|
||||
void ApplyAsyncImages(wr::WebRenderAPI* aApi);
|
||||
|
||||
private:
|
||||
void DeleteOldAsyncImages(wr::WebRenderAPI* aApi);
|
||||
|
||||
uint32_t GetNextResourceId() { return ++mResourceId; }
|
||||
uint32_t GetNamespace() { return mIdNamespace; }
|
||||
wr::ImageKey GetImageKey()
|
||||
|
@ -69,8 +93,14 @@ public:
|
|||
key.mHandle = GetNextResourceId();
|
||||
return key;
|
||||
}
|
||||
|
||||
private:
|
||||
void GetImageKeys(nsTArray<wr::ImageKey>& aKeys, size_t aChannelNumber);
|
||||
void GetImageKeysForExternalImage(nsTArray<wr::ImageKey>& aKeys);
|
||||
bool GetImageKeyForTextureHost(wr::WebRenderAPI* aApi, TextureHost* aTexture, nsTArray<wr::ImageKey>& aKeys);
|
||||
void PushExternalImage(wr::DisplayListBuilder& aBuilder,
|
||||
const WrRect& aBounds,
|
||||
const WrClipRegionToken aClip,
|
||||
wr::ImageRendering aFilter,
|
||||
nsTArray<wr::ImageKey>& aKeys);
|
||||
|
||||
struct ForwardingTextureHost {
|
||||
ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
|
||||
|
@ -87,9 +117,37 @@ private:
|
|||
Maybe<wr::Epoch> mDestroyedEpoch;
|
||||
};
|
||||
|
||||
struct AsyncImagePipelineHolder {
|
||||
AsyncImagePipelineHolder();
|
||||
|
||||
bool mInitialised;
|
||||
bool mIsChanged;
|
||||
LayerRect mScBounds;
|
||||
gfx::Matrix4x4 mScTransform;
|
||||
gfx::MaybeIntSize mScaleToSize;
|
||||
MaybeLayerRect mClipRect;
|
||||
MaybeImageMask mMask;
|
||||
WrImageRendering mFilter;
|
||||
WrMixBlendMode mMixBlendMode;
|
||||
RefPtr<WebRenderImageHost> mImageHost;
|
||||
CompositableTextureHostRef mCurrentTexture;
|
||||
nsTArray<wr::ImageKey> mKeys;
|
||||
};
|
||||
|
||||
bool UpdateImageKeys(wr::WebRenderAPI* aApi,
|
||||
bool& aUseExternalImage,
|
||||
AsyncImagePipelineHolder* aHolder,
|
||||
nsTArray<wr::ImageKey>& aKeys,
|
||||
nsTArray<wr::ImageKey>& aKeysToDelete);
|
||||
|
||||
uint32_t mIdNamespace;
|
||||
uint32_t mResourceId;
|
||||
|
||||
nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
|
||||
nsClassHashtable<nsUint64HashKey, AsyncImagePipelineHolder> mAsyncImagePipelineHolders;
|
||||
uint32_t mAsyncImageEpoch;
|
||||
nsTArray<wr::ImageKey> mKeysToDelete;
|
||||
bool mDestroyed;
|
||||
|
||||
// Render time for the current composition.
|
||||
TimeStamp mCompositionTime;
|
||||
|
|
|
@ -68,6 +68,10 @@ WebRenderImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
|
|||
mImages.SwapElements(newImages);
|
||||
newImages.Clear();
|
||||
|
||||
if (mWrBridge && GetAsyncRef()) {
|
||||
mWrBridge->ScheduleComposition();
|
||||
}
|
||||
|
||||
// Video producers generally send replacement images with the same frameID but
|
||||
// slightly different timestamps in order to sync with the audio clock. This
|
||||
// means that any CompositeUntil() call we made in Composite() may no longer
|
||||
|
|
|
@ -73,6 +73,8 @@ public:
|
|||
|
||||
void ClearWrBridge();
|
||||
|
||||
TextureHost* GetCurrentTextureHost() { return mCurrentTextureHost; }
|
||||
|
||||
protected:
|
||||
// ImageComposite
|
||||
virtual TimeStamp GetCompositionTime() const override;
|
||||
|
|
|
@ -33,9 +33,6 @@ WebRenderImageLayer::~WebRenderImageLayer()
|
|||
{
|
||||
MOZ_COUNT_DTOR(WebRenderImageLayer);
|
||||
|
||||
for (auto key : mVideoKeys) {
|
||||
WrManager()->AddImageKeyForDiscard(key);
|
||||
}
|
||||
if (mKey.isSome()) {
|
||||
WrManager()->AddImageKeyForDiscard(mKey.value());
|
||||
}
|
||||
|
@ -43,6 +40,9 @@ WebRenderImageLayer::~WebRenderImageLayer()
|
|||
if (mExternalImageId.isSome()) {
|
||||
WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
|
||||
}
|
||||
if (mPipelineId.isSome()) {
|
||||
WrBridge()->RemovePipelineIdForAsyncCompositable(mPipelineId.ref());
|
||||
}
|
||||
}
|
||||
|
||||
CompositableType
|
||||
|
@ -90,17 +90,6 @@ WebRenderImageLayer::ClearCachedResources()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderImageLayer::AddWRVideoImage(size_t aChannelNumber)
|
||||
{
|
||||
for (size_t i = 0; i < aChannelNumber; ++i) {
|
||||
WrImageKey key = GetImageKey();
|
||||
WrManager()->AddImageKeyForDiscard(key);
|
||||
mVideoKeys.AppendElement(key);
|
||||
}
|
||||
WrBridge()->AddWebRenderParentCommand(OpAddExternalVideoImage(mExternalImageId.value(), mVideoKeys));
|
||||
}
|
||||
|
||||
void
|
||||
WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
|
||||
const StackingContextHelper& aSc)
|
||||
|
@ -126,62 +115,89 @@ WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
|
|||
mImageClient->Connect();
|
||||
}
|
||||
|
||||
if (mExternalImageId.isNothing()) {
|
||||
if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
|
||||
MOZ_ASSERT(!mImageClient);
|
||||
mExternalImageId = Some(WrBridge()->AllocExternalImageId(mContainer->GetAsyncContainerHandle()));
|
||||
// Alloc async image pipeline id.
|
||||
mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
|
||||
} else {
|
||||
// Handle CompositableType::IMAGE case
|
||||
MOZ_ASSERT(mImageClient);
|
||||
mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
|
||||
}
|
||||
if (GetImageClientType() == CompositableType::IMAGE_BRIDGE && mPipelineId.isNothing()) {
|
||||
MOZ_ASSERT(!mImageClient);
|
||||
// Alloc async image pipeline id.
|
||||
mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
|
||||
WrBridge()->AddPipelineIdForAsyncCompositable(mPipelineId.ref(),
|
||||
mContainer->GetAsyncContainerHandle());
|
||||
} else if (GetImageClientType() == CompositableType::IMAGE && mExternalImageId.isNothing()) {
|
||||
MOZ_ASSERT(mImageClient);
|
||||
mExternalImageId = Some(WrBridge()->AllocExternalImageIdForCompositable(mImageClient));
|
||||
MOZ_ASSERT(mExternalImageId.isSome());
|
||||
}
|
||||
MOZ_ASSERT(mExternalImageId.isSome());
|
||||
|
||||
// XXX Not good for async ImageContainer case.
|
||||
if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
|
||||
MOZ_ASSERT(!mImageClient);
|
||||
MOZ_ASSERT(mExternalImageId.isNothing());
|
||||
|
||||
// Push IFrame for async image pipeline.
|
||||
|
||||
ParentLayerRect bounds = GetLocalTransformTyped().TransformBounds(Bounds());
|
||||
|
||||
// As with WebRenderTextLayer, because we don't push a stacking context for
|
||||
// this async image pipeline, WR doesn't know about the transform on this layer.
|
||||
// Therefore we need to apply that transform to the bounds before we pass it on to WR.
|
||||
// The conversion from ParentLayerPixel to LayerPixel below is a result of
|
||||
// changing the reference layer from "this layer" to the "the layer that
|
||||
// created aSc".
|
||||
LayerRect rect = ViewAs<LayerPixel>(bounds,
|
||||
PixelCastJustification::MovingDownToChildren);
|
||||
DumpLayerInfo("Image Layer async", rect);
|
||||
|
||||
// XXX Remove IFrame for async image pipeline when partial display list update is supported.
|
||||
WrClipRegionToken clipRegion = aBuilder.PushClipRegion(aSc.ToRelativeWrRect(rect));
|
||||
aBuilder.PushIFrame(aSc.ToRelativeWrRect(rect), clipRegion, mPipelineId.ref());
|
||||
|
||||
// Prepare data that are necessary for async image pipelin.
|
||||
// They are used within WebRenderCompositableHolder
|
||||
|
||||
gfx::Matrix4x4 scTransform = GetTransform();
|
||||
// Translate is applied as part of PushIFrame()
|
||||
scTransform.PostTranslate(-rect.x, -rect.y, 0);
|
||||
// Adjust transform as to apply origin
|
||||
LayerPoint scOrigin = Bounds().TopLeft();
|
||||
scTransform.PreTranslate(-scOrigin.x, -scOrigin.y, 0);
|
||||
|
||||
MaybeIntSize scaleToSize;
|
||||
if (mScaleMode != ScaleMode::SCALE_NONE) {
|
||||
NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
|
||||
"No other scalemodes than stretch and none supported yet.");
|
||||
scaleToSize = Some(mScaleToSize);
|
||||
}
|
||||
LayerRect scBounds = BoundsForStackingContext();
|
||||
wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
|
||||
wr::MixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
|
||||
|
||||
StackingContextHelper sc(aSc, aBuilder, this);
|
||||
Maybe<WrImageMask> mask = BuildWrMaskLayer(&sc);
|
||||
|
||||
WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(mPipelineId.value(),
|
||||
scBounds,
|
||||
scTransform,
|
||||
scaleToSize,
|
||||
ClipRect(),
|
||||
mask,
|
||||
filter,
|
||||
mixBlendMode));
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(GetImageClientType() == CompositableType::IMAGE);
|
||||
MOZ_ASSERT(mImageClient->AsImageClientSingle());
|
||||
|
||||
AutoLockImage autoLock(mContainer);
|
||||
Image* image = autoLock.GetImage();
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
gfx::IntSize size = image->GetSize();
|
||||
|
||||
if (GetImageClientType() != CompositableType::IMAGE_BRIDGE) {
|
||||
// Handle CompositableType::IMAGE case
|
||||
MOZ_ASSERT(mImageClient->AsImageClientSingle());
|
||||
mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
|
||||
mContainer,
|
||||
mKey,
|
||||
mExternalImageId.ref());
|
||||
if (mKey.isNothing()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Always allocate key.
|
||||
mVideoKeys.Clear();
|
||||
|
||||
// XXX (Jerry): Remove the hardcode image format setting.
|
||||
#if defined(XP_WIN)
|
||||
// Use libyuv to convert the buffer to rgba format. So, use 1 image key here.
|
||||
AddWRVideoImage(1);
|
||||
#elif defined(XP_MACOSX)
|
||||
if (gfx::gfxVars::CanUseHardwareVideoDecoding()) {
|
||||
// Use the hardware MacIOSurface with YCbCr interleaved format. It uses 1
|
||||
// image key.
|
||||
AddWRVideoImage(1);
|
||||
} else {
|
||||
// Use libyuv.
|
||||
AddWRVideoImage(1);
|
||||
}
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
// Use libyuv.
|
||||
AddWRVideoImage(1);
|
||||
#elif defined(ANDROID)
|
||||
// Use libyuv.
|
||||
AddWRVideoImage(1);
|
||||
#endif
|
||||
mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
|
||||
mContainer,
|
||||
mKey,
|
||||
mExternalImageId.ref());
|
||||
if (mKey.isNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScrollingLayersHelper scroller(this, aBuilder, aSc);
|
||||
|
@ -208,37 +224,7 @@ WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
|
|||
GetLayer(),
|
||||
Stringify(filter).c_str());
|
||||
}
|
||||
|
||||
if (GetImageClientType() != CompositableType::IMAGE_BRIDGE) {
|
||||
aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mKey.value());
|
||||
} else {
|
||||
// XXX (Jerry): Remove the hardcode image format setting. The format of
|
||||
// textureClient could change from time to time. So, we just set the most
|
||||
// usable format here.
|
||||
#if defined(XP_WIN)
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(mVideoKeys.Length() == 1);
|
||||
aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
|
||||
#elif defined(XP_MACOSX)
|
||||
if (gfx::gfxVars::CanUseHardwareVideoDecoding()) {
|
||||
// Use the hardware MacIOSurface with YCbCr interleaved format.
|
||||
MOZ_ASSERT(mVideoKeys.Length() == 1);
|
||||
aBuilder.PushYCbCrInterleavedImage(sc.ToRelativeWrRect(rect), clip, mVideoKeys[0], WrYuvColorSpace::Rec601, filter);
|
||||
} else {
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(mVideoKeys.Length() == 1);
|
||||
aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
|
||||
}
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(mVideoKeys.Length() == 1);
|
||||
aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
|
||||
#elif defined(ANDROID)
|
||||
// Use libyuv to convert the buffer to rgba format.
|
||||
MOZ_ASSERT(mVideoKeys.Length() == 1);
|
||||
aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mVideoKeys[0]);
|
||||
#endif
|
||||
}
|
||||
aBuilder.PushImage(sc.ToRelativeWrRect(rect), clip, filter, mKey.value());
|
||||
}
|
||||
|
||||
Maybe<WrImageMask>
|
||||
|
|
|
@ -39,9 +39,6 @@ protected:
|
|||
void AddWRVideoImage(size_t aChannelNumber);
|
||||
|
||||
wr::MaybeExternalImageId mExternalImageId;
|
||||
// Some video image format contains multiple channel data.
|
||||
nsTArray<wr::ImageKey> mVideoKeys;
|
||||
// The regular single channel image.
|
||||
Maybe<wr::ImageKey> mKey;
|
||||
RefPtr<ImageClient> mImageClient;
|
||||
CompositableType mImageClientTypeContainer;
|
||||
|
|
|
@ -464,9 +464,16 @@ WebRenderLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserve
|
|||
void
|
||||
WebRenderLayerManager::FlushRendering()
|
||||
{
|
||||
CompositorBridgeChild* bridge = GetCompositorBridgeChild();
|
||||
if (bridge) {
|
||||
bridge->SendFlushRendering();
|
||||
CompositorBridgeChild* cBridge = GetCompositorBridgeChild();
|
||||
if (!cBridge) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mWidget);
|
||||
|
||||
if (mWidget->SynchronouslyRepaintOnResize() || gfxPrefs::LayersForceSynchronousResize()) {
|
||||
cBridge->SendFlushRendering();
|
||||
} else {
|
||||
cBridge->SendFlushRenderingAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -172,6 +172,44 @@ struct ParamTraits<WrPoint>
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<WrImageMask>
|
||||
{
|
||||
static void
|
||||
Write(Message* aMsg, const WrImageMask& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.image);
|
||||
WriteParam(aMsg, aParam.rect);
|
||||
WriteParam(aMsg, aParam.repeat);
|
||||
}
|
||||
|
||||
static bool
|
||||
Read(const Message* aMsg, PickleIterator* aIter, WrImageMask* aResult)
|
||||
{
|
||||
return ReadParam(aMsg, aIter, &aResult->image)
|
||||
&& ReadParam(aMsg, aIter, &aResult->rect)
|
||||
&& ReadParam(aMsg, aIter, &aResult->repeat);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<WrImageRendering>
|
||||
: public ContiguousEnumSerializer<
|
||||
WrImageRendering,
|
||||
WrImageRendering::Auto,
|
||||
WrImageRendering::Sentinel>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<WrMixBlendMode>
|
||||
: public ContiguousEnumSerializer<
|
||||
WrMixBlendMode,
|
||||
WrMixBlendMode::Normal,
|
||||
WrMixBlendMode::Sentinel>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<WrBuiltDisplayListDescriptor>
|
||||
{
|
||||
|
|
|
@ -502,7 +502,7 @@ private:
|
|||
DECL_OVERRIDE_PREF(Live, "layers.advanced.button-foreground-layers", LayersAllowButtonForegroundLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.canvas-background-color", LayersAllowCanvasBackgroundColorLayers, gfxPrefs::OverrideBase_WebRendest());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.caret-layers", LayersAllowCaretLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.columnRule-layers", LayersAllowColumnRuleLayers, gfxPrefs::OverrideBase_WebRendest());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.columnRule-layers", LayersAllowColumnRuleLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.displaybuttonborder-layers", LayersAllowDisplayButtonBorder, gfxPrefs::OverrideBase_WebRender());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.image-layers", LayersAllowImageLayers, gfxPrefs::OverrideBase_WebRendest());
|
||||
DECL_OVERRIDE_PREF(Live, "layers.advanced.outline-layers", LayersAllowOutlineLayers, gfxPrefs::OverrideBase_WebRender());
|
||||
|
|
|
@ -23,6 +23,7 @@ class CompositorWidget;
|
|||
|
||||
namespace layers {
|
||||
class CompositorBridgeParentBase;
|
||||
class WebRenderBridgeParent;
|
||||
}
|
||||
|
||||
namespace wr {
|
||||
|
@ -125,6 +126,7 @@ protected:
|
|||
bool mUseANGLE;
|
||||
|
||||
friend class DisplayListBuilder;
|
||||
friend class layers::WebRenderBridgeParent;
|
||||
};
|
||||
|
||||
/// This is a simple C++ wrapper around WrState defined in the rust bindings.
|
||||
|
|
|
@ -1172,7 +1172,7 @@ pub struct WrState {
|
|||
#[no_mangle]
|
||||
pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId,
|
||||
content_size: WrSize) -> *mut WrState {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
|
||||
let state = Box::new(WrState {
|
||||
pipeline_id: pipeline_id,
|
||||
|
@ -1186,7 +1186,7 @@ pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId,
|
|||
/// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_state_delete(state: *mut WrState) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
|
||||
unsafe {
|
||||
Box::from_raw(state);
|
||||
|
@ -1197,7 +1197,7 @@ pub extern "C" fn wr_state_delete(state: *mut WrState) {
|
|||
pub extern "C" fn wr_dp_begin(state: &mut WrState,
|
||||
width: u32,
|
||||
height: u32) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
state.frame_builder.dl_builder.data.clear();
|
||||
|
||||
let bounds = LayoutRect::new(LayoutPoint::new(0.0, 0.0),
|
||||
|
@ -1216,7 +1216,7 @@ pub extern "C" fn wr_dp_begin(state: &mut WrState,
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_dp_end(state: &mut WrState) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
state.frame_builder.dl_builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
|
@ -1227,7 +1227,7 @@ pub extern "C" fn wr_dp_push_clip_region(state: &mut WrState,
|
|||
complex_count: usize,
|
||||
image_mask: *const WrImageMask)
|
||||
-> WrClipRegionToken {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
|
||||
let main = main.into();
|
||||
let complex_slice = make_slice(complex, complex_count);
|
||||
|
@ -1246,7 +1246,7 @@ pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState,
|
|||
opacity: *const f32,
|
||||
transform: *const WrMatrix,
|
||||
mix_blend_mode: WrMixBlendMode) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
|
||||
let bounds = bounds.into();
|
||||
|
||||
|
@ -1282,7 +1282,7 @@ pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState,
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
state.frame_builder.dl_builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
|
@ -1299,7 +1299,7 @@ pub extern "C" fn wr_dp_push_clip(state: &mut WrState,
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn wr_dp_pop_clip(state: &mut WrState) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
state.frame_builder.dl_builder.pop_clip_node();
|
||||
}
|
||||
|
||||
|
@ -1353,7 +1353,7 @@ pub extern "C" fn wr_dp_push_rect(state: &mut WrState,
|
|||
rect: WrRect,
|
||||
clip: WrClipRegionToken,
|
||||
color: WrColor) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
|
||||
state.frame_builder.dl_builder.push_rect(rect.into(), clip.into(), color.into());
|
||||
}
|
||||
|
@ -1366,7 +1366,7 @@ pub extern "C" fn wr_dp_push_image(state: &mut WrState,
|
|||
tile_spacing: WrSize,
|
||||
image_rendering: WrImageRendering,
|
||||
key: WrImageKey) {
|
||||
assert!(unsafe { is_in_main_thread() });
|
||||
assert!(unsafe { !is_in_render_thread() });
|
||||
|
||||
state.frame_builder
|
||||
.dl_builder
|
||||
|
|
|
@ -507,8 +507,7 @@ js::obj_toString(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// Step 15.
|
||||
RootedValue tag(cx);
|
||||
RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
|
||||
if (!GetProperty(cx, obj, obj, toStringTagId, &tag))
|
||||
if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toStringTag, &tag))
|
||||
return false;
|
||||
|
||||
// Step 16.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// |jit-test| error:overflow
|
||||
// |jit-test| error:overflow; allow-oom
|
||||
if (getBuildConfiguration().debug === true)
|
||||
throw "overflow";
|
||||
function f(){};
|
||||
|
|
|
@ -3200,9 +3200,8 @@ js::ToPrimitiveSlow(JSContext* cx, JSType preferredType, MutableHandleValue vp)
|
|||
RootedObject obj(cx, &vp.toObject());
|
||||
|
||||
// Steps 4-5.
|
||||
RootedId id(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive));
|
||||
RootedValue method(cx);
|
||||
if (!GetProperty(cx, obj, obj, id, &method))
|
||||
if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive, &method))
|
||||
return false;
|
||||
|
||||
// Step 6.
|
||||
|
@ -3564,6 +3563,7 @@ JSObject::dump(FILE* fp) const
|
|||
if (obj->isDelegate()) fprintf(fp, " delegate");
|
||||
if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(fp, " not_extensible");
|
||||
if (obj->isIndexed()) fprintf(fp, " indexed");
|
||||
if (obj->maybeHasInterestingSymbolProperty()) fprintf(fp, " maybe_has_interesting_symbol");
|
||||
if (obj->isBoundFunction()) fprintf(fp, " bound_function");
|
||||
if (obj->isQualifiedVarObj()) fprintf(fp, " varobj");
|
||||
if (obj->isUnqualifiedVarObj()) fprintf(fp, " unqualified_varobj");
|
||||
|
|
|
@ -274,6 +274,13 @@ class JSObject : public js::gc::Cell
|
|||
*/
|
||||
inline bool isIndexed() const;
|
||||
|
||||
/*
|
||||
* Whether there may be "interesting symbol" properties on this object. An
|
||||
* interesting symbol is a symbol for which symbol->isInterestingSymbol()
|
||||
* returns true.
|
||||
*/
|
||||
MOZ_ALWAYS_INLINE bool maybeHasInterestingSymbolProperty() const;
|
||||
|
||||
/*
|
||||
* If this object was instantiated with `new Ctor`, return the constructor's
|
||||
* display atom. Otherwise, return nullptr.
|
||||
|
@ -897,6 +904,18 @@ GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t ind
|
|||
inline bool
|
||||
GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp);
|
||||
|
||||
// Returns whether |obj| or an object on its proto chain may have an interesting
|
||||
// symbol property (see JSObject::hasInterestingSymbolProperty). If it returns
|
||||
// true, *holder is set to the object that may have this property.
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
MaybeHasInterestingSymbolProperty(JSContext* cx, JSObject* obj, Symbol* symbol,
|
||||
JSObject** holder = nullptr);
|
||||
|
||||
// Like GetProperty but optimized for interesting symbol properties like
|
||||
// @@toStringTag.
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
GetInterestingSymbolProperty(JSContext* cx, HandleObject obj, Symbol* sym, MutableHandleValue vp);
|
||||
|
||||
/*
|
||||
* ES6 [[Set]]. Carry out the assignment `obj[id] = v`.
|
||||
*
|
||||
|
|
|
@ -45,6 +45,28 @@ MaybeConvertUnboxedObjectToNative(JSContext* cx, JSObject* obj)
|
|||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
|
||||
{
|
||||
MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
|
||||
|
||||
if (!clasp->getResolve()) {
|
||||
// Sanity check: we should only have a mayResolve hook if we have a
|
||||
// resolve hook.
|
||||
MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
|
||||
// Tell the analysis our mayResolve hooks won't trigger GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
if (!mayResolve(names, id, maybeObj))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
inline js::Shape*
|
||||
|
@ -242,6 +264,50 @@ js::DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResul
|
|||
return DeleteProperty(cx, obj, id, result);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
js::MaybeHasInterestingSymbolProperty(JSContext* cx, JSObject* obj, Symbol* symbol,
|
||||
JSObject** holder)
|
||||
{
|
||||
MOZ_ASSERT(symbol->isInterestingSymbol());
|
||||
|
||||
jsid id = SYMBOL_TO_JSID(symbol);
|
||||
do {
|
||||
if (obj->maybeHasInterestingSymbolProperty() ||
|
||||
obj->hasDynamicPrototype() ||
|
||||
MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj) ||
|
||||
obj->getClass()->getGetProperty()))
|
||||
{
|
||||
if (holder)
|
||||
*holder = obj;
|
||||
return true;
|
||||
}
|
||||
obj = obj->staticPrototype();
|
||||
} while (obj);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
js::GetInterestingSymbolProperty(JSContext* cx, HandleObject obj, Symbol* sym, MutableHandleValue vp)
|
||||
{
|
||||
JSObject* holder;
|
||||
if (!MaybeHasInterestingSymbolProperty(cx, obj, sym, &holder)) {
|
||||
#ifdef DEBUG
|
||||
RootedValue receiver(cx, ObjectValue(*obj));
|
||||
RootedId id(cx, SYMBOL_TO_JSID(sym));
|
||||
if (!GetProperty(cx, obj, receiver, id, vp))
|
||||
return false;
|
||||
MOZ_ASSERT(vp.isUndefined());
|
||||
#endif
|
||||
vp.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject holderRoot(cx, holder);
|
||||
RootedValue receiver(cx, ObjectValue(*obj));
|
||||
RootedId id(cx, SYMBOL_TO_JSID(sym));
|
||||
return GetProperty(cx, holderRoot, receiver, id, vp);
|
||||
}
|
||||
|
||||
/* * */
|
||||
|
||||
|
@ -411,6 +477,23 @@ JSObject::isIndexed() const
|
|||
return hasAllFlags(js::BaseShape::INDEXED);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
JSObject::maybeHasInterestingSymbolProperty() const
|
||||
{
|
||||
const js::NativeObject* nobj;
|
||||
if (isNative()) {
|
||||
nobj = &as<js::NativeObject>();
|
||||
} else if (is<js::UnboxedPlainObject>()) {
|
||||
nobj = as<js::UnboxedPlainObject>().maybeExpando();
|
||||
if (!nobj)
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return nobj->hasAllFlags(js::BaseShape::HAS_INTERESTING_SYMBOL);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSObject::staticPrototypeIsImmutable() const
|
||||
{
|
||||
|
|
|
@ -672,28 +672,6 @@ CallResolveOp(JSContext* cx, HandleNativeObject obj, HandleId id,
|
|||
return true;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
ClassMayResolveId(const JSAtomState& names, const Class* clasp, jsid id, JSObject* maybeObj)
|
||||
{
|
||||
MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
|
||||
|
||||
if (!clasp->getResolve()) {
|
||||
// Sanity check: we should only have a mayResolve hook if we have a
|
||||
// resolve hook.
|
||||
MOZ_ASSERT(!clasp->getMayResolve(), "Class with mayResolve hook but no resolve hook");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
|
||||
// Tell the analysis our mayResolve hooks won't trigger GC.
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
if (!mayResolve(names, id, maybeObj))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <AllowGC allowGC>
|
||||
static MOZ_ALWAYS_INLINE bool
|
||||
LookupOwnPropertyInline(JSContext* cx,
|
||||
|
|
|
@ -445,6 +445,24 @@ ShouldConvertToDictionary(NativeObject* obj)
|
|||
return obj->lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE UnownedBaseShape*
|
||||
GetBaseShapeForNewShape(JSContext* cx, HandleShape last, HandleId id)
|
||||
{
|
||||
uint32_t index;
|
||||
bool indexed = IdIsIndex(id, &index);
|
||||
bool interestingSymbol = JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id)->isInterestingSymbol();
|
||||
|
||||
if (MOZ_LIKELY(!indexed && !interestingSymbol))
|
||||
return last->base()->unowned();
|
||||
|
||||
StackBaseShape base(last->base());
|
||||
if (indexed)
|
||||
base.flags |= BaseShape::INDEXED;
|
||||
else if (interestingSymbol)
|
||||
base.flags |= BaseShape::HAS_INTERESTING_SYMBOL;
|
||||
return BaseShape::getUnowned(cx, base);
|
||||
}
|
||||
|
||||
/* static */ Shape*
|
||||
NativeObject::addPropertyInternal(JSContext* cx,
|
||||
HandleNativeObject obj, HandleId id,
|
||||
|
@ -497,20 +515,9 @@ NativeObject::addPropertyInternal(JSContext* cx,
|
|||
RootedShape shape(cx);
|
||||
{
|
||||
RootedShape last(cx, obj->lastProperty());
|
||||
|
||||
uint32_t index;
|
||||
bool indexed = IdIsIndex(id, &index);
|
||||
|
||||
Rooted<UnownedBaseShape*> nbase(cx);
|
||||
if (!indexed) {
|
||||
nbase = last->base()->unowned();
|
||||
} else {
|
||||
StackBaseShape base(last->base());
|
||||
base.flags |= BaseShape::INDEXED;
|
||||
nbase = BaseShape::getUnowned(cx, base);
|
||||
if (!nbase)
|
||||
return nullptr;
|
||||
}
|
||||
Rooted<UnownedBaseShape*> nbase(cx, GetBaseShapeForNewShape(cx, last, id));
|
||||
if (!nbase)
|
||||
return nullptr;
|
||||
|
||||
Rooted<StackShape> child(cx, StackShape(nbase, id, slot, attrs, flags));
|
||||
child.updateGetterSetter(getter, setter);
|
||||
|
@ -569,17 +576,9 @@ js::ReshapeForAllocKind(JSContext* cx, Shape* shape, TaggedProto proto,
|
|||
for (unsigned i = 0; i < ids.length(); i++) {
|
||||
id = ids[i];
|
||||
|
||||
uint32_t index;
|
||||
bool indexed = IdIsIndex(id, &index);
|
||||
|
||||
Rooted<UnownedBaseShape*> nbase(cx, newShape->base()->unowned());
|
||||
if (indexed) {
|
||||
StackBaseShape base(nbase);
|
||||
base.flags |= BaseShape::INDEXED;
|
||||
nbase = BaseShape::getUnowned(cx, base);
|
||||
if (!nbase)
|
||||
return nullptr;
|
||||
}
|
||||
Rooted<UnownedBaseShape*> nbase(cx, GetBaseShapeForNewShape(cx, newShape, id));
|
||||
if (!nbase)
|
||||
return nullptr;
|
||||
|
||||
Rooted<StackShape> child(cx, StackShape(nbase, id, i, JSPROP_ENUMERATE, 0));
|
||||
newShape = cx->zone()->propertyTree().getChild(cx, newShape, child);
|
||||
|
@ -684,12 +683,8 @@ NativeObject::putProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
|
|||
|
||||
Rooted<UnownedBaseShape*> nbase(cx);
|
||||
{
|
||||
uint32_t index;
|
||||
bool indexed = IdIsIndex(id, &index);
|
||||
StackBaseShape base(obj->lastProperty()->base());
|
||||
if (indexed)
|
||||
base.flags |= BaseShape::INDEXED;
|
||||
nbase = BaseShape::getUnowned(cx, base);
|
||||
RootedShape shape(cx, obj->lastProperty());
|
||||
nbase = GetBaseShapeForNewShape(cx, shape, id);
|
||||
if (!nbase)
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -389,7 +389,7 @@ class BaseShape : public gc::TenuredCell
|
|||
DELEGATE = 0x8,
|
||||
NOT_EXTENSIBLE = 0x10,
|
||||
INDEXED = 0x20,
|
||||
/* (0x40 is unused) */
|
||||
HAS_INTERESTING_SYMBOL = 0x40,
|
||||
HAD_ELEMENTS_ACCESS = 0x80,
|
||||
WATCHED = 0x100,
|
||||
ITERATED_SINGLETON = 0x200,
|
||||
|
|
|
@ -68,6 +68,15 @@ class Symbol : public js::gc::TenuredCell
|
|||
|
||||
bool isWellKnownSymbol() const { return uint32_t(code_) < WellKnownSymbolLimit; }
|
||||
|
||||
// An "interesting symbol" is a well-known symbol, like @@toStringTag,
|
||||
// that's often looked up on random objects but is usually not present. We
|
||||
// optimize this by setting a flag on the object's BaseShape when such
|
||||
// symbol properties are added, so we can optimize lookups on objects that
|
||||
// don't have the BaseShape flag.
|
||||
bool isInterestingSymbol() const {
|
||||
return code_ == SymbolCode::toStringTag || code_ == SymbolCode::toPrimitive;
|
||||
}
|
||||
|
||||
static const JS::TraceKind TraceKind = JS::TraceKind::Symbol;
|
||||
inline void traceChildren(JSTracer* trc) {
|
||||
if (description_)
|
||||
|
|
|
@ -10,7 +10,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533845
|
|||
onload="doTest()">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<panel id="panel" width="50" height="50" onpopupshown="continueTest()">
|
||||
<panel id="panel" width="50" height="50">
|
||||
<iframe type="content" id="contentFrame" src="data:text/html,<html><body onclick='document.body.textContent=1'>This is a panel!</body></html>" width="500" height="500"/>
|
||||
</panel>
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
@ -23,7 +23,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533845
|
|||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function doTest() {
|
||||
document.getElementById('panel').showPopup();
|
||||
let panel = document.getElementById("panel");
|
||||
panel.addEventListener("popupshown", function onpopupshown() {
|
||||
panel.removeEventListener("popupshown", onpopupshown);
|
||||
continueTest();
|
||||
panel.addEventListener("popuphidden", function onpopuphidden() {
|
||||
panel.removeEventListener("popuphidden", onpopuphidden);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
panel.hidePopup();
|
||||
});
|
||||
panel.openPopup();
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
|
@ -37,7 +47,6 @@ function continueTest() {
|
|||
utils.sendMouseEvent("mousedown", x, y, 0, 1, 0);
|
||||
utils.sendMouseEvent("mouseup", x, y, 0, 1, 0);
|
||||
is(ifrwindow.document.body.textContent, "1", "Should have got a click event!");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]></script>
|
||||
|
|
|
@ -183,6 +183,21 @@ public:
|
|||
nsLayoutUtils::GetTextShadowRectsUnion(mRect, mFrame);
|
||||
return mRect.Union(shadowRect);
|
||||
}
|
||||
|
||||
virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
|
||||
{
|
||||
if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
|
||||
// On OS X, web authors can turn off subpixel text rendering using the
|
||||
// CSS property -moz-osx-font-smoothing. If they do that, we don't need
|
||||
// to use component alpha layers for the affected text.
|
||||
if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
|
||||
return nsRect();
|
||||
}
|
||||
}
|
||||
bool snap;
|
||||
return GetBounds(aBuilder, &snap);
|
||||
}
|
||||
|
||||
virtual void Paint(nsDisplayListBuilder* aBuilder,
|
||||
nsRenderingContext* aCtx) override;
|
||||
|
||||
|
|
|
@ -87,6 +87,12 @@ nsDisplayColumnRule::GetLayerState(nsDisplayListBuilder* aBuilder,
|
|||
return LAYER_NONE;
|
||||
}
|
||||
|
||||
for (auto iter = mBorderRenderers.begin(); iter != mBorderRenderers.end(); iter++) {
|
||||
if (!iter->CanCreateWebRenderCommands()) {
|
||||
return LAYER_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return LAYER_ACTIVE;
|
||||
}
|
||||
|
||||
|
|
|
@ -4904,9 +4904,7 @@ public:
|
|||
// On OS X, web authors can turn off subpixel text rendering using the
|
||||
// CSS property -moz-osx-font-smoothing. If they do that, we don't need
|
||||
// to use component alpha layers for the affected text.
|
||||
nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
|
||||
const nsStyleFont* fontStyle = f->StyleFont();
|
||||
if (fontStyle->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
|
||||
if (mFrame->StyleFont()->mFont.smoothing == NS_FONT_SMOOTHING_GRAYSCALE) {
|
||||
return nsRect();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -276,18 +276,6 @@ nsMathMLFrame::ParseNumericValue(const nsString& aString,
|
|||
aFontSizeInflation);
|
||||
}
|
||||
|
||||
// ================
|
||||
// Utils to map attributes into CSS rules (work-around to bug 69409 which
|
||||
// is not scheduled to be fixed anytime soon)
|
||||
//
|
||||
|
||||
struct
|
||||
nsCSSMapping {
|
||||
int32_t compatibility;
|
||||
const nsIAtom* attrAtom;
|
||||
const char* cssProperty;
|
||||
};
|
||||
|
||||
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
|
||||
class nsDisplayMathMLBoundingMetrics : public nsDisplayItem {
|
||||
public:
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
== min-width-1a.html pref-width-1-ref.html
|
||||
== min-width-1b.html min-width-1-ref.html
|
||||
== min-width-1c.html min-width-1-ref.html
|
||||
== min-width-2.html min-width-2-ref.html
|
||||
fuzzy-if(webrender,255,2) == min-width-2.html min-width-2-ref.html
|
||||
== column-balancing-overflow-000.html column-balancing-overflow-000.ref.html
|
||||
== column-balancing-overflow-001.html column-balancing-overflow-000.ref.html
|
||||
== column-balancing-overflow-002.html column-balancing-overflow-002.ref.html
|
||||
|
|
|
@ -131,23 +131,31 @@ fuzzy(106,354) == css-multicol-1/multicol-rule-double-000.xht css-multicol-1/mul
|
|||
fails-if(OSX||winWidget) == css-multicol-1/multicol-rule-fraction-001.xht css-multicol-1/multicol-rule-fraction-001-ref.xht
|
||||
fails-if(OSX||winWidget) == css-multicol-1/multicol-rule-fraction-002.xht css-multicol-1/multicol-rule-fraction-002-ref.xht
|
||||
fails-if(!styloVsGecko) == css-multicol-1/multicol-rule-fraction-003.xht css-multicol-1/multicol-rule-fraction-3-ref.xht
|
||||
fuzzy(106,354) == css-multicol-1/multicol-rule-groove-000.xht css-multicol-1/multicol-rule-groove-000-ref.xht
|
||||
fuzzy(106,354) fails-if(webrender) == css-multicol-1/multicol-rule-groove-000.xht css-multicol-1/multicol-rule-groove-000-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.border-layers,1) == css-multicol-1/multicol-rule-groove-000.xht css-multicol-1/multicol-rule-groove-000-ref.xht
|
||||
fuzzy(94,256) == css-multicol-1/multicol-rule-hidden-000.xht css-multicol-1/multicol-rule-hidden-000-ref.xht
|
||||
fuzzy(106,354) == css-multicol-1/multicol-rule-inset-000.xht css-multicol-1/multicol-rule-ridge-000-ref.xht
|
||||
fuzzy(106,354) fails-if(webrender) == css-multicol-1/multicol-rule-inset-000.xht css-multicol-1/multicol-rule-ridge-000-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.border-layers,1) == css-multicol-1/multicol-rule-inset-000.xht css-multicol-1/multicol-rule-ridge-000-ref.xht
|
||||
fuzzy(255,2808) == css-multicol-1/multicol-rule-large-001.xht css-multicol-1/multicol-rule-large-001-ref.xht
|
||||
fuzzy(94,256) == css-multicol-1/multicol-rule-none-000.xht css-multicol-1/multicol-rule-hidden-000-ref.xht
|
||||
fuzzy(106,354) == css-multicol-1/multicol-rule-outset-000.xht css-multicol-1/multicol-rule-groove-000-ref.xht
|
||||
fuzzy(106,354) fails-if(webrender) == css-multicol-1/multicol-rule-outset-000.xht css-multicol-1/multicol-rule-groove-000-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.border-layers,1) == css-multicol-1/multicol-rule-outset-000.xht css-multicol-1/multicol-rule-groove-000-ref.xht
|
||||
== css-multicol-1/multicol-rule-percent-001.xht css-multicol-1/multicol-containing-002-ref.xht
|
||||
fails-if(!styloVsGecko) == css-multicol-1/multicol-rule-px-001.xht css-multicol-1/multicol-rule-ref.xht
|
||||
fuzzy(106,354) == css-multicol-1/multicol-rule-ridge-000.xht css-multicol-1/multicol-rule-ridge-000-ref.xht
|
||||
fuzzy(106,354) fails-if(webrender) == css-multicol-1/multicol-rule-ridge-000.xht css-multicol-1/multicol-rule-ridge-000-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.border-layers,1) == css-multicol-1/multicol-rule-ridge-000.xht css-multicol-1/multicol-rule-ridge-000-ref.xht
|
||||
== css-multicol-1/multicol-rule-samelength-001.xht css-multicol-1/multicol-rule-samelength-001-ref.xht
|
||||
== css-multicol-1/multicol-rule-shorthand-001.xht css-multicol-1/multicol-rule-samelength-001-ref.xht
|
||||
fuzzy(106,354) == css-multicol-1/multicol-rule-solid-000.xht css-multicol-1/multicol-rule-solid-000-ref.xht
|
||||
fails-if(!styloVsGecko) == css-multicol-1/multicol-rule-stacking-001.xht css-multicol-1/multicol-rule-stacking-ref.xht
|
||||
== css-multicol-1/multicol-rule-style-groove-001.xht css-multicol-1/multicol-rule-style-groove-001-ref.xht
|
||||
== css-multicol-1/multicol-rule-style-inset-001.xht css-multicol-1/multicol-rule-style-ridge-001-ref.xht
|
||||
== css-multicol-1/multicol-rule-style-outset-001.xht css-multicol-1/multicol-rule-style-groove-001-ref.xht
|
||||
== css-multicol-1/multicol-rule-style-ridge-001.xht css-multicol-1/multicol-rule-style-ridge-001-ref.xht
|
||||
fails-if(webrender) == css-multicol-1/multicol-rule-style-groove-001.xht css-multicol-1/multicol-rule-style-groove-001-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.table,1) == css-multicol-1/multicol-rule-style-groove-001.xht css-multicol-1/multicol-rule-style-groove-001-ref.xht
|
||||
fails-if(webrender) == css-multicol-1/multicol-rule-style-inset-001.xht css-multicol-1/multicol-rule-style-ridge-001-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.table,1) == css-multicol-1/multicol-rule-style-inset-001.xht css-multicol-1/multicol-rule-style-ridge-001-ref.xht
|
||||
fails-if(webrender) == css-multicol-1/multicol-rule-style-outset-001.xht css-multicol-1/multicol-rule-style-groove-001-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.table,1) == css-multicol-1/multicol-rule-style-outset-001.xht css-multicol-1/multicol-rule-style-groove-001-ref.xht
|
||||
fails-if(webrender) == css-multicol-1/multicol-rule-style-ridge-001.xht css-multicol-1/multicol-rule-style-ridge-001-ref.xht
|
||||
skip-if(!webrender) pref(layers.advanced.table,1) == css-multicol-1/multicol-rule-style-ridge-001.xht css-multicol-1/multicol-rule-style-ridge-001-ref.xht
|
||||
fails-if(!styloVsGecko) == css-multicol-1/multicol-shorthand-001.xht css-multicol-1/multicol-rule-ref.xht
|
||||
fails-if(!styloVsGecko) == css-multicol-1/multicol-span-000.xht css-multicol-1/multicol-span-000-ref.xht
|
||||
fails-if(!styloVsGecko) == css-multicol-1/multicol-span-all-001.xht css-multicol-1/multicol-span-all-001-ref.xht
|
||||
|
|
|
@ -1724,7 +1724,7 @@ CSS_PROP_SVG(
|
|||
FillOpacity,
|
||||
CSS_PROPERTY_PARSE_VALUE,
|
||||
"",
|
||||
VARIANT_HN | VARIANT_OPENTYPE_SVG_KEYWORD,
|
||||
VARIANT_HN | VARIANT_KEYWORD,
|
||||
kContextOpacityKTable,
|
||||
offsetof(nsStyleSVG, mFillOpacity),
|
||||
eStyleAnimType_float)
|
||||
|
@ -3869,7 +3869,7 @@ CSS_PROP_SVG(
|
|||
StrokeOpacity,
|
||||
CSS_PROPERTY_PARSE_VALUE,
|
||||
"",
|
||||
VARIANT_HN | VARIANT_OPENTYPE_SVG_KEYWORD,
|
||||
VARIANT_HN | VARIANT_KEYWORD,
|
||||
kContextOpacityKTable,
|
||||
offsetof(nsStyleSVG, mStrokeOpacity),
|
||||
eStyleAnimType_float)
|
||||
|
|
|
@ -9649,6 +9649,10 @@ nsRuleNode::ComputeSVGData(void* aStartStruct,
|
|||
svg->mContextPropsBits |= NS_STYLE_CONTEXT_PROPERTY_FILL;
|
||||
} else if (atom == nsGkAtoms::stroke) {
|
||||
svg->mContextPropsBits |= NS_STYLE_CONTEXT_PROPERTY_STROKE;
|
||||
} else if (atom == nsGkAtoms::fill_opacity) {
|
||||
svg->mContextPropsBits |= NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY;
|
||||
} else if (atom == nsGkAtoms::stroke_opacity) {
|
||||
svg->mContextPropsBits |= NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1062,6 +1062,8 @@ enum class StyleWhiteSpace : uint8_t {
|
|||
// -moz-context-properties
|
||||
#define NS_STYLE_CONTEXT_PROPERTY_FILL (1 << 0)
|
||||
#define NS_STYLE_CONTEXT_PROPERTY_STROKE (1 << 1)
|
||||
#define NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY (1 << 2)
|
||||
#define NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY (1 << 3)
|
||||
|
||||
/*
|
||||
* -moz-window-shadow
|
||||
|
|
|
@ -80,7 +80,7 @@ to mochitest command.
|
|||
* Unsupported values
|
||||
* SVG-in-OpenType values not supported servo/servo#15211 bug 1355412
|
||||
* test_value_storage.html `context-` [7]
|
||||
* test_bug798843_pref.html [5]
|
||||
* test_bug798843_pref.html [3]
|
||||
* Incorrect parsing
|
||||
* different parsing bug 1364260
|
||||
* test_supports_rules.html [6]
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
<script>
|
||||
|
||||
var props = {
|
||||
"fillOpacity" : "context-stroke-opacity",
|
||||
"strokeOpacity" : "context-fill-opacity",
|
||||
"strokeDasharray" : "context-value",
|
||||
"strokeDashoffset" : "context-value",
|
||||
"strokeWidth" : "context-value"
|
||||
|
|
|
@ -395,6 +395,14 @@ SVGEmbeddingContextPaint::Hash() const
|
|||
hash = HashGeneric(hash, mStroke->ToABGR());
|
||||
}
|
||||
|
||||
if (mFillOpacity != 1.0f) {
|
||||
hash = HashGeneric(hash, mFillOpacity);
|
||||
}
|
||||
|
||||
if (mStrokeOpacity != 1.0f) {
|
||||
hash = HashGeneric(hash, mStrokeOpacity);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
|
|
@ -251,7 +251,10 @@ class SVGEmbeddingContextPaint : public SVGContextPaint
|
|||
typedef gfx::Color Color;
|
||||
|
||||
public:
|
||||
SVGEmbeddingContextPaint() {}
|
||||
SVGEmbeddingContextPaint()
|
||||
: mFillOpacity(1.0f)
|
||||
, mStrokeOpacity(1.0f)
|
||||
{}
|
||||
|
||||
bool operator==(const SVGEmbeddingContextPaint& aOther) const {
|
||||
MOZ_ASSERT(GetStrokeWidth() == aOther.GetStrokeWidth() &&
|
||||
|
@ -259,7 +262,10 @@ public:
|
|||
GetStrokeDashArray() == aOther.GetStrokeDashArray(),
|
||||
"We don't currently include these in the context information "
|
||||
"from an embedding element");
|
||||
return mFill == aOther.mFill && mStroke == aOther.mStroke;
|
||||
return mFill == aOther.mFill &&
|
||||
mStroke == aOther.mStroke &&
|
||||
mFillOpacity == aOther.mFillOpacity &&
|
||||
mStrokeOpacity == aOther.mStrokeOpacity;
|
||||
}
|
||||
|
||||
void SetFill(nscolor aFill) {
|
||||
|
@ -283,14 +289,18 @@ public:
|
|||
GetStrokePattern(const DrawTarget* aDrawTarget, float aStrokeOpacity,
|
||||
const gfxMatrix& aCTM, imgDrawingParams& aImgParams) override;
|
||||
|
||||
void SetFillOpacity(float aOpacity) {
|
||||
mFillOpacity = aOpacity;
|
||||
}
|
||||
float GetFillOpacity() const override {
|
||||
// Always 1.0f since we don't currently allow 'context-fill-opacity'
|
||||
return 1.0f;
|
||||
return mFillOpacity;
|
||||
};
|
||||
|
||||
void SetStrokeOpacity(float aOpacity) {
|
||||
mStrokeOpacity = aOpacity;
|
||||
}
|
||||
float GetStrokeOpacity() const override {
|
||||
// Always 1.0f since we don't currently allow 'context-stroke-opacity'
|
||||
return 1.0f;
|
||||
return mStrokeOpacity;
|
||||
};
|
||||
|
||||
uint32_t Hash() const override;
|
||||
|
@ -298,6 +308,8 @@ public:
|
|||
private:
|
||||
Maybe<Color> mFill;
|
||||
Maybe<Color> mStroke;
|
||||
float mFillOpacity;
|
||||
float mStrokeOpacity;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -49,6 +49,14 @@ SVGImageContext::MaybeStoreContextPaint(Maybe<SVGImageContext>& aContext,
|
|||
haveContextPaint = true;
|
||||
contextPaint->SetStroke(style->mStroke.GetColor());
|
||||
}
|
||||
if (style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY) {
|
||||
haveContextPaint = true;
|
||||
contextPaint->SetFillOpacity(style->mFillOpacity);
|
||||
}
|
||||
if (style->mContextPropsBits & NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY) {
|
||||
haveContextPaint = true;
|
||||
contextPaint->SetStrokeOpacity(style->mStrokeOpacity);
|
||||
}
|
||||
|
||||
if (haveContextPaint) {
|
||||
if (!aContext) {
|
||||
|
|
|
@ -567,6 +567,8 @@ class AvailableEvent final : public Runnable
|
|||
: mStream(stream)
|
||||
, mCallback(callback)
|
||||
, mDoingCallback(false)
|
||||
, mSize(0)
|
||||
, mResultForCallback(NS_OK)
|
||||
{
|
||||
mCallbackTarget = NS_GetCurrentThread();
|
||||
}
|
||||
|
|
|
@ -24,8 +24,13 @@ EXPORTS += [
|
|||
'CacheStorageService.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
SOURCES += [
|
||||
'AppCacheStorage.cpp',
|
||||
'CacheStorage.cpp',
|
||||
]
|
||||
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CacheEntry.cpp',
|
||||
'CacheFile.cpp',
|
||||
'CacheFileChunk.cpp',
|
||||
|
@ -42,7 +47,6 @@ UNIFIED_SOURCES += [
|
|||
'CacheIOThread.cpp',
|
||||
'CacheLog.cpp',
|
||||
'CacheObserver.cpp',
|
||||
'CacheStorage.cpp',
|
||||
'CacheStorageService.cpp',
|
||||
'OldWrappers.cpp',
|
||||
]
|
||||
|
|
|
@ -335,9 +335,10 @@ class Bootstrapper(object):
|
|||
print(STYLO_REQUIRES_CLONE)
|
||||
sys.exit(1)
|
||||
|
||||
self.instance.stylo = True
|
||||
self.instance.state_dir = state_dir
|
||||
self.instance.ensure_stylo_packages(state_dir, checkout_root)
|
||||
self.instance.stylo = wants_stylo
|
||||
if wants_stylo:
|
||||
self.instance.state_dir = state_dir
|
||||
self.instance.ensure_stylo_packages(state_dir, checkout_root)
|
||||
|
||||
print(self.finished % name)
|
||||
if not (self.instance.which('rustc') and self.instance._parse_version('rustc') >= MODERN_RUST_VERSION):
|
||||
|
|
|
@ -494,6 +494,52 @@ uint32_t ActivePS::sNextGeneration = 0;
|
|||
// The mutex that guards accesses to CorePS and ActivePS.
|
||||
static PSMutex gPSMutex;
|
||||
|
||||
// The preferred way to check profiler activeness and features is via
|
||||
// ActivePS(). However, that requires locking gPSMutex. There are some hot
|
||||
// operations where absolute precision isn't required, so we duplicate the
|
||||
// activeness/feature state in a lock-free manner in this class.
|
||||
class RacyFeatures
|
||||
{
|
||||
public:
|
||||
static void SetActive(uint32_t aFeatures)
|
||||
{
|
||||
sActiveAndFeatures = Active | aFeatures;
|
||||
}
|
||||
|
||||
static void SetInactive() { sActiveAndFeatures = 0; }
|
||||
|
||||
static bool IsActive() { return uint32_t(sActiveAndFeatures) & Active; }
|
||||
|
||||
static bool IsActiveWithFeature(uint32_t aFeature)
|
||||
{
|
||||
uint32_t af = sActiveAndFeatures; // copy it first
|
||||
return (af & Active) && (af & aFeature);
|
||||
}
|
||||
|
||||
static bool IsActiveWithoutPrivacy()
|
||||
{
|
||||
uint32_t af = sActiveAndFeatures; // copy it first
|
||||
return (af & Active) && !(af & ProfilerFeature::Privacy);
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint32_t Active = 1u << 31;
|
||||
|
||||
// Ensure Active doesn't overlap with any of the feature bits.
|
||||
#define NO_OVERLAP(n_, str_, Name_) \
|
||||
static_assert(ProfilerFeature::Name_ != Active, "bad Active value");
|
||||
|
||||
PROFILER_FOR_EACH_FEATURE(NO_OVERLAP);
|
||||
|
||||
#undef NO_OVERLAP
|
||||
|
||||
// We combine the active bit with the feature bits so they can be read or
|
||||
// written in a single atomic operation.
|
||||
static Atomic<uint32_t> sActiveAndFeatures;
|
||||
};
|
||||
|
||||
Atomic<uint32_t> RacyFeatures::sActiveAndFeatures(0);
|
||||
|
||||
// Each live thread has a ThreadInfo, and we store a reference to it in TLS.
|
||||
// This class encapsulates that TLS.
|
||||
class TLSInfo
|
||||
|
@ -2410,6 +2456,9 @@ locked_profiler_start(PSLockRef aLock, int aEntries, double aInterval,
|
|||
mozilla::java::GeckoJavaSampler::Start(javaInterval, 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
// At the very end, set up RacyFeatures.
|
||||
RacyFeatures::SetActive(ActivePS::Features(aLock));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2455,6 +2504,9 @@ locked_profiler_stop(PSLockRef aLock)
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
|
||||
|
||||
// At the very start, clear RacyFeatures.
|
||||
RacyFeatures::SetInactive();
|
||||
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
if (ActivePS::FeatureTaskTracer(aLock)) {
|
||||
mozilla::tasktracer::StopLogging();
|
||||
|
@ -2598,13 +2650,8 @@ profiler_feature_active(uint32_t aFeature)
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
if (!ActivePS::Exists(lock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!(ActivePS::Features(lock) & aFeature);
|
||||
// This function is hot enough that we use RacyFeatures, not ActivePS.
|
||||
return RacyFeatures::IsActiveWithFeature(aFeature);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2614,9 +2661,8 @@ profiler_is_active()
|
|||
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
return ActivePS::Exists(lock);
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
return RacyFeatures::IsActive();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2851,16 +2897,21 @@ profiler_get_backtrace_noalloc(char *output, size_t outputSize)
|
|||
}
|
||||
|
||||
static void
|
||||
locked_profiler_add_marker(PSLockRef aLock, const char* aMarkerName,
|
||||
ProfilerMarkerPayload* aPayload)
|
||||
racy_profiler_add_marker(const char* aMarkerName,
|
||||
ProfilerMarkerPayload* aPayload)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
MOZ_RELEASE_ASSERT(ActivePS::Exists(aLock) &&
|
||||
!ActivePS::FeaturePrivacy(aLock));
|
||||
|
||||
// aPayload must be freed if we return early.
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
|
||||
|
||||
// We don't assert that RacyFeatures::IsActiveWithoutPrivacy() is true here,
|
||||
// because it's possible that the result has changed since we tested it in
|
||||
// the caller.
|
||||
//
|
||||
// Because of this imprecision it's possible to miss a marker or record one
|
||||
// we shouldn't. Either way is not a big deal.
|
||||
|
||||
RacyThreadInfo* racyInfo = TLSInfo::RacyInfo();
|
||||
if (!racyInfo) {
|
||||
return;
|
||||
|
@ -2879,16 +2930,15 @@ profiler_add_marker(const char* aMarkerName, ProfilerMarkerPayload* aPayload)
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
// aPayload must be freed if we return early.
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> payload(aPayload);
|
||||
|
||||
if (!ActivePS::Exists(lock) || ActivePS::FeaturePrivacy(lock)) {
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
if (!RacyFeatures::IsActiveWithoutPrivacy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
locked_profiler_add_marker(lock, aMarkerName, payload.release());
|
||||
racy_profiler_add_marker(aMarkerName, payload.release());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2897,14 +2947,13 @@ profiler_tracing(const char* aCategory, const char* aMarkerName,
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
if (!ActivePS::Exists(lock) || ActivePS::FeaturePrivacy(lock)) {
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
if (!RacyFeatures::IsActiveWithoutPrivacy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto payload = new ProfilerMarkerTracing(aCategory, aKind);
|
||||
locked_profiler_add_marker(lock, aMarkerName, payload);
|
||||
racy_profiler_add_marker(aMarkerName, payload);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2913,15 +2962,14 @@ profiler_tracing(const char* aCategory, const char* aMarkerName,
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
if (!ActivePS::Exists(lock) || ActivePS::FeaturePrivacy(lock)) {
|
||||
// This function is hot enough that we use RacyFeatures, notActivePS.
|
||||
if (!RacyFeatures::IsActiveWithoutPrivacy()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto payload =
|
||||
new ProfilerMarkerTracing(aCategory, aKind, mozilla::Move(aCause));
|
||||
locked_profiler_add_marker(lock, aMarkerName, payload);
|
||||
racy_profiler_add_marker(aMarkerName, payload);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ProfileGatherer.h"
|
||||
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* When a subprocess exits before we've gathered profiles, we'll
|
||||
* store profiles for those processes until gathering starts. We'll
|
||||
* only store up to MAX_SUBPROCESS_EXIT_PROFILES. The buffer is
|
||||
* circular, so as soon as we receive another exit profile, we'll
|
||||
* bump the oldest one out of the buffer.
|
||||
*/
|
||||
static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
|
||||
|
||||
NS_IMPL_ISUPPORTS0(ProfileGatherer)
|
||||
|
||||
ProfileGatherer::ProfileGatherer()
|
||||
: mPendingProfiles(0)
|
||||
, mGathering(false)
|
||||
{
|
||||
}
|
||||
|
||||
ProfileGatherer::~ProfileGatherer()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
void
|
||||
ProfileGatherer::GatheredOOPProfile(const nsACString& aProfile)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mGathering) {
|
||||
// If we're not actively gathering, then we don't actually
|
||||
// care that we gathered a profile here. This can happen for
|
||||
// processes that exit while profiling.
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(mWriter.isSome(), "Should always have a writer if mGathering is true");
|
||||
|
||||
if (!aProfile.IsEmpty()) {
|
||||
mWriter->Splice(PromiseFlatCString(aProfile).get());
|
||||
}
|
||||
|
||||
mPendingProfiles--;
|
||||
|
||||
if (mPendingProfiles == 0) {
|
||||
// We've got all of the async profiles now. Let's
|
||||
// finish off the profile and resolve the Promise.
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ProfileGatherer::ProfileGatherPromise>
|
||||
ProfileGatherer::Start(double aSinceTime)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mGathering) {
|
||||
// If we're already gathering, return a rejected promise - this isn't
|
||||
// going to end well.
|
||||
return ProfileGatherPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
|
||||
}
|
||||
|
||||
mGathering = true;
|
||||
|
||||
// Request profiles from the other processes. This will trigger
|
||||
// asynchronous calls to ProfileGatherer::GatheredOOPProfile as the
|
||||
// profiles arrive.
|
||||
// Do this before the call to profiler_stream_json_for_this_process because
|
||||
// that call is slow and we want to let the other processes grab their
|
||||
// profiles as soon as possible.
|
||||
nsTArray<RefPtr<ProfilerParent::SingleProcessProfilePromise>> profiles =
|
||||
ProfilerParent::GatherProfiles();
|
||||
|
||||
mWriter.emplace();
|
||||
|
||||
// Start building up the JSON result and grab the profile from this process.
|
||||
mWriter->Start(SpliceableJSONWriter::SingleLineStyle);
|
||||
if (!profiler_stream_json_for_this_process(*mWriter, aSinceTime)) {
|
||||
// The profiler is inactive. This either means that it was inactive even
|
||||
// at the time that ProfileGatherer::Start() was called, or that it was
|
||||
// stopped on a different thread since that call. Either way, we need to
|
||||
// reject the promise and stop gathering.
|
||||
return ProfileGatherPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
|
||||
}
|
||||
|
||||
mWriter->StartArrayProperty("processes");
|
||||
|
||||
// If we have any process exit profiles, add them immediately, and clear
|
||||
// mExitProfiles.
|
||||
for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
|
||||
if (!mExitProfiles[i].IsEmpty()) {
|
||||
mWriter->Splice(mExitProfiles[i].get());
|
||||
}
|
||||
}
|
||||
mExitProfiles.Clear();
|
||||
|
||||
mPromiseHolder.emplace();
|
||||
RefPtr<ProfileGatherPromise> promise = mPromiseHolder->Ensure(__func__);
|
||||
|
||||
// Keep the array property "processes" and the root object in mWriter open
|
||||
// until Finish() is called. As profiles from the other processes come in,
|
||||
// they will be inserted and end up in the right spot. Finish() will close
|
||||
// the array and the root object.
|
||||
|
||||
mPendingProfiles = profiles.Length();
|
||||
RefPtr<ProfileGatherer> self = this;
|
||||
for (auto profile : profiles) {
|
||||
profile->Then(AbstractThread::MainThread(), __func__,
|
||||
[self](const nsCString& aResult) {
|
||||
self->GatheredOOPProfile(aResult);
|
||||
},
|
||||
[self](PromiseRejectReason aReason) {
|
||||
self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
|
||||
});
|
||||
}
|
||||
if (!mPendingProfiles) {
|
||||
Finish();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void
|
||||
ProfileGatherer::Finish()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(mWriter.isSome());
|
||||
MOZ_RELEASE_ASSERT(mPromiseHolder.isSome());
|
||||
|
||||
// Close the "processes" array property.
|
||||
mWriter->EndArray();
|
||||
|
||||
// Close the root object of the generated JSON.
|
||||
mWriter->End();
|
||||
|
||||
UniquePtr<char[]> buf = mWriter->WriteFunc()->CopyData();
|
||||
nsCString result(buf.get());
|
||||
mPromiseHolder->Resolve(result, __func__);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
ProfileGatherer::Reset()
|
||||
{
|
||||
mPromiseHolder.reset();
|
||||
mPendingProfiles = 0;
|
||||
mGathering = false;
|
||||
mWriter.reset();
|
||||
}
|
||||
|
||||
void
|
||||
ProfileGatherer::Cancel()
|
||||
{
|
||||
// If we have a Promise in flight, we should reject it.
|
||||
if (mPromiseHolder.isSome()) {
|
||||
mPromiseHolder->RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
ProfileGatherer::OOPExitProfile(const nsACString& aProfile)
|
||||
{
|
||||
// Append the exit profile to mExitProfiles so that it can be picked up the
|
||||
// next time a profile is requested. If we're currently gathering a profile,
|
||||
// do not add this exit profile to it; chances are that we already have a
|
||||
// profile from the exiting process and we don't want another one.
|
||||
// We only keep around at most MAX_SUBPROCESS_EXIT_PROFILES exit profiles.
|
||||
if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
|
||||
mExitProfiles.RemoveElementAt(0);
|
||||
}
|
||||
mExitProfiles.AppendElement(aProfile);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,42 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZ_PROFILE_GATHERER_H
|
||||
#define MOZ_PROFILE_GATHERER_H
|
||||
|
||||
#include "nsIFile.h"
|
||||
#include "ProfileJSONWriter.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// This class holds the state for an async profile-gathering request.
|
||||
class ProfileGatherer final : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
typedef MozPromise<nsCString, nsresult, false> ProfileGatherPromise;
|
||||
|
||||
explicit ProfileGatherer();
|
||||
void GatheredOOPProfile(const nsACString& aProfile);
|
||||
RefPtr<ProfileGatherPromise> Start(double aSinceTime);
|
||||
void OOPExitProfile(const nsACString& aProfile);
|
||||
|
||||
private:
|
||||
~ProfileGatherer();
|
||||
void Cancel();
|
||||
void Finish();
|
||||
void Reset();
|
||||
|
||||
nsTArray<nsCString> mExitProfiles;
|
||||
Maybe<MozPromiseHolder<ProfileGatherPromise>> mPromiseHolder;
|
||||
Maybe<SpliceableChunkedJSONWriter> mWriter;
|
||||
uint32_t mPendingProfiles;
|
||||
bool mGathering;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -23,8 +23,8 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "ProfileGatherer.h"
|
||||
#include "nsLocalFile.h"
|
||||
#include "ProfilerParent.h"
|
||||
#include "platform.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -37,13 +37,9 @@ NS_IMPL_ISUPPORTS(nsProfiler, nsIProfiler)
|
|||
|
||||
nsProfiler::nsProfiler()
|
||||
: mLockedForPrivateBrowsing(false)
|
||||
, mPendingProfiles(0)
|
||||
, mGathering(false)
|
||||
{
|
||||
// If MOZ_PROFILER_STARTUP is set, the profiler will already be running. We
|
||||
// need to create a ProfileGatherer in that case.
|
||||
// XXX: this is probably not the best approach. See bug 1356694 for details.
|
||||
if (profiler_is_active()) {
|
||||
mGatherer = new ProfileGatherer();
|
||||
}
|
||||
}
|
||||
|
||||
nsProfiler::~nsProfiler()
|
||||
|
@ -125,10 +121,9 @@ nsProfiler::StartProfiler(uint32_t aEntries, double aInterval,
|
|||
|
||||
#undef ADD_FEATURE_BIT
|
||||
|
||||
profiler_start(aEntries, aInterval, features, aFilters, aFilterCount);
|
||||
ResetGathering();
|
||||
|
||||
// Do this after profiler_start().
|
||||
mGatherer = new ProfileGatherer();
|
||||
profiler_start(aEntries, aInterval, features, aFilters, aFilterCount);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -136,8 +131,7 @@ nsProfiler::StartProfiler(uint32_t aEntries, double aInterval,
|
|||
NS_IMETHODIMP
|
||||
nsProfiler::StopProfiler()
|
||||
{
|
||||
// Do this before profiler_stop().
|
||||
mGatherer = nullptr;
|
||||
CancelGathering();
|
||||
|
||||
profiler_stop();
|
||||
|
||||
|
@ -253,7 +247,7 @@ nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mGatherer) {
|
||||
if (!profiler_is_active()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -274,7 +268,7 @@ nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
|
|||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
mGatherer->Start(aSinceTime)->Then(
|
||||
StartGathering(aSinceTime)->Then(
|
||||
AbstractThread::MainThread(), __func__,
|
||||
[promise](nsCString aResult) {
|
||||
AutoJSAPI jsapi;
|
||||
|
@ -321,7 +315,7 @@ nsProfiler::GetProfileDataAsArrayBuffer(double aSinceTime, JSContext* aCx,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mGatherer) {
|
||||
if (!profiler_is_active()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -342,7 +336,7 @@ nsProfiler::GetProfileDataAsArrayBuffer(double aSinceTime, JSContext* aCx,
|
|||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
mGatherer->Start(aSinceTime)->Then(
|
||||
StartGathering(aSinceTime)->Then(
|
||||
AbstractThread::MainThread(), __func__,
|
||||
[promise](nsCString aResult) {
|
||||
AutoJSAPI jsapi;
|
||||
|
@ -377,13 +371,13 @@ nsProfiler::DumpProfileToFileAsync(const nsACString& aFilename,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mGatherer) {
|
||||
if (!profiler_is_active()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCString filename(aFilename);
|
||||
|
||||
mGatherer->Start(aSinceTime)->Then(
|
||||
StartGathering(aSinceTime)->Then(
|
||||
AbstractThread::MainThread(), __func__,
|
||||
[filename](const nsCString& aResult) {
|
||||
nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||
|
@ -496,21 +490,166 @@ nsProfiler::GatheredOOPProfile(const nsACString& aProfile)
|
|||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mGatherer) {
|
||||
if (!profiler_is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGatherer->GatheredOOPProfile(aProfile);
|
||||
if (!mGathering) {
|
||||
// If we're not actively gathering, then we don't actually care that we
|
||||
// gathered a profile here. This can happen for processes that exit while
|
||||
// profiling.
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(mWriter.isSome(),
|
||||
"Should always have a writer if mGathering is true");
|
||||
|
||||
if (!aProfile.IsEmpty()) {
|
||||
mWriter->Splice(PromiseFlatCString(aProfile).get());
|
||||
}
|
||||
|
||||
mPendingProfiles--;
|
||||
|
||||
if (mPendingProfiles == 0) {
|
||||
// We've got all of the async profiles now. Let's
|
||||
// finish off the profile and resolve the Promise.
|
||||
FinishGathering();
|
||||
}
|
||||
}
|
||||
|
||||
// When a subprocess exits before we've gathered profiles, we'll store profiles
|
||||
// for those processes until gathering starts. We'll only store up to
|
||||
// MAX_SUBPROCESS_EXIT_PROFILES. The buffer is circular, so as soon as we
|
||||
// receive another exit profile, we'll bump the oldest one out of the buffer.
|
||||
static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
|
||||
|
||||
void
|
||||
nsProfiler::ReceiveShutdownProfile(const nsCString& aProfile)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mGatherer) {
|
||||
if (!profiler_is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mGatherer->OOPExitProfile(aProfile);
|
||||
// Append the exit profile to mExitProfiles so that it can be picked up the
|
||||
// next time a profile is requested. If we're currently gathering a profile,
|
||||
// do not add this exit profile to it; chances are that we already have a
|
||||
// profile from the exiting process and we don't want another one.
|
||||
// We only keep around at most MAX_SUBPROCESS_EXIT_PROFILES exit profiles.
|
||||
if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
|
||||
mExitProfiles.RemoveElementAt(0);
|
||||
}
|
||||
mExitProfiles.AppendElement(aProfile);
|
||||
}
|
||||
|
||||
RefPtr<nsProfiler::GatheringPromise>
|
||||
nsProfiler::StartGathering(double aSinceTime)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mGathering) {
|
||||
// If we're already gathering, return a rejected promise - this isn't
|
||||
// going to end well.
|
||||
return GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
|
||||
}
|
||||
|
||||
mGathering = true;
|
||||
|
||||
// Request profiles from the other processes. This will trigger asynchronous
|
||||
// calls to ProfileGatherer::GatheredOOPProfile as the profiles arrive.
|
||||
//
|
||||
// Do this before the call to profiler_stream_json_for_this_process() because
|
||||
// that call is slow and we want to let the other processes grab their
|
||||
// profiles as soon as possible.
|
||||
nsTArray<RefPtr<ProfilerParent::SingleProcessProfilePromise>> profiles =
|
||||
ProfilerParent::GatherProfiles();
|
||||
|
||||
mWriter.emplace();
|
||||
|
||||
// Start building up the JSON result and grab the profile from this process.
|
||||
mWriter->Start(SpliceableJSONWriter::SingleLineStyle);
|
||||
if (!profiler_stream_json_for_this_process(*mWriter, aSinceTime)) {
|
||||
// The profiler is inactive. This either means that it was inactive even
|
||||
// at the time that ProfileGatherer::Start() was called, or that it was
|
||||
// stopped on a different thread since that call. Either way, we need to
|
||||
// reject the promise and stop gathering.
|
||||
return GatheringPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
|
||||
}
|
||||
|
||||
mWriter->StartArrayProperty("processes");
|
||||
|
||||
// If we have any process exit profiles, add them immediately, and clear
|
||||
// mExitProfiles.
|
||||
for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
|
||||
if (!mExitProfiles[i].IsEmpty()) {
|
||||
mWriter->Splice(mExitProfiles[i].get());
|
||||
}
|
||||
}
|
||||
mExitProfiles.Clear();
|
||||
|
||||
mPromiseHolder.emplace();
|
||||
RefPtr<GatheringPromise> promise = mPromiseHolder->Ensure(__func__);
|
||||
|
||||
// Keep the array property "processes" and the root object in mWriter open
|
||||
// until FinishGathering() is called. As profiles from the other processes
|
||||
// come in, they will be inserted and end up in the right spot.
|
||||
// FinishGathering() will close the array and the root object.
|
||||
|
||||
mPendingProfiles = profiles.Length();
|
||||
RefPtr<nsProfiler> self = this;
|
||||
for (auto profile : profiles) {
|
||||
profile->Then(AbstractThread::MainThread(), __func__,
|
||||
[self](const nsCString& aResult) {
|
||||
self->GatheredOOPProfile(aResult);
|
||||
},
|
||||
[self](ipc::PromiseRejectReason aReason) {
|
||||
self->GatheredOOPProfile(NS_LITERAL_CSTRING(""));
|
||||
});
|
||||
}
|
||||
if (!mPendingProfiles) {
|
||||
FinishGathering();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void
|
||||
nsProfiler::CancelGathering()
|
||||
{
|
||||
// If we have a Promise in flight, we should reject it.
|
||||
if (mPromiseHolder.isSome()) {
|
||||
mPromiseHolder->RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
|
||||
}
|
||||
ResetGathering();
|
||||
}
|
||||
|
||||
void
|
||||
nsProfiler::FinishGathering()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(mWriter.isSome());
|
||||
MOZ_RELEASE_ASSERT(mPromiseHolder.isSome());
|
||||
|
||||
// Close the "processes" array property.
|
||||
mWriter->EndArray();
|
||||
|
||||
// Close the root object of the generated JSON.
|
||||
mWriter->End();
|
||||
|
||||
UniquePtr<char[]> buf = mWriter->WriteFunc()->CopyData();
|
||||
nsCString result(buf.get());
|
||||
mPromiseHolder->Resolve(result, __func__);
|
||||
|
||||
ResetGathering();
|
||||
}
|
||||
|
||||
void
|
||||
nsProfiler::ResetGathering()
|
||||
{
|
||||
mPromiseHolder.reset();
|
||||
mPendingProfiles = 0;
|
||||
mGathering = false;
|
||||
mWriter.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,46 +1,59 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef _NSPROFILER_H_
|
||||
#define _NSPROFILER_H_
|
||||
#ifndef nsProfiler_h
|
||||
#define nsProfiler_h
|
||||
|
||||
#include "nsIProfiler.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ProfileGatherer;
|
||||
}
|
||||
#include "ProfileJSONWriter.h"
|
||||
|
||||
class nsProfiler final : public nsIProfiler, public nsIObserver
|
||||
{
|
||||
public:
|
||||
nsProfiler();
|
||||
nsProfiler();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIPROFILER
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIPROFILER
|
||||
|
||||
nsresult Init();
|
||||
nsresult Init();
|
||||
|
||||
static nsProfiler* GetOrCreate()
|
||||
{
|
||||
nsCOMPtr<nsIProfiler> iprofiler =
|
||||
do_GetService("@mozilla.org/tools/profiler;1");
|
||||
return static_cast<nsProfiler*>(iprofiler.get());
|
||||
}
|
||||
static nsProfiler* GetOrCreate()
|
||||
{
|
||||
nsCOMPtr<nsIProfiler> iprofiler =
|
||||
do_GetService("@mozilla.org/tools/profiler;1");
|
||||
return static_cast<nsProfiler*>(iprofiler.get());
|
||||
}
|
||||
|
||||
void GatheredOOPProfile(const nsACString& aProfile);
|
||||
void GatheredOOPProfile(const nsACString& aProfile);
|
||||
|
||||
private:
|
||||
~nsProfiler();
|
||||
~nsProfiler();
|
||||
|
||||
RefPtr<mozilla::ProfileGatherer> mGatherer;
|
||||
bool mLockedForPrivateBrowsing;
|
||||
typedef mozilla::MozPromise<nsCString, nsresult, false> GatheringPromise;
|
||||
|
||||
RefPtr<GatheringPromise> StartGathering(double aSinceTime);
|
||||
void CancelGathering();
|
||||
void FinishGathering();
|
||||
void ResetGathering();
|
||||
|
||||
bool mLockedForPrivateBrowsing;
|
||||
|
||||
// These fields are all related to profile gathering.
|
||||
nsTArray<nsCString> mExitProfiles;
|
||||
mozilla::Maybe<mozilla::MozPromiseHolder<GatheringPromise>> mPromiseHolder;
|
||||
mozilla::Maybe<SpliceableChunkedJSONWriter> mWriter;
|
||||
uint32_t mPendingProfiles;
|
||||
bool mGathering;
|
||||
};
|
||||
|
||||
#endif /* _NSPROFILER_H_ */
|
||||
#endif // nsProfiler_h
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ if CONFIG['MOZ_GECKO_PROFILER']:
|
|||
'gecko/ChildProfilerController.cpp',
|
||||
'gecko/nsProfilerFactory.cpp',
|
||||
'gecko/nsProfilerStartParams.cpp',
|
||||
'gecko/ProfileGatherer.cpp',
|
||||
'gecko/ProfilerChild.cpp',
|
||||
'gecko/ProfilerIOInterposeObserver.cpp',
|
||||
'gecko/ProfilerParent.cpp',
|
||||
|
|
|
@ -290,7 +290,9 @@ ProfilerBacktraceDestructor::operator()(ProfilerBacktrace* aBacktrace) {}
|
|||
PROFILER_FUNC(bool profiler_is_active(), false)
|
||||
|
||||
// Check if a profiler feature (specified via the ProfilerFeature type) is
|
||||
// active. Returns false if the profiler is inactive.
|
||||
// active. Returns false if the profiler is inactive. Note: the return value
|
||||
// can become immediately out-of-date, much like the return value of
|
||||
// profiler_is_active().
|
||||
PROFILER_FUNC(bool profiler_feature_active(uint32_t aFeature), false)
|
||||
|
||||
// Get the profile encoded as a JSON string. A no-op (returning nullptr) if the
|
||||
|
|
|
@ -30,7 +30,7 @@ using namespace mozilla;
|
|||
|
||||
typedef Vector<const char*> StrVec;
|
||||
|
||||
void
|
||||
static void
|
||||
InactiveFeaturesAndParamsCheck()
|
||||
{
|
||||
int entries;
|
||||
|
@ -51,7 +51,7 @@ InactiveFeaturesAndParamsCheck()
|
|||
ASSERT_TRUE(filters.empty());
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
ActiveParamsCheck(int aEntries, double aInterval, uint32_t aFeatures,
|
||||
const char** aFilters, size_t aFiltersLen)
|
||||
{
|
||||
|
@ -320,6 +320,45 @@ TEST(GeckoProfiler, Pause)
|
|||
ASSERT_TRUE(!profiler_is_paused());
|
||||
}
|
||||
|
||||
// A class that keeps track of how many instances have been created, streamed,
|
||||
// and destroyed.
|
||||
class GTestPayload : public ProfilerMarkerPayload
|
||||
{
|
||||
public:
|
||||
explicit GTestPayload(int aN)
|
||||
: mN(aN)
|
||||
{
|
||||
sNumCreated++;
|
||||
}
|
||||
|
||||
virtual ~GTestPayload() { sNumDestroyed++; }
|
||||
|
||||
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
|
||||
const mozilla::TimeStamp& aStartTime,
|
||||
UniqueStacks& aUniqueStacks) override
|
||||
{
|
||||
streamCommonProps("gtest", aWriter, aStartTime, aUniqueStacks);
|
||||
char buf[64];
|
||||
SprintfLiteral(buf, "gtest-%d", mN);
|
||||
aWriter.IntProperty(buf, mN);
|
||||
sNumStreamed++;
|
||||
}
|
||||
|
||||
private:
|
||||
int mN;
|
||||
|
||||
public:
|
||||
// The number of GTestPayload instances that have been created, streamed, and
|
||||
// destroyed.
|
||||
static int sNumCreated;
|
||||
static int sNumStreamed;
|
||||
static int sNumDestroyed;
|
||||
};
|
||||
|
||||
int GTestPayload::sNumCreated = 0;
|
||||
int GTestPayload::sNumStreamed = 0;
|
||||
int GTestPayload::sNumDestroyed = 0;
|
||||
|
||||
TEST(GeckoProfiler, Markers)
|
||||
{
|
||||
uint32_t features = ProfilerFeature::StackWalk;
|
||||
|
@ -349,7 +388,50 @@ TEST(GeckoProfiler, Markers)
|
|||
"M4", new ProfilerMarkerTracing("C", TRACING_EVENT,
|
||||
profiler_get_backtrace()));
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
PROFILER_MARKER_PAYLOAD("M5", new GTestPayload(i));
|
||||
}
|
||||
|
||||
// Sleep briefly to ensure a sample is taken and the pending markers are
|
||||
// processed.
|
||||
PR_Sleep(PR_MillisecondsToInterval(500));
|
||||
|
||||
SpliceableChunkedJSONWriter w;
|
||||
ASSERT_TRUE(profiler_stream_json_for_this_process(w));
|
||||
|
||||
UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
|
||||
|
||||
// The GTestPayloads should have been created and streamed, but not yet
|
||||
// destroyed.
|
||||
ASSERT_TRUE(GTestPayload::sNumCreated == 10);
|
||||
ASSERT_TRUE(GTestPayload::sNumStreamed == 10);
|
||||
ASSERT_TRUE(GTestPayload::sNumDestroyed == 0);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
char buf[64];
|
||||
SprintfLiteral(buf, "\"gtest-%d\"", i);
|
||||
ASSERT_TRUE(strstr(profile.get(), buf));
|
||||
}
|
||||
|
||||
profiler_stop();
|
||||
|
||||
// The GTestPayloads should have been destroyed.
|
||||
ASSERT_TRUE(GTestPayload::sNumDestroyed == 10);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
PROFILER_MARKER_PAYLOAD("M5", new GTestPayload(i));
|
||||
}
|
||||
|
||||
profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
|
||||
features, filters, MOZ_ARRAY_LENGTH(filters));
|
||||
|
||||
ASSERT_TRUE(profiler_stream_json_for_this_process(w));
|
||||
|
||||
profiler_stop();
|
||||
|
||||
// The second set of GTestPayloads should not have been streamed.
|
||||
ASSERT_TRUE(GTestPayload::sNumCreated == 20);
|
||||
ASSERT_TRUE(GTestPayload::sNumStreamed == 10);
|
||||
ASSERT_TRUE(GTestPayload::sNumDestroyed == 20);
|
||||
}
|
||||
|
||||
TEST(GeckoProfiler, Time)
|
||||
|
@ -394,6 +476,29 @@ TEST(GeckoProfiler, GetProfile)
|
|||
ASSERT_TRUE(!profiler_get_profile());
|
||||
}
|
||||
|
||||
static void
|
||||
JSONOutputCheck(const char* aOutput)
|
||||
{
|
||||
// Check that various expected strings are in the JSON.
|
||||
|
||||
ASSERT_TRUE(aOutput);
|
||||
ASSERT_TRUE(aOutput[0] == '{');
|
||||
|
||||
ASSERT_TRUE(strstr(aOutput, "\"libs\""));
|
||||
|
||||
ASSERT_TRUE(strstr(aOutput, "\"meta\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"version\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"startTime\""));
|
||||
|
||||
ASSERT_TRUE(strstr(aOutput, "\"threads\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"GeckoMain\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"samples\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"markers\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"stackTable\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"frameTable\""));
|
||||
ASSERT_TRUE(strstr(aOutput, "\"stringTable\""));
|
||||
}
|
||||
|
||||
TEST(GeckoProfiler, StreamJSONForThisProcess)
|
||||
{
|
||||
uint32_t features = ProfilerFeature::StackWalk;
|
||||
|
@ -410,7 +515,8 @@ TEST(GeckoProfiler, StreamJSONForThisProcess)
|
|||
w.End();
|
||||
|
||||
UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
|
||||
ASSERT_TRUE(profile && profile[0] == '{');
|
||||
|
||||
JSONOutputCheck(profile.get());
|
||||
|
||||
profiler_stop();
|
||||
|
||||
|
@ -442,7 +548,8 @@ TEST(GeckoProfiler, StreamJSONForThisProcessThreaded)
|
|||
}), NS_DISPATCH_SYNC);
|
||||
|
||||
UniquePtr<char[]> profile = w.WriteFunc()->CopyData();
|
||||
ASSERT_TRUE(profile && profile[0] == '{');
|
||||
|
||||
JSONOutputCheck(profile.get());
|
||||
|
||||
// Stop the profiler and call profiler_stream_json_for_this_process on a
|
||||
// background thread.
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "Widget: Android")
|
||||
|
||||
with Files("*CompositorWidget*"):
|
||||
BUG_COMPONENT = ("Core", "Graphics")
|
||||
|
||||
DIRS += [
|
||||
'bindings',
|
||||
'fennec',
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "Widget: Gtk")
|
||||
|
||||
with Files("*CompositorWidget*"):
|
||||
BUG_COMPONENT = ("Core", "Graphics")
|
||||
|
||||
with Files("*WindowSurface*"):
|
||||
BUG_COMPONENT = ("Core", "Graphics")
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
|
||||
DIRS += ['mozgtk']
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@ with Files("reftests/*fallback*"):
|
|||
with Files("*CompositorWidget*"):
|
||||
BUG_COMPONENT = ("Core", "Graphics")
|
||||
|
||||
with Files("*WindowSurface*"):
|
||||
BUG_COMPONENT = ("Core", "Graphics")
|
||||
|
||||
with Files("*FontRange*"):
|
||||
BUG_COMPONENT = ("Core", "Widget: Cocoa")
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "Widget: Win32")
|
||||
|
||||
with Files("*CompositorWidget*"):
|
||||
BUG_COMPONENT = ("Core", "Graphics")
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
EXPORTS += [
|
||||
|
|