Merge inbound to mozilla central r=merge a=merge

This commit is contained in:
Bogdan Tara 2017-12-07 12:53:20 +02:00
commit cbd802ee7d
37 changed files with 996 additions and 415 deletions

View File

@ -4383,26 +4383,16 @@
let {tabParent} = browser.frameLoader;
if (state == this.STATE_LOADING) {
this.assert(!this.minimizedOrFullyOccluded);
if (!this.tabbrowser.tabWarmingEnabled) {
browser.docShellIsActive = true;
}
if (tabParent) {
browser.renderLayers = true;
} else {
browser.docShellIsActive = true;
if (!tabParent) {
this.onLayersReady(browser);
}
} else if (state == this.STATE_UNLOADING) {
this.unwarmTab(tab);
// Setting the docShell to be inactive will also cause it
// to stop rendering layers.
browser.docShellIsActive = false;
if (!tabParent) {
this.onLayersCleared(browser);
}
} else if (state == this.STATE_LOADED) {
this.maybeActivateDocShell(tab);
}
if (!tab.linkedBrowser.isRemoteBrowser) {
@ -4427,6 +4417,11 @@
init() {
this.log("START");
// If we minimized the window before the switcher was activated,
// we might have set the preserveLayers flag for the current
// browser. Let's clear it.
this.tabbrowser.mCurrentBrowser.preserveLayers(false);
window.addEventListener("MozAfterPaint", this);
window.addEventListener("MozLayerTreeReady", this);
window.addEventListener("MozLayerTreeCleared", this);
@ -4436,30 +4431,16 @@
window.addEventListener("SwapDocShells", this, true);
window.addEventListener("EndSwapDocShells", this, true);
let initialTab = this.requestedTab;
let tab = this.requestedTab;
let browser = tab.linkedBrowser;
let tabIsLoaded = !browser.isRemoteBrowser ||
browser.frameLoader.tabParent.hasPresented;
for (let tab of this.tabbrowser.tabs) {
if (tab == initialTab && this.minimizedOrFullyOccluded) {
continue;
}
if (tab.linkedPanel) {
let b = tab.linkedBrowser;
let state;
if (b.renderLayers && b.hasLayers) {
state = this.STATE_LOADED;
} else if (b.renderLayers && !b.hasLayers) {
state = this.STATE_LOADING;
} else if (!b.renderLayers && b.hasLayers) {
state = this.STATE_UNLOADING;
} else {
state = this.STATE_UNLOADED;
}
this.setTabState(tab, state);
}
if (!this.minimizedOrFullyOccluded) {
this.log("Initial tab is loaded?: " + tabIsLoaded);
this.setTabState(tab, tabIsLoaded ? this.STATE_LOADED
: this.STATE_LOADING);
}
this.logState("Tab states initialized");
},
destroy() {
@ -4638,8 +4619,6 @@
} else {
this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
}
this.maybeActivateDocShell(this.requestedTab);
}
}
@ -4678,29 +4657,6 @@
this.setTabState(this.requestedTab, this.STATE_LOADING);
},
maybeActivateDocShell(tab) {
// If we've reached the point where the requested tab has entered
// the loaded state, but the DocShell is still not yet active, we
// should activate it.
let browser = tab.linkedBrowser;
let state = this.getTabState(tab);
let canCheckDocShellState = !browser.mDestroyed &&
(browser.docShell ||
browser.frameLoader.tabParent);
if (tab == this.requestedTab &&
canCheckDocShellState &&
state == this.STATE_LOADED &&
!browser.docShellIsActive &&
!this.minimizedOrFullyOccluded) {
browser.docShellIsActive = true;
this.logState("Set requested tab docshell to active and preserveLayers to false");
// If we minimized the window before the switcher was activated,
// we might have set the preserveLayers flag for the current
// browser. Let's clear it.
browser.preserveLayers(false);
}
},
// This function runs before every event. It fixes up the state
// to account for closed tabs.
preActions() {
@ -4869,13 +4825,6 @@
// Fires when the layers become available for a tab.
onLayersReady(browser) {
let tab = this.tabbrowser.getTabForBrowser(browser);
if (!tab) {
// We probably got a layer update from a tab that got before
// the switcher was created, or for browser that's not being
// tracked by the async tab switcher (like the preloaded about:newtab).
return;
}
this.logState(`onLayersReady(${tab._tPos}, ${browser.isRemoteBrowser})`);
this.assert(this.getTabState(tab) == this.STATE_LOADING ||
@ -5021,12 +4970,7 @@
activateBrowserForPrintPreview(browser) {
let tab = this.tabbrowser.getTabForBrowser(browser);
let state = this.getTabState(tab);
if (state != this.STATE_LOADING &&
state != this.STATE_LOADED) {
this.setTabState(tab, this.STATE_LOADING);
this.logState("Activated browser " + this.tinfo(tab) + " for print preview");
}
this.setTabState(tab, this.STATE_LOADING);
},
canWarmTab(tab) {

View File

@ -25,6 +25,7 @@ CHECK(NonMemMovableTemplateArgChecker, "non-memmovable-template-arg")
CHECK(NonParamInsideFunctionDeclChecker, "non-memmovable-template-arg")
CHECK(OverrideBaseCallChecker, "override-base-call")
CHECK(OverrideBaseCallUsageChecker, "override-base-call-usage")
CHECK(ParamTraitsEnumChecker, "paramtraits-enum")
CHECK(RefCountedCopyConstructorChecker, "refcounted-copy-constructor")
CHECK(RefCountedInsideLambdaChecker, "refcounted-inside-lambda")
CHECK(ScopeChecker, "scope")

View File

@ -26,6 +26,7 @@
#include "NonParamInsideFunctionDeclChecker.h"
#include "OverrideBaseCallChecker.h"
#include "OverrideBaseCallUsageChecker.h"
#include "ParamTraitsEnumChecker.h"
#include "RefCountedCopyConstructorChecker.h"
#include "RefCountedInsideLambdaChecker.h"
#include "ScopeChecker.h"

View File

@ -0,0 +1,37 @@
/* 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 "ParamTraitsEnumChecker.h"
#include "CustomMatchers.h"
void ParamTraitsEnumChecker::registerMatchers(MatchFinder* AstMatcher) {
AstMatcher->addMatcher(
classTemplateSpecializationDecl(hasName("ParamTraits")).bind("decl"), this);
}
void ParamTraitsEnumChecker::check(const MatchFinder::MatchResult &Result) {
const ClassTemplateSpecializationDecl *Decl =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("decl");
for (auto& Inner : Decl->decls()) {
if (auto* Def = dyn_cast<TypedefDecl>(Inner)) {
QualType UnderlyingType = Def->getUnderlyingType();
QualType CanonicalType = UnderlyingType.getCanonicalType();
const clang::Type *TypePtr = CanonicalType.getTypePtrOrNull();
if (!TypePtr) {
return;
}
if (TypePtr->isEnumeralType()) {
diag(Decl->getLocStart(),
"Custom ParamTraits implementation for an enum type",
DiagnosticIDs::Error);
diag(Decl->getLocStart(),
"Please use a helper class for example ContiguousEnumSerializer",
DiagnosticIDs::Note);
}
}
}
}

View File

@ -0,0 +1,19 @@
/* 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 ParamTraitsEnumChecker_h__
#define ParamTraitsEnumChecker_h__
#include "plugin.h"
class ParamTraitsEnumChecker : public BaseCheck {
public:
ParamTraitsEnumChecker(StringRef CheckName,
ContextType *Context = nullptr)
: BaseCheck(CheckName, Context) {}
void registerMatchers(MatchFinder* AstMatcher) override;
void check(const MatchFinder::MatchResult &Result) override;
};
#endif

View File

@ -33,6 +33,7 @@ UNIFIED_SOURCES += [
'NonParamInsideFunctionDeclChecker.cpp',
'OverrideBaseCallChecker.cpp',
'OverrideBaseCallUsageChecker.cpp',
'ParamTraitsEnumChecker.cpp',
'RefCountedCopyConstructorChecker.cpp',
'RefCountedInsideLambdaChecker.cpp',
'ScopeChecker.cpp',

View File

@ -0,0 +1,94 @@
typedef enum {
BadFirst,
BadSecond,
BadThird
} BadEnum;
typedef enum {
NestedFirst,
NestedSecond
} NestedBadEnum;
typedef enum {
GoodFirst,
GoodSecond,
GoodLast
} GoodEnum;
enum RawEnum {
RawFirst,
RawLast
};
enum class ClassEnum {
ClassFirst,
ClassLast
};
template <class P> struct ParamTraits;
// Simplified EnumSerializer etc. from IPCMessageUtils.h
template <typename E, typename EnumValidator>
struct EnumSerializer {
typedef E paramType;
};
template <typename E,
E MinLegal,
E HighBound>
class ContiguousEnumValidator
{};
template <typename E,
E MinLegal,
E HighBound>
struct ContiguousEnumSerializer
: EnumSerializer<E,
ContiguousEnumValidator<E, MinLegal, HighBound>>
{};
// Typical ParamTraits implementation that should be avoided
template<>
struct ParamTraits<ClassEnum> // expected-error {{Custom ParamTraits implementation for an enum type}} expected-note {{Please use a helper class for example ContiguousEnumSerializer}}
{
typedef ClassEnum paramType;
};
template<>
struct ParamTraits<enum RawEnum> // expected-error {{Custom ParamTraits implementation for an enum type}} expected-note {{Please use a helper class for example ContiguousEnumSerializer}}
{
typedef enum RawEnum paramType;
};
template<>
struct ParamTraits<BadEnum> // expected-error {{Custom ParamTraits implementation for an enum type}} expected-note {{Please use a helper class for example ContiguousEnumSerializer}}
{
typedef BadEnum paramType;
};
// Make sure the analysis catches nested typedefs
typedef NestedBadEnum NestedDefLevel1;
typedef NestedDefLevel1 NestedDefLevel2;
template<>
struct ParamTraits<NestedDefLevel2> // expected-error {{Custom ParamTraits implementation for an enum type}} expected-note {{Please use a helper class for example ContiguousEnumSerializer}}
{
typedef NestedDefLevel2 paramType;
};
// Make sure a non enum typedef is not accidentally flagged
typedef int IntTypedef;
template<>
struct ParamTraits<IntTypedef>
{
typedef IntTypedef paramType;
};
// Make sure ParamTraits using helper classes are not flagged
template<>
struct ParamTraits<GoodEnum>
: public ContiguousEnumSerializer<GoodEnum,
GoodEnum::GoodFirst,
GoodEnum::GoodLast>
{};

View File

@ -39,6 +39,7 @@ SOURCES += [
'TestNoRefcountedInsideLambdas.cpp',
'TestOverrideBaseCall.cpp',
'TestOverrideBaseCallAnnotation.cpp',
'TestParamTraitsEnum.cpp',
'TestRefCountedCopyConstructor.cpp',
'TestSprintfLiteral.cpp',
'TestStackClass.cpp',

View File

@ -6450,6 +6450,10 @@ nsDocShell::SetIsActive(bool aIsActive)
// Keep track ourselves.
mIsActive = aIsActive;
if (TabChild* tc = TabChild::GetFrom(this)) {
tc->OnDocShellActivated(aIsActive);
}
// Clear prerender flag if necessary.
if (mIsPrerendered && aIsActive) {
MOZ_ASSERT(mPrerenderGlobalHistory.get());

View File

@ -38,17 +38,23 @@ function test() {
// Lots of callbacks going on here
waitForExplicitFinish();
// Begin the test
step1();
// Begin the test. First, we disable tab warming because it's
// possible for the mouse to be over one of the tabs during
// this test, warm it up, and cause this test to fail intermittently
// in automation.
SpecialPowers.pushPrefEnv({
set: [["browser.tabs.remote.warmup.enabled", false]],
}).then(step1);
}
async function step1() {
function step1() {
// Get a handle on the initial tab
ctx.tab0 = gBrowser.selectedTab;
ctx.tab0Browser = gBrowser.getBrowserForTab(ctx.tab0);
await BrowserTestUtils.waitForCondition(() => ctx.tab0Browser.docShellIsActive,
"Timed out waiting for initial tab to be active.");
// Our current tab should be active
ok(ctx.tab0Browser.docShellIsActive, "Tab 0 should be active at test start");
// Open a New Tab
ctx.tab1 = BrowserTestUtils.addTab(gBrowser, testPath + "bug343515_pg1.html");

View File

@ -17,38 +17,22 @@ interface nsITabParent : nsISupports
readonly attribute boolean useAsyncPanZoom;
/**
* Manages the docshell active state of the remote browser. Setting the
* docShell to be active will also cause it to render layers and upload
* them to the compositor. Setting the docShell as not active will clear
* those layers.
*/
* Manages the docshell active state of the remote browser.
*/
attribute boolean docShellIsActive;
/**
* When set to true, this tells the child to paint and upload layers to
* the compositor. When set to false, previous layers are cleared from
* the compositor, but only if preserveLayers is also set to false.
*/
attribute boolean renderLayers;
/**
* True if layers are being rendered and the compositor has reported
* receiving them.
*/
readonly attribute boolean hasLayers;
/**
* Whether this tabParent is in prerender mode.
*/
[infallible] readonly attribute boolean isPrerendered;
/**
* As an optimisation, setting the docshell's active state to
* inactive also triggers a layer invalidation to free up some
* potentially unhelpful memory usage. Calling preserveLayers
* will cause the layers to be preserved even for inactive
* docshells.
*/
* As an optimisation, setting the docshell's active state to
* inactive also triggers a layer invalidation to free up some
* potentially unhelpful memory usage. Calling preserveLayers
* will cause the layers to be preserved even for inactive
* docshells.
*/
void preserveLayers(in boolean aPreserveLayers);
/**

View File

@ -791,22 +791,11 @@ child:
* Whether to activate or deactivate the docshell.
* @param aPreserveLayers
* Whether layer trees should be preserved for inactive docshells.
*/
async SetDocShellIsActive(bool aIsActive);
/**
* If aEnabled is true, tells the child to paint and upload layers to
* the compositor. If aEnabled is false, the child stops painting and
* clears the layers from the compositor.
*
* @param aEnabled
* True if the child should render and upload layers, false if the
* child should clear layers.
* @param aLayerObserverEpoch
* The layer observer epoch for this activation. This message should be
* ignored if this epoch has already been observed (via ForcePaint).
*/
async RenderLayers(bool aEnabled, uint64_t aLayerObserverEpoch);
async SetDocShellIsActive(bool aIsActive, bool aPreserveLayers, uint64_t aLayerObserverEpoch);
/**
* Notify the child that it shouldn't paint the offscreen displayport.

View File

@ -165,7 +165,7 @@ NS_IMPL_ISUPPORTS(TabChildSHistoryListener,
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
nsTHashtable<nsPtrHashKey<TabChild>>* TabChild::sVisibleTabs;
nsTHashtable<nsPtrHashKey<TabChild>>* TabChild::sActiveTabs;
typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
static TabChildMap* sTabChildren;
@ -428,7 +428,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mDidLoadURLInit(false)
, mAwaitingLA(false)
, mSkipKeyPress(false)
, mLayerObserverEpoch(1)
, mLayerObserverEpoch(0)
#if defined(XP_WIN) && defined(ACCESSIBILITY)
, mNativeWindowHandle(0)
#endif
@ -436,6 +436,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mTopLevelDocAccessibleChild(nullptr)
#endif
, mPendingDocShellIsActive(false)
, mPendingDocShellPreserveLayers(false)
, mPendingDocShellReceivedMessage(false)
, mPendingDocShellBlockers(0)
, mWidgetNativeData(0)
@ -1150,11 +1151,11 @@ TabChild::ActorDestroy(ActorDestroyReason why)
TabChild::~TabChild()
{
if (sVisibleTabs) {
sVisibleTabs->RemoveEntry(this);
if (sVisibleTabs->IsEmpty()) {
delete sVisibleTabs;
sVisibleTabs = nullptr;
if (sActiveTabs) {
sActiveTabs->RemoveEntry(this);
if (sActiveTabs->IsEmpty()) {
delete sActiveTabs;
sActiveTabs = nullptr;
}
}
@ -2661,57 +2662,38 @@ TabChild::RemovePendingDocShellBlocker()
mPendingDocShellBlockers--;
if (!mPendingDocShellBlockers && mPendingDocShellReceivedMessage) {
mPendingDocShellReceivedMessage = false;
InternalSetDocShellIsActive(mPendingDocShellIsActive);
InternalSetDocShellIsActive(mPendingDocShellIsActive,
mPendingDocShellPreserveLayers);
}
}
void
TabChild::InternalSetDocShellIsActive(bool aIsActive)
TabChild::OnDocShellActivated(bool aIsActive)
{
// docshell is consider prerendered only if not active yet
mIsPrerendered &= !aIsActive;
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {
docShell->SetIsActive(aIsActive);
if (aIsActive) {
if (!sActiveTabs) {
sActiveTabs = new nsTHashtable<nsPtrHashKey<TabChild>>();
}
sActiveTabs->PutEntry(this);
} else {
if (sActiveTabs) {
sActiveTabs->RemoveEntry(this);
// We don't delete sActiveTabs here when it's empty since that
// could cause a lot of churn. Instead, we wait until ~TabChild.
}
}
}
mozilla::ipc::IPCResult
TabChild::RecvSetDocShellIsActive(const bool& aIsActive)
void
TabChild::InternalSetDocShellIsActive(bool aIsActive, bool aPreserveLayers)
{
// If we're currently waiting for window opening to complete, we need to hold
// off on setting the docshell active. We queue up the values we're receiving
// in the mWindowOpenDocShellActiveStatus.
if (mPendingDocShellBlockers > 0) {
mPendingDocShellReceivedMessage = true;
mPendingDocShellIsActive = aIsActive;
return IPC_OK();
}
InternalSetDocShellIsActive(aIsActive);
return IPC_OK();
}
mozilla::ipc::IPCResult
TabChild::RecvRenderLayers(const bool& aEnabled, const uint64_t& aLayerObserverEpoch)
{
// Since requests to change the rendering state come in from both the hang
// monitor channel and the PContent channel, we have an ordering problem. This
// code ensures that we respect the order in which the requests were made and
// ignore stale requests.
if (mLayerObserverEpoch >= aLayerObserverEpoch) {
return IPC_OK();
}
mLayerObserverEpoch = aLayerObserverEpoch;
auto clearForcePaint = MakeScopeExit([&] {
// We might force a paint, or we might already have painted and this is a
// no-op. In either case, once we exit this scope, we need to alert the
// ProcessHangMonitor that we've finished responding to what might have
// been a request to force paint. This is so that the BackgroundHangMonitor
// for force painting can be made to wait again.
if (aEnabled) {
if (aIsActive) {
ProcessHangMonitor::ClearForcePaint();
}
});
@ -2723,31 +2705,34 @@ TabChild::RecvRenderLayers(const bool& aEnabled, const uint64_t& aLayerObserverE
// We send the current layer observer epoch to the compositor so that
// TabParent knows whether a layer update notification corresponds to the
// latest RecvRenderLayers request that was made.
// latest SetDocShellIsActive request that was made.
lm->SetLayerObserverEpoch(mLayerObserverEpoch);
}
if (aEnabled) {
if (IsVisible()) {
// docshell is consider prerendered only if not active yet
mIsPrerendered &= !aIsActive;
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {
bool wasActive;
docShell->GetIsActive(&wasActive);
if (aIsActive && wasActive) {
// This request is a no-op. In this case, we still want a MozLayerTreeReady
// notification to fire in the parent (so that it knows that the child has
// updated its epoch). ForcePaintNoOp does that.
if (IPCOpen()) {
Unused << SendForcePaintNoOp(mLayerObserverEpoch);
return IPC_OK();
return;
}
}
if (!sVisibleTabs) {
sVisibleTabs = new nsTHashtable<nsPtrHashKey<TabChild>>();
}
sVisibleTabs->PutEntry(this);
docShell->SetIsActive(aIsActive);
}
if (aIsActive) {
MakeVisible();
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (!docShell) {
return IPC_OK();
return;
}
// We don't use TabChildBase::GetPresShell() here because that would create
@ -2755,8 +2740,6 @@ TabChild::RecvRenderLayers(const bool& aEnabled, const uint64_t& aLayerObserverE
// cause JS to run, which we want to avoid. nsIDocShell::GetPresShell
// returns null if no content viewer exists yet.
if (nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell()) {
presShell->SetIsActive(true);
if (nsIFrame* root = presShell->FrameConstructor()->GetRootFrame()) {
FrameLayerBuilder::InvalidateAllLayersForFrame(
nsLayoutUtils::GetDisplayRootFrame(root));
@ -2780,16 +2763,36 @@ TabChild::RecvRenderLayers(const bool& aEnabled, const uint64_t& aLayerObserverE
}
APZCCallbackHelper::SuppressDisplayport(false, presShell);
}
} else {
if (sVisibleTabs) {
sVisibleTabs->RemoveEntry(this);
// We don't delete sVisibleTabs here when it's empty since that
// could cause a lot of churn. Instead, we wait until ~TabChild.
}
} else if (!aPreserveLayers) {
MakeHidden();
}
}
mozilla::ipc::IPCResult
TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
const bool& aPreserveLayers,
const uint64_t& aLayerObserverEpoch)
{
// Since requests to change the active docshell come in from both the hang
// monitor channel and the PContent channel, we have an ordering problem. This
// code ensures that we respect the order in which the requests were made and
// ignore stale requests.
if (mLayerObserverEpoch >= aLayerObserverEpoch) {
return IPC_OK();
}
mLayerObserverEpoch = aLayerObserverEpoch;
// If we're currently waiting for window opening to complete, we need to hold
// off on setting the docshell active. We queue up the values we're receiving
// in the mWindowOpenDocShellActiveStatus.
if (mPendingDocShellBlockers > 0) {
mPendingDocShellReceivedMessage = true;
mPendingDocShellIsActive = aIsActive;
mPendingDocShellPreserveLayers = aPreserveLayers;
return IPC_OK();
}
InternalSetDocShellIsActive(aIsActive, aPreserveLayers);
return IPC_OK();
}
@ -2937,9 +2940,6 @@ TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIden
ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
InitAPZState();
RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
MOZ_ASSERT(lm);
lm->SetLayerObserverEpoch(mLayerObserverEpoch);
} else {
NS_WARNING("Fallback to BasicLayerManager");
mLayersConnected = Some(false);
@ -3037,7 +3037,7 @@ TabChild::NotifyPainted()
void
TabChild::MakeVisible()
{
if (IsVisible()) {
if (mPuppetWidget && mPuppetWidget->IsVisible()) {
return;
}
@ -3049,33 +3049,19 @@ TabChild::MakeVisible()
void
TabChild::MakeHidden()
{
if (!IsVisible()) {
if (mPuppetWidget && !mPuppetWidget->IsVisible()) {
return;
}
// Due to the nested event loop in ContentChild::ProvideWindowCommon,
// it's possible to be told to become hidden before we're finished
// setting up a layer manager. We should skip clearing cached layers
// in that case, since doing so might accidentally put is into
// BasicLayers mode.
if (mPuppetWidget && mPuppetWidget->HasLayerManager()) {
ClearCachedResources();
}
ClearCachedResources();
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {
// Hide all plugins in this tab. We don't use TabChildBase::GetPresShell()
// here because that would create a content viewer if one doesn't exist yet.
// Creating a content viewer can cause JS to run, which we want to avoid.
// nsIDocShell::GetPresShell returns null if no content viewer exists yet.
if (nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell()) {
if (nsPresContext* presContext = presShell->GetPresContext()) {
nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
nsIFrame* rootFrame = presShell->FrameConstructor()->GetRootFrame();
rootPresContext->ComputePluginGeometryUpdates(rootFrame, nullptr, nullptr);
rootPresContext->ApplyPluginGeometryUpdates();
}
presShell->SetIsActive(false);
// Hide all plugins in this tab.
if (nsCOMPtr<nsIPresShell> shell = GetPresShell()) {
if (nsPresContext* presContext = shell->GetPresContext()) {
nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
nsIFrame* rootFrame = shell->FrameConstructor()->GetRootFrame();
rootPresContext->ComputePluginGeometryUpdates(rootFrame, nullptr, nullptr);
rootPresContext->ApplyPluginGeometryUpdates();
}
}
@ -3084,12 +3070,6 @@ TabChild::MakeHidden()
}
}
bool
TabChild::IsVisible()
{
return mPuppetWidget && mPuppetWidget->IsVisible();
}
NS_IMETHODIMP
TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult)
{
@ -3571,14 +3551,14 @@ TabChild::GetOuterRect()
void
TabChild::ForcePaint(uint64_t aLayerObserverEpoch)
{
if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasLayerManager()) {
if (!IPCOpen()) {
// Don't bother doing anything now. Better to wait until we receive the
// message on the PContent channel.
return;
}
nsAutoScriptBlocker scriptBlocker;
RecvRenderLayers(true, aLayerObserverEpoch);
RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
}
void

View File

@ -579,7 +579,8 @@ public:
*/
void MakeVisible();
void MakeHidden();
bool IsVisible();
void OnDocShellActivated(bool aIsActive);
nsIContentChild* Manager() const { return mManager; }
@ -776,20 +777,20 @@ public:
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
static bool HasVisibleTabs()
static bool HasActiveTabs()
{
return sVisibleTabs && !sVisibleTabs->IsEmpty();
return sActiveTabs && !sActiveTabs->IsEmpty();
}
// Returns the set of TabChilds that are currently rendering layers. There
// can be multiple TabChilds in this state if Firefox has multiple windows
// open or is warming tabs up. There can also be zero TabChilds in this
// state. Note that this function should only be called if HasVisibleTabs()
// returns true.
static const nsTHashtable<nsPtrHashKey<TabChild>>& GetVisibleTabs()
// Returns the set of TabChilds that are currently in the foreground. There
// can be multiple foreground TabChilds if Firefox has multiple windows
// open. There can also be zero foreground TabChilds if the foreground tab is
// in a different content process. Note that this function should only be
// called if HasActiveTabs() returns true.
static const nsTHashtable<nsPtrHashKey<TabChild>>& GetActiveTabs()
{
MOZ_ASSERT(HasVisibleTabs());
return *sVisibleTabs;
MOZ_ASSERT(HasActiveTabs());
return *sActiveTabs;
}
protected:
@ -801,9 +802,9 @@ protected:
virtual mozilla::ipc::IPCResult RecvDestroy() override;
virtual mozilla::ipc::IPCResult RecvSetDocShellIsActive(const bool& aIsActive) override;
virtual mozilla::ipc::IPCResult RecvRenderLayers(const bool& aEnabled, const uint64_t& aLayerObserverEpoch) override;
virtual mozilla::ipc::IPCResult RecvSetDocShellIsActive(const bool& aIsActive,
const bool& aIsHidden,
const uint64_t& aLayerObserverEpoch) override;
virtual mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
const bool& aForDocumentNavigation) override;
@ -894,7 +895,8 @@ private:
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
void InternalSetDocShellIsActive(bool aIsActive);
void InternalSetDocShellIsActive(bool aIsActive,
bool aPreserveLayers);
bool CreateRemoteLayerManager(mozilla::layers::PCompositorBridgeChild* aCompositorChild);
@ -984,16 +986,17 @@ private:
bool mCoalesceMouseMoveEvents;
bool mPendingDocShellIsActive;
bool mPendingDocShellPreserveLayers;
bool mPendingDocShellReceivedMessage;
uint32_t mPendingDocShellBlockers;
WindowsHandle mWidgetNativeData;
// This state is used to keep track of the current visible tabs (the ones rendering
// layers). There may be more than one if there are multiple browser windows open, or
// tabs are being warmed up. There may be none if this process does not host any
// visible or warming tabs.
static nsTHashtable<nsPtrHashKey<TabChild>>* sVisibleTabs;
// This state is used to keep track of the current active tabs (the ones in
// the foreground). There may be more than one if there are multiple browser
// windows open. There may be none if this process does not host any
// foreground tabs.
static nsTHashtable<nsPtrHashKey<TabChild>>* sActiveTabs;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

View File

@ -171,10 +171,8 @@ TabParent::TabParent(nsIContentParent* aManager,
#ifdef DEBUG
, mActiveSupressDisplayportCount(0)
#endif
, mLayerTreeEpoch(1)
, mLayerTreeEpoch(0)
, mPreserveLayers(false)
, mRenderLayers(true)
, mHasLayers(false)
, mHasPresented(false)
, mHasBeforeUnload(false)
, mIsMouseEnterIntoWidgetEventSuppressed(false)
@ -2904,11 +2902,14 @@ TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
NS_IMETHODIMP
TabParent::SetDocShellIsActive(bool isActive)
{
// Increment the epoch so that layer tree updates from previous
// SetDocShellIsActive requests are ignored.
mLayerTreeEpoch++;
// docshell is consider prerendered only if not active yet
mIsPrerendered &= !isActive;
mDocShellIsActive = isActive;
SetRenderLayers(isActive);
Unused << SendSetDocShellIsActive(isActive);
Unused << SendSetDocShellIsActive(isActive, mPreserveLayers, mLayerTreeEpoch);
// update active accessible documents on windows
#if defined(XP_WIN) && defined(ACCESSIBILITY)
@ -2931,6 +2932,13 @@ TabParent::SetDocShellIsActive(bool isActive)
// changing of the process priority.
ProcessPriorityManager::TabActivityChanged(this, isActive);
// Ask the child to repaint using the PHangMonitor channel/thread (which may
// be less congested).
if (isActive) {
ContentParent* cp = Manager()->AsContentParent();
cp->ForceTabPaint(this, mLayerTreeEpoch);
}
return NS_OK;
}
@ -2948,67 +2956,6 @@ TabParent::GetIsPrerendered(bool* aIsPrerendered)
return NS_OK;
}
NS_IMETHODIMP
TabParent::SetRenderLayers(bool aEnabled)
{
if (aEnabled == mRenderLayers) {
if (aEnabled == mHasLayers && mPreserveLayers) {
// RenderLayers might be called when we've been preserving layers,
// and already had layers uploaded. In that case, the MozLayerTreeReady
// event will not naturally arrive, which can confuse the front-end
// layer. So we fire the event here.
RefPtr<TabParent> self = this;
bool epoch = mLayerTreeEpoch;
bool enabled = aEnabled;
NS_DispatchToMainThread(NS_NewRunnableFunction(
"dom::TabParent::RenderLayers",
[self, epoch, enabled] () {
MOZ_ASSERT(NS_IsMainThread());
self->LayerTreeUpdate(epoch, enabled);
}));
}
return NS_OK;
}
// Preserve layers means that attempts to stop rendering layers
// will be ignored.
if (!aEnabled && mPreserveLayers) {
return NS_OK;
}
mRenderLayers = aEnabled;
// Increment the epoch so that layer tree updates from previous
// RenderLayers requests are ignored.
mLayerTreeEpoch++;
Unused << SendRenderLayers(aEnabled, mLayerTreeEpoch);
// Ask the child to repaint using the PHangMonitor channel/thread (which may
// be less congested).
if (aEnabled) {
ContentParent* cp = Manager()->AsContentParent();
cp->ForceTabPaint(this, mLayerTreeEpoch);
}
return NS_OK;
}
NS_IMETHODIMP
TabParent::GetRenderLayers(bool* aResult)
{
*aResult = mRenderLayers;
return NS_OK;
}
NS_IMETHODIMP
TabParent::GetHasLayers(bool* aResult)
{
*aResult = mHasLayers;
return NS_OK;
}
NS_IMETHODIMP
TabParent::PreserveLayers(bool aPreserveLayers)
{
@ -3096,6 +3043,35 @@ TabParent::GetHasBeforeUnload(bool* aResult)
return NS_OK;
}
class LayerTreeUpdateRunnable final
: public mozilla::Runnable
{
uint64_t mLayersId;
uint64_t mEpoch;
bool mActive;
public:
explicit LayerTreeUpdateRunnable(uint64_t aLayersId,
uint64_t aEpoch,
bool aActive)
: Runnable("dom::LayerTreeUpdateRunnable")
, mLayersId(aLayersId)
, mEpoch(aEpoch)
, mActive(aActive)
{
MOZ_ASSERT(!NS_IsMainThread());
}
private:
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
if (RefPtr<TabParent> tabParent = TabParent::GetTabParentFromLayersId(mLayersId)) {
tabParent->LayerTreeUpdate(mEpoch, mActive);
}
return NS_OK;
}
};
void
TabParent::LayerTreeUpdate(uint64_t aEpoch, bool aActive)
{
@ -3112,8 +3088,6 @@ TabParent::LayerTreeUpdate(uint64_t aEpoch, bool aActive)
return;
}
mHasLayers = aActive;
RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
if (aActive) {
mHasPresented = true;

View File

@ -782,15 +782,6 @@ private:
// the tab's docshell is inactive.
bool mPreserveLayers;
// Holds the most recent value passed to the RenderLayers function. This
// does not necessarily mean that the layers have finished rendering
// and have uploaded - for that, use mHasLayers.
bool mRenderLayers;
// True if the compositor has reported that the TabChild has uploaded
// layers.
bool mHasLayers;
// True if this TabParent has had its layer tree sent to the compositor
// at least once.
bool mHasPresented;

View File

@ -119,6 +119,8 @@ MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConst
MediaEngine::DEFAULT_43_VIDEO_HEIGHT
#endif
);
mOpts.mWidth = std::max(160, std::min(mOpts.mWidth, 4096));
mOpts.mHeight = std::max(90, std::min(mOpts.mHeight, 2160));
mState = kAllocated;
*aOutHandle = nullptr;
return NS_OK;

View File

@ -60,6 +60,17 @@ bool is_glcontext_egl(void* glcontext_ptr)
return glcontext->GetContextType() == mozilla::gl::GLContextType::EGL;
}
bool is_glcontext_angle(void* glcontext_ptr)
{
MOZ_ASSERT(glcontext_ptr);
mozilla::gl::GLContext* glcontext = reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
if (!glcontext) {
return false;
}
return glcontext->IsANGLE();
}
bool gfx_use_wrench()
{
return gfxEnv::EnableWebRenderRecording();

View File

@ -12,7 +12,7 @@ use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener};
use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
use webrender::DebugFlags;
use webrender::{ApiRecordingReceiver, BinaryRecorder};
use webrender::ProgramCache;
use webrender::{ProgramCache, UploadMethod, VertexUsageHint};
use thread_profiler::register_thread_with_profiler;
use moz2d_renderer::Moz2dImageRenderer;
use app_units::Au;
@ -417,6 +417,7 @@ extern "C" {
fn is_in_render_thread() -> bool;
fn is_in_main_thread() -> bool;
fn is_glcontext_egl(glcontext_ptr: *mut c_void) -> bool;
fn is_glcontext_angle(glcontext_ptr: *mut c_void) -> bool;
// Enables binary recording that can be used with `wrench replay`
// Outputs a wr-record-*.bin file for each window that is shown
// Note: wrench will panic if external images are used, they can
@ -718,6 +719,12 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
Arc::clone(&(*thread_pool).0)
};
let upload_method = if unsafe { is_glcontext_angle(gl_context) } {
UploadMethod::Immediate
} else {
UploadMethod::PixelBuffer(VertexUsageHint::Dynamic)
};
let opts = RendererOptions {
enable_aa: true,
enable_subpixel_aa: true,
@ -738,6 +745,7 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
}
},
renderer_id: Some(window_id.0),
upload_method,
..Default::default()
};

View File

@ -20,6 +20,7 @@ bool is_in_compositor_thread();
bool is_in_main_thread();
bool is_in_render_thread();
bool is_glcontext_egl(void* glcontext_ptr);
bool is_glcontext_angle(void* glcontext_ptr);
bool gfx_use_wrench();
const char* gfx_wr_resource_path_override();
void gfx_critical_note(const char* msg);

View File

@ -662,8 +662,8 @@ BackgroundChildImpl::GetMessageSchedulerGroups(const Message& aMsg, SchedulerGro
if (aMsg.type() == layout::PVsync::MessageType::Msg_Notify__ID) {
MOZ_ASSERT(NS_IsMainThread());
aGroups.Clear();
if (dom::TabChild::HasVisibleTabs()) {
for (auto iter = dom::TabChild::GetVisibleTabs().ConstIter();
if (dom::TabChild::HasActiveTabs()) {
for (auto iter = dom::TabChild::GetActiveTabs().ConstIter();
!iter.Done(); iter.Next()) {
aGroups.Put(iter.Get()->GetKey()->TabGroup());
}

View File

@ -41,11 +41,12 @@ function test_with_no_protochain(a) {
// return false, and not consider the prototype chain at all"
function test_with_protochain(a) {
var a = new Int32Array(1000).fill(1);
// try to force the behaviour of 9.4.5.2
a[1012] = "1012";
a["-0"] = "-0";
a[-10] = "-10";
warmup(a);
// try to force the behaviour of 9.4.5.2
Object.prototype["-0"] = "value";
Object.prototype[-1] = "value";
Object.prototype[-10] = "value";
Object.prototype[1012] = "value";
check_assertions(a);
}

View File

@ -1078,12 +1078,16 @@ IsArraySpecies(JSContext* cx, HandleObject origArray)
#endif
return true;
}
} else {
// 9.4.2.3 Step 4. Non-array objects always use the default constructor.
if (!origArray->is<ArrayObject>())
return true;
return false;
}
// 9.4.2.3 Step 4. Non-array objects always use the default constructor.
if (!origArray->is<ArrayObject>())
return true;
if (cx->compartment()->arraySpeciesLookup.tryOptimizeArray(cx, &origArray->as<ArrayObject>()))
return true;
Value ctor;
if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), &ctor))
return false;
@ -4081,3 +4085,151 @@ js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)
return true;
}
#endif
void
js::ArraySpeciesLookup::initialize(JSContext* cx)
{
MOZ_ASSERT(state_ == State::Uninitialized);
// Get the canonical Array.prototype.
NativeObject* arrayProto = cx->global()->maybeGetArrayPrototype();
// Leave the cache uninitialized if the Array class itself is not yet
// initialized.
if (!arrayProto)
return;
// Get the canonical Array constructor.
const Value& arrayCtorValue = cx->global()->getConstructor(JSProto_Array);
MOZ_ASSERT(arrayCtorValue.isObject(),
"The Array constructor is initialized iff Array.prototype is initialized");
JSFunction* arrayCtor = &arrayCtorValue.toObject().as<JSFunction>();
// Shortcut returns below means Array[@@species] will never be
// optimizable, set to disabled now, and clear it later when we succeed.
state_ = State::Disabled;
// Look up Array.prototype[@@iterator] and ensure it's a data property.
Shape* ctorShape = arrayProto->lookup(cx, NameToId(cx->names().constructor));
if (!ctorShape || !ctorShape->isDataProperty())
return;
// Get the referred value, and ensure it holds the canonical Array
// constructor.
JSFunction* ctorFun;
if (!IsFunctionObject(arrayProto->getSlot(ctorShape->slot()), &ctorFun))
return;
if (ctorFun != arrayCtor)
return;
// Look up the '@@species' value on Array
Shape* speciesShape = arrayCtor->lookup(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
if (!speciesShape || !speciesShape->hasGetterValue())
return;
// Get the referred value, ensure it holds the canonical Array[@@species]
// function.
JSFunction* speciesFun;
if (!IsFunctionObject(speciesShape->getterValue(), &speciesFun))
return;
if (!IsSelfHostedFunctionWithName(speciesFun, cx->names().ArraySpecies))
return;
// Store raw pointers below. This is okay to do here, because all objects
// are in the tenured heap.
MOZ_ASSERT(!IsInsideNursery(arrayProto));
MOZ_ASSERT(!IsInsideNursery(arrayCtor));
MOZ_ASSERT(!IsInsideNursery(arrayCtor->lastProperty()));
MOZ_ASSERT(!IsInsideNursery(speciesShape));
MOZ_ASSERT(!IsInsideNursery(speciesFun));
MOZ_ASSERT(!IsInsideNursery(arrayProto->lastProperty()));
state_ = State::Initialized;
arrayProto_ = arrayProto;
arrayConstructor_ = arrayCtor;
arrayConstructorShape_ = arrayCtor->lastProperty();
#ifdef DEBUG
arraySpeciesShape_ = speciesShape;
canonicalSpeciesFunc_ = speciesFun;
#endif
arrayProtoShape_ = arrayProto->lastProperty();
arrayProtoConstructorSlot_ = ctorShape->slot();
}
void
js::ArraySpeciesLookup::reset()
{
state_ = State::Uninitialized;
arrayProto_ = nullptr;
arrayConstructor_ = nullptr;
arrayConstructorShape_ = nullptr;
#ifdef DEBUG
arraySpeciesShape_ = nullptr;
canonicalSpeciesFunc_ = nullptr;
#endif
arrayProtoShape_ = nullptr;
arrayProtoConstructorSlot_ = -1;
}
bool
js::ArraySpeciesLookup::isArrayStateStillSane()
{
MOZ_ASSERT(state_ == State::Initialized);
// Ensure that Array.prototype still has the expected shape.
if (arrayProto_->lastProperty() != arrayProtoShape_)
return false;
// Ensure that Array.prototype.constructor contains the canonical Array
// constructor function.
if (arrayProto_->getSlot(arrayProtoConstructorSlot_) != ObjectValue(*arrayConstructor_))
return false;
// Ensure that Array still has the expected shape.
if (arrayConstructor_->lastProperty() != arrayConstructorShape_)
return false;
// Ensure the species getter contains the canonical @@species function.
// Note: This is currently guaranteed to be always true, because modifying
// the getter property implies a new shape is generated. If this ever
// changes, convert this assertion into an if-statement.
MOZ_ASSERT(arraySpeciesShape_->getterObject() == canonicalSpeciesFunc_);
return true;
}
bool
js::ArraySpeciesLookup::tryOptimizeArray(JSContext* cx, ArrayObject* array)
{
if (state_ == State::Uninitialized) {
// If the cache is not initialized, initialize it.
initialize(cx);
} else if (state_ == State::Initialized && !isArrayStateStillSane()) {
// Otherwise, if the array state is no longer sane, reinitialize.
reset();
initialize(cx);
}
// If the cache is disabled or still uninitialized, don't bother trying to
// optimize.
if (state_ != State::Initialized)
return false;
// By the time we get here, we should have a sane array state.
MOZ_ASSERT(isArrayStateStillSane());
// Ensure |array|'s prototype is the actual Array.prototype.
if (array->staticPrototype() != arrayProto_)
return false;
// Ensure |array| doesn't define any own properties besides its
// non-deletable "length" property. This serves as a quick check to make
// sure |array| doesn't define an own "constructor" property which may
// shadow Array.prototype.constructor.
Shape* shape = array->shape();
if (shape->previous() && !shape->previous()->isEmptyShape())
return false;
MOZ_ASSERT(JSID_IS_ATOM(shape->propidRaw(), cx->names().length));
return true;
}

View File

@ -199,6 +199,85 @@ array_construct(JSContext* cx, unsigned argc, Value* vp);
extern bool
IsWrappedArrayConstructor(JSContext* cx, const Value& v, bool* result);
class MOZ_NON_TEMPORARY_CLASS ArraySpeciesLookup final
{
/*
* An ArraySpeciesLookup holds the following:
*
* Array.prototype (arrayProto_)
* To ensure that the incoming array has the standard proto.
*
* Array.prototype's shape (arrayProtoShape_)
* To ensure that Array.prototype has not been modified.
*
* Array (arrayConstructor_)
* Array's shape (arrayConstructorShape_)
* To ensure that Array has not been modified.
*
* Array.prototype's slot number for constructor (arrayProtoConstructorSlot_)
* To quickly retrieve and ensure that the Array constructor
* stored in the slot has not changed.
*
* Array's shape for the @@species getter. (arraySpeciesShape_)
* Array's canonical value for @@species (canonicalSpeciesFunc_)
* To quickly retrieve and ensure that the @@species getter for Array
* has not changed.
*/
// Pointer to canonical Array.prototype and Array.
NativeObject* arrayProto_;
NativeObject* arrayConstructor_;
// Shape of matching Array, and slot containing the @@species
// property, and the canonical value.
Shape* arrayConstructorShape_;
#ifdef DEBUG
Shape* arraySpeciesShape_;
JSFunction* canonicalSpeciesFunc_;
#endif
// Shape of matching Array.prototype object, and slot containing the
// constructor for it.
Shape* arrayProtoShape_;
uint32_t arrayProtoConstructorSlot_;
enum class State : uint8_t {
// Flags marking the lazy initialization of the above fields.
Uninitialized,
Initialized,
// The disabled flag is set when we don't want to try optimizing
// anymore because core objects were changed.
Disabled
};
State state_;
// Initialize the internal fields.
void initialize(JSContext* cx);
// Reset the cache.
void reset();
// Check if the global array-related objects have not been messed with
// in a way that would disable this cache.
bool isArrayStateStillSane();
public:
ArraySpeciesLookup() {
reset();
}
// Try to optimize the @@species lookup for an array.
bool tryOptimizeArray(JSContext* cx, ArrayObject* array);
// Purge the cache and all info associated with it.
void purge() {
if (state_ == State::Initialized)
reset();
}
};
} /* namespace js */
#endif /* jsarray_h */

View File

@ -68,6 +68,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
allocationMetadataBuilder(nullptr),
lastAnimationTime(0),
regExps(),
arraySpeciesLookup(),
globalWriteBarriered(0),
detachedTypedObjects(0),
objectMetadataState(ImmediateMetadata()),
@ -1083,6 +1084,7 @@ JSCompartment::purge()
newProxyCache.purge();
objectGroups.purge();
iteratorCache.clearAndShrink();
arraySpeciesLookup.purge();
}
void

View File

@ -726,6 +726,8 @@ struct JSCompartment
js::RegExpCompartment regExps;
js::ArraySpeciesLookup arraySpeciesLookup;
using IteratorCache = js::HashSet<js::PropertyIteratorObject*,
js::IteratorHashPolicy,
js::SystemAllocPolicy>;

View File

@ -38,7 +38,7 @@ MOZ_MTLOG_MODULE("jsep")
std::ostringstream os; \
os << error; \
mLastError = os.str(); \
MOZ_MTLOG(ML_ERROR, mLastError); \
MOZ_MTLOG(ML_ERROR, "[" << mName << "]: " << mLastError); \
} while (0);
static std::bitset<128> GetForbiddenSdpPayloadTypes() {
@ -72,7 +72,7 @@ nsresult
JsepSessionImpl::AddTransceiver(RefPtr<JsepTransceiver> transceiver)
{
mLastError.clear();
MOZ_MTLOG(ML_DEBUG, "Adding transceiver.");
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: Adding transceiver.");
if (transceiver->GetMediaType() != SdpMediaSection::kApplication) {
// Make sure we have an ssrc. Might already be set.
@ -636,8 +636,8 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
{
mLastError.clear();
MOZ_MTLOG(ML_DEBUG, "SetLocalDescription type=" << type << "\nSDP=\n"
<< sdp);
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SetLocalDescription type=" << type
<< "\nSDP=\n" << sdp);
if (type == kJsepSdpRollback) {
if (mState != kJsepStateHaveLocalOffer) {
@ -682,6 +682,19 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
rv = ValidateLocalDescription(*parsed);
NS_ENSURE_SUCCESS(rv, rv);
switch (type) {
case kJsepSdpOffer:
rv = ValidateOffer(*parsed);
break;
case kJsepSdpAnswer:
case kJsepSdpPranswer:
rv = ValidateAnswer(*mPendingRemoteDescription, *parsed);
break;
case kJsepSdpRollback:
MOZ_CRASH(); // Handled above
}
NS_ENSURE_SUCCESS(rv, rv);
if (type == kJsepSdpOffer) {
// Save in case we need to rollback
mOldTransceivers.clear();
@ -734,12 +747,8 @@ JsepSessionImpl::SetLocalDescriptionAnswer(JsepSdpType type,
MOZ_ASSERT(mState == kJsepStateHaveRemoteOffer);
mPendingLocalDescription = Move(answer);
nsresult rv = ValidateAnswer(*mPendingRemoteDescription,
*mPendingLocalDescription);
NS_ENSURE_SUCCESS(rv, rv);
rv = HandleNegotiatedSession(mPendingLocalDescription,
mPendingRemoteDescription);
nsresult rv = HandleNegotiatedSession(mPendingLocalDescription,
mPendingRemoteDescription);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentRemoteDescription = Move(mPendingRemoteDescription);
@ -756,8 +765,8 @@ JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
{
mLastError.clear();
MOZ_MTLOG(ML_DEBUG, "SetRemoteDescription type=" << type << "\nSDP=\n"
<< sdp);
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: SetRemoteDescription type=" << type
<< "\nSDP=\n" << sdp);
if (type == kJsepSdpRollback) {
if (mState != kJsepStateHaveRemoteOffer) {
@ -804,6 +813,19 @@ JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
rv = ValidateRemoteDescription(*parsed);
NS_ENSURE_SUCCESS(rv, rv);
switch (type) {
case kJsepSdpOffer:
rv = ValidateOffer(*parsed);
break;
case kJsepSdpAnswer:
case kJsepSdpPranswer:
rv = ValidateAnswer(*mPendingLocalDescription, *parsed);
break;
case kJsepSdpRollback:
MOZ_CRASH(); // Handled above
}
NS_ENSURE_SUCCESS(rv, rv);
bool iceLite =
parsed->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute);
@ -970,7 +992,7 @@ JsepSessionImpl::MakeNegotiatedTransceiver(const SdpMediaSection& remote,
}
}
MOZ_MTLOG(ML_DEBUG, "Negotiated m= line"
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: Negotiated m= line"
<< " index=" << local.GetLevel()
<< " type=" << local.GetMediaType()
<< " sending=" << sending
@ -1017,15 +1039,16 @@ JsepSessionImpl::MakeNegotiatedTransceiver(const SdpMediaSection& remote,
// TODO(bug 1105005): Once we have urn:ietf:params:rtp-hdrext:sdes:mid
// support, we should only fire this warning if that extension was not
// negotiated.
MOZ_MTLOG(ML_ERROR, "Bundled m-section has no ssrc attributes. "
"This may cause media packets to be dropped.");
MOZ_MTLOG(ML_ERROR, "[" << mName << "]: Bundled m-section has no ssrc "
"attributes. This may cause media packets to be "
"dropped.");
}
}
if (transceiver->mTransport->mComponents == 2) {
// RTCP MUX or not.
// TODO(bug 1095743): verify that the PTs are consistent with mux.
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is off");
MOZ_MTLOG(ML_DEBUG, "[" << mName << "]: RTCP-MUX is off");
}
if (local.GetMediaType() != SdpMediaSection::kApplication) {
@ -1300,9 +1323,6 @@ JsepSessionImpl::SetRemoteDescriptionOffer(UniquePtr<Sdp> offer)
{
MOZ_ASSERT(mState == kJsepStateStable);
nsresult rv = ValidateOffer(*offer);
NS_ENSURE_SUCCESS(rv, rv);
mPendingRemoteDescription = Move(offer);
SetState(kJsepStateHaveRemoteOffer);
@ -1318,12 +1338,8 @@ JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
mPendingRemoteDescription = Move(answer);
nsresult rv = ValidateAnswer(*mPendingLocalDescription,
*mPendingRemoteDescription);
NS_ENSURE_SUCCESS(rv, rv);
rv = HandleNegotiatedSession(mPendingLocalDescription,
mPendingRemoteDescription);
nsresult rv = HandleNegotiatedSession(mPendingLocalDescription,
mPendingRemoteDescription);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentRemoteDescription = Move(mPendingRemoteDescription);
@ -1789,9 +1805,9 @@ JsepSessionImpl::ValidateAnswer(const Sdp& offer, const Sdp& answer)
// FIXME we do not return an error here, because Chrome up to
// version 57 is actually tripping over this if they are the
// answerer. See bug 1355010 for details.
MOZ_MTLOG(ML_WARNING, "Answer has inconsistent direction on extmap "
"attribute at level " << i << " ("
<< ansExt.extensionname << "). Offer had "
MOZ_MTLOG(ML_WARNING, "[" << mName << "]: Answer has inconsistent"
" direction on extmap attribute at level " << i
<< " (" << ansExt.extensionname << "). Offer had "
<< offExt.direction << ", answer had "
<< ansExt.direction << ".");
// return NS_ERROR_INVALID_ARG;
@ -1801,10 +1817,10 @@ JsepSessionImpl::ValidateAnswer(const Sdp& offer, const Sdp& answer)
// FIXME we do not return an error here, because Cisco Spark
// actually does respond with different extension ID's then we
// offer. See bug 1361206 for details.
MOZ_MTLOG(ML_WARNING, "Answer changed id for extmap attribute at"
" level " << i << " (" << offExt.extensionname << ") "
"from " << offExt.entry << " to "
<< ansExt.entry << ".");
MOZ_MTLOG(ML_WARNING, "[" << mName << "]: Answer changed id for "
"extmap attribute at level " << i << " ("
<< offExt.extensionname << ") from " << offExt.entry
<< " to " << ansExt.entry << ".");
// return NS_ERROR_INVALID_ARG;
}

View File

@ -47,7 +47,7 @@
"talos_options": ["--disable-stylo"]
},
"g4-e10s": {
"tests": ["basic_compositor_video", "glvideo", "displaylist_mutate"]
"tests": ["basic_compositor_video", "glvideo", "displaylist_mutate", "rasterflood_svg", "rasterflood_gradient"]
},
"g4-stylo-disabled-e10s": {
"tests": ["basic_compositor_video", "glvideo"],

View File

@ -963,3 +963,49 @@ class displaylist_mutate(PageloaderTest):
'docshell.event_starvation_delay_hint': 1,
'dom.send_after_paint_to_content': False}
unit = 'ms'
@register_test()
class rasterflood_svg(PageloaderTest):
"""
Test modifying single items in a large display list. Measure transaction speed
to the compositor.
"""
tpmanifest = '${talos}/tests/gfx/rasterflood_svg.manifest'
tpcycles = 1
tppagecycles = 10
tploadnocache = True
tpmozafterpaint = False
tpchrome = False
gecko_profile_interval = 2
gecko_profile_entries = 2000000
win_counters = w7_counters = linux_counters = mac_counters = None
filters = filter.ignore_first.prepare(1) + filter.median.prepare()
"""ASAP mode"""
preferences = {'layout.frame_rate': 0,
'docshell.event_starvation_delay_hint': 1,
'dom.send_after_paint_to_content': False}
unit = 'ms'
@register_test()
class rasterflood_gradient(PageloaderTest):
"""
Test expensive rasterization while the main thread is busy.
"""
tpmanifest = '${talos}/tests/gfx/rasterflood_gradient.manifest'
tpcycles = 1
tppagecycles = 10
tploadnocache = True
tpmozafterpaint = False
tpchrome = False
gecko_profile_interval = 2
gecko_profile_entries = 2000000
win_counters = w7_counters = linux_counters = mac_counters = None
filters = filter.ignore_first.prepare(1) + filter.median.prepare()
"""ASAP mode"""
preferences = {'layout.frame_rate': 0,
'docshell.event_starvation_delay_hint': 1,
'dom.send_after_paint_to_content': False}
lower_is_better = False
unit = 'score'

View File

@ -0,0 +1,127 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Paint-In-Time 2</title>
<style>
.gradient {
position: absolute;
width: 800px;
height: 600px;
opacity: 0.4;
}
</style>
</head>
<body>
<div id="grid">
</div>
</body>
<script>
// Only allow painting a tiny slice of the frame - here, 15ms.
var kFrameBudgetMs = 14;
var kDivCount = 10;
var kMaxTime = 20 * 1000;
var gCrazyCounter = 0;
var gIterations = 0;
var gStart;
var gDivs = [];
function GetColor() {
var color = GetColor.kColors[GetColor.Index];
GetColor.Index++;
GetColor.Index = (GetColor.Index % GetColor.kColors.length);
return color;
}
GetColor.Index = 0;
GetColor.kColors = ["red", "white", "green", "yellow", "orange", "brown"];
function RadialGradient(offset) {
this.colors = [GetColor(), GetColor()];
this.percents = [0 + offset, 20 + offset];
this.toStyle = function() {
return "radial-gradient(" +
this.colors[0] + " " + this.percents[0] + "%, " +
this.colors[1] + " " + this.percents[1] + "%)";
};
this.advance = function() {
this.percents[0] += 1;
this.percents[0] %= 80;
this.percents[1] = this.percents[0] + 20;
};
}
function runFrame() {
var start = performance.now();
// Spin loop.
while (performance.now() - start < kFrameBudgetMs)
gCrazyCounter++;
for (var i = 0; i < gDivs.length; i++) {
var info = gDivs[i];
info.gradient.advance();
info.element.style.background = info.gradient.toStyle();
}
gIterations++;
if (performance.now() - gStart >= kMaxTime) {
if (window.tpRecordTime) {
window.tpRecordTime(gIterations);
}
return;
}
window.requestAnimationFrame(runFrame);
}
function setup() {
var root = document.getElementById("grid");
for (var i = 0; i < kDivCount; i++) {
var gradient = new RadialGradient(i * 10 / 2);
var div = document.createElement("div");
var info = {};
info.element = div;
info.gradient = gradient;
div.classList.add("gradient");
div.style.left = "10px";
div.style.top = "10px";
div.style.background = gradient.toStyle();
root.appendChild(div);
gDivs.push(info);
}
}
function startTest() {
setup();
gStart = performance.now();
window.requestAnimationFrame(runFrame);
}
addEventListener("load", function() {
try {
// Outside of talos, this throws a security exception which no-op this file.
// (It's not required nor allowed for addons since Firefox 17)
// It's used inside talos from non-privileged pages (like during tscroll),
// and it works because talos disables all/most security measures.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
} catch (e) {}
try {
Components.utils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://talos-powers-content/content/TalosContentProfiler.js");
TalosContentProfiler.resume("rasterflood_gradient.html loaded", true).then(() => {
startTest();
});
} catch (e) {
startTest();
}
});
</script>
</html>

View File

@ -0,0 +1,155 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Paint-In-Time</title>
<style>
svg {
width: 600px;
height: 800px;
}
</style>
</head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="filter1">
<feGaussianBlur stdDeviation=".5"></feGaussianBlur>
</filter>
<filter id="filter2">
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur1"></feGaussianBlur>
<feSpecularLighting result="specOut" in="blur1" specularExponent="20" lighting-color="#cccccc">
<fePointLight x="50" y="50" z="1"></fePointLight>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
</filter>
</defs>
<g fill="none" stroke="blue" stroke-width="20" stroke-dasharray="1" class="rotating">
<rect x="50" y="50" width="80px" height="80px" fill="blue" id="rect1" opacity="0.25" filter="url(#filter2)" transform="rotate(202 64 64)">
</rect>
</g>
<g fill="none" stroke="red" stroke-width="20" lass="rotating">
<rect x="75" y="75" width="80px" height="80px" fill="blue" id="rect2" opacity="0.5" filter="url(#filter1)" transform="rotate(202 89 89)">
</rect>
</g>
<g fill="none" stroke="green" stroke-width="20" class="rotating">
<rect x="100" y="100" width="80px" height="80px" fill="green" id="rect3" opacity="0.75" filter="url(#filter1)" transform="rotate(202 114 114)">
</rect>
</g>
<g fill="none" stroke="yellow" stroke-width="20" stroke-dasharray="1" class="rotating">
<rect x="125" y="125" width="80px" height="80px" fill="green" id="rect4" opacity="1" filter="url(#filter2)" transform="rotate(202 139 139)">
</rect>
</g>
<g fill="none" stroke="pink" stroke-width="20" stroke-dasharray="1" class="rotating">
<rect x="75" y="125" width="80px" height="80px" fill="green" id="rect5" opacity="1" filter="url(#filter2)" transform="rotate(202 89 139)">
</rect>
</g>
</svg>
<script>
// Only allow painting a tiny slice of the frame - here, 15ms.
var kFrameBudgetMs = 14;
var kDegPerFrame = 1;
var kCurrentRotation = 0;
var kMaxIterations = 600;
var kMinSize = 80;
var kMaxSize = 200;
var kOpacityPerFrame = [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05];
var gCrazyCounter = 0;
var gStart;
var gSizeChangePerFrame = 1;
var gCurrentSize = 50;
var gIterations = 0;
function runFrame() {
var start = performance.now();
// Spin loop.
while (performance.now() - start < kFrameBudgetMs)
gCrazyCounter++;
// Change rotation.
var elts = [
document.getElementById("rect1"),
document.getElementById("rect2"),
document.getElementById("rect3"),
document.getElementById("rect4"),
document.getElementById("rect5"),
];
kCurrentRotation = (kCurrentRotation + kDegPerFrame) % 360;
// Change size.
if (gCurrentSize >= kMaxSize)
gSizeChangePerFrame = -1;
else if (gCurrentSize <= kMinSize)
gSizeChangePerFrame = 1;
gCurrentSize += gSizeChangePerFrame;
for (var i = 0; i < elts.length; i++) {
var elt = elts[i];
elt.setAttribute("height", gCurrentSize + "px");
elt.setAttribute("width", gCurrentSize + "px");
var bb = elt.getBBox();
var x = bb.x + bb.width / 2;
var y = bb.y + bb.height / 2;
var origin = x + " " + y;
elt.setAttribute("transform", "rotate(" + kCurrentRotation + " " + origin + ")");
var opacity = parseFloat(window.getComputedStyle(elt).getPropertyValue("opacity"));
if (opacity + kOpacityPerFrame[i] >= 1.0) {
opacity = 1.0;
kOpacityPerFrame[i] = -kOpacityPerFrame[i];
} else if (opacity + kOpacityPerFrame[i] < 0.1) {
opacity = 1.0;
kOpacityPerFrame[i] = -kOpacityPerFrame[i];
}
elt.setAttribute("opacity", opacity);
}
if (gIterations >= kMaxIterations) {
var end = performance.now();
if (window.tpRecordTime) {
window.tpRecordTime(end - gStart, gStart);
}
if (parent.reportResults) {
parent.reportResults(end - gStart, gStart);
}
return;
}
window.requestAnimationFrame(runFrame);
gIterations++;
}
function startTest() {
gStart = performance.now();
window.requestAnimationFrame(runFrame);
}
addEventListener("load", function() {
try {
// Outside of talos, this throws a security exception which no-op this file.
// (It's not required nor allowed for addons since Firefox 17)
// It's used inside talos from non-privileged pages (like during tscroll),
// and it works because talos disables all/most security measures.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
} catch (e) {}
try {
Components.utils.import("resource://gre/modules/Services.jsm");
Services.scriptloader.loadSubScript("chrome://talos-powers-content/content/TalosContentProfiler.js");
TalosContentProfiler.resume("rasterflood_svg.html loaded", true).then(() => {
startTest();
});
} catch (e) {
startTest();
}
});
</script>
</body></html>

View File

@ -0,0 +1 @@
% http://localhost/tests/gfx/benchmarks/rasterflood_gradient.html

View File

@ -0,0 +1 @@
% http://localhost/tests/gfx/benchmarks/rasterflood_svg.html

View File

@ -304,27 +304,6 @@
</body>
</method>
<property name="renderLayers">
<getter>
<![CDATA[
return this.docShellIsActive;
]]>
</getter>
<setter>
<![CDATA[
return this.docShellIsActive = val;
]]>
</setter>
</property>
<property name="hasLayers" readonly="true">
<getter>
<![CDATA[
return this.docShellIsActive;
]]>
</getter>
</property>
<property name="imageDocument"
readonly="true">
<getter>

View File

@ -258,37 +258,6 @@
]]></body>
</method>
<property name="renderLayers">
<getter>
<![CDATA[
let {frameLoader} = this;
if (frameLoader && frameLoader.tabParent) {
return frameLoader.tabParent.renderLayers;
}
return false;
]]>
</getter>
<setter>
<![CDATA[
let {frameLoader} = this;
if (frameLoader && frameLoader.tabParent) {
return frameLoader.tabParent.renderLayers = val;
}
return false;
]]>
</setter>
</property>
<property name="hasLayers" readonly="true">
<getter><![CDATA[
let {frameLoader} = this;
if (frameLoader.tabParent) {
return frameLoader.tabParent.hasLayers;
}
return false;
]]></getter>
</property>
<field name="_manifestURI"/>
<property name="manifestURI"
onget="return this._manifestURI"

View File

@ -166,21 +166,21 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority,
return nullptr;
}
// Move visible tabs to the front of the queue. The mAvoidVisibleTabCount field
// prevents us from preferentially processing events from visible tabs twice in
// Move active tabs to the front of the queue. The mAvoidActiveTabCount field
// prevents us from preferentially processing events from active tabs twice in
// a row. This scheme is designed to prevent starvation.
if (TabChild::HasVisibleTabs() && mAvoidVisibleTabCount <= 0) {
for (auto iter = TabChild::GetVisibleTabs().ConstIter();
if (TabChild::HasActiveTabs() && mAvoidActiveTabCount <= 0) {
for (auto iter = TabChild::GetActiveTabs().ConstIter();
!iter.Done(); iter.Next()) {
SchedulerGroup* group = iter.Get()->GetKey()->TabGroup();
if (!group->isInList() || group == sCurrentSchedulerGroup) {
continue;
}
// For each visible tab we move to the front of the queue, we have to
// process two SchedulerGroups (the visible tab and another one, presumably
// a background group) before we prioritize visible tabs again.
mAvoidVisibleTabCount += 2;
// For each active tab we move to the front of the queue, we have to
// process two SchedulerGroups (the active tab and another one, presumably
// a background group) before we prioritize active tabs again.
mAvoidActiveTabCount += 2;
// We move |group| right before sCurrentSchedulerGroup and then set
// sCurrentSchedulerGroup to group.
@ -195,7 +195,7 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority,
SchedulerGroup* firstGroup = sCurrentSchedulerGroup;
SchedulerGroup* group = firstGroup;
do {
mAvoidVisibleTabCount--;
mAvoidActiveTabCount--;
auto queueEntry = mLabeled.Lookup(group);
if (!queueEntry) {

View File

@ -149,11 +149,11 @@ private:
EpochQueue mEpochs;
size_t mNumEvents = 0;
// Number of SchedulerGroups that must be processed before we prioritize a
// visible tab. This field is designed to guarantee a 1:1 interleaving between
// Number of SchedulerGroups that must be processed before we prioritize an
// active tab. This field is designed to guarantee a 1:1 interleaving between
// foreground and background SchedulerGroups. For details, see its usage in
// LabeledEventQueue.cpp.
int64_t mAvoidVisibleTabCount = 0;
int64_t mAvoidActiveTabCount = 0;
};
} // namespace mozilla