mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Merge inbound to mozilla central r=merge a=merge
This commit is contained in:
commit
cbd802ee7d
@ -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) {
|
||||
|
@ -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")
|
||||
|
@ -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"
|
||||
|
37
build/clang-plugin/ParamTraitsEnumChecker.cpp
Normal file
37
build/clang-plugin/ParamTraitsEnumChecker.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
build/clang-plugin/ParamTraitsEnumChecker.h
Normal file
19
build/clang-plugin/ParamTraitsEnumChecker.h
Normal 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
|
@ -33,6 +33,7 @@ UNIFIED_SOURCES += [
|
||||
'NonParamInsideFunctionDeclChecker.cpp',
|
||||
'OverrideBaseCallChecker.cpp',
|
||||
'OverrideBaseCallUsageChecker.cpp',
|
||||
'ParamTraitsEnumChecker.cpp',
|
||||
'RefCountedCopyConstructorChecker.cpp',
|
||||
'RefCountedInsideLambdaChecker.cpp',
|
||||
'ScopeChecker.cpp',
|
||||
|
94
build/clang-plugin/tests/TestParamTraitsEnum.cpp
Normal file
94
build/clang-plugin/tests/TestParamTraitsEnum.cpp
Normal 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>
|
||||
{};
|
@ -39,6 +39,7 @@ SOURCES += [
|
||||
'TestNoRefcountedInsideLambdas.cpp',
|
||||
'TestOverrideBaseCall.cpp',
|
||||
'TestOverrideBaseCallAnnotation.cpp',
|
||||
'TestParamTraitsEnum.cpp',
|
||||
'TestRefCountedCopyConstructor.cpp',
|
||||
'TestSprintfLiteral.cpp',
|
||||
'TestStackClass.cpp',
|
||||
|
@ -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());
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -726,6 +726,8 @@ struct JSCompartment
|
||||
|
||||
js::RegExpCompartment regExps;
|
||||
|
||||
js::ArraySpeciesLookup arraySpeciesLookup;
|
||||
|
||||
using IteratorCache = js::HashSet<js::PropertyIteratorObject*,
|
||||
js::IteratorHashPolicy,
|
||||
js::SystemAllocPolicy>;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"],
|
||||
|
@ -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'
|
||||
|
@ -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>
|
155
testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html
Normal file
155
testing/talos/talos/tests/gfx/benchmarks/rasterflood_svg.html
Normal 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>
|
@ -0,0 +1 @@
|
||||
% http://localhost/tests/gfx/benchmarks/rasterflood_gradient.html
|
1
testing/talos/talos/tests/gfx/rasterflood_svg.manifest
Normal file
1
testing/talos/talos/tests/gfx/rasterflood_svg.manifest
Normal file
@ -0,0 +1 @@
|
||||
% http://localhost/tests/gfx/benchmarks/rasterflood_svg.html
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user