зеркало из https://github.com/mozilla/gecko-dev.git
Merge central to inbound
This commit is contained in:
Коммит
3aa6f0a8fc
|
@ -157,6 +157,8 @@
|
|||
#include "nsCycleCollector.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "nsLayoutStatics.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -4374,6 +4376,102 @@ nsINode::IsEqualNode(nsIDOMNode* aOther, bool* aReturn)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericElement)
|
||||
|
||||
#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
|
||||
|
||||
class ContentUnbinder : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ContentUnbinder()
|
||||
{
|
||||
nsLayoutStatics::AddRef();
|
||||
mLast = this;
|
||||
}
|
||||
|
||||
~ContentUnbinder()
|
||||
{
|
||||
Run();
|
||||
nsLayoutStatics::Release();
|
||||
}
|
||||
|
||||
void UnbindSubtree(nsIContent* aNode)
|
||||
{
|
||||
if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
|
||||
aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
|
||||
return;
|
||||
}
|
||||
nsGenericElement* container = static_cast<nsGenericElement*>(aNode);
|
||||
PRUint32 childCount = container->mAttrsAndChildren.ChildCount();
|
||||
if (childCount) {
|
||||
while (childCount-- > 0) {
|
||||
// Hold a strong ref to the node when we remove it, because we may be
|
||||
// the last reference to it. We need to call TakeChildAt() and
|
||||
// update mFirstChild before calling UnbindFromTree, since this last
|
||||
// can notify various observers and they should really see consistent
|
||||
// tree state.
|
||||
nsCOMPtr<nsIContent> child =
|
||||
container->mAttrsAndChildren.TakeChildAt(childCount);
|
||||
if (childCount == 0) {
|
||||
container->mFirstChild = nsnull;
|
||||
}
|
||||
UnbindSubtree(child);
|
||||
child->UnbindFromTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
PRUint32 len = mSubtreeRoots.Length();
|
||||
if (len) {
|
||||
PRTime start = PR_Now();
|
||||
for (PRUint32 i = 0; i < len; ++i) {
|
||||
UnbindSubtree(mSubtreeRoots[i]);
|
||||
}
|
||||
mSubtreeRoots.Clear();
|
||||
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND,
|
||||
PRUint32(PR_Now() - start) / PR_USEC_PER_MSEC);
|
||||
}
|
||||
if (this == sContentUnbinder) {
|
||||
sContentUnbinder = nsnull;
|
||||
if (mNext) {
|
||||
nsRefPtr<ContentUnbinder> next;
|
||||
next.swap(mNext);
|
||||
sContentUnbinder = next;
|
||||
next->mLast = mLast;
|
||||
mLast = nsnull;
|
||||
NS_DispatchToMainThread(next);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void Append(nsIContent* aSubtreeRoot)
|
||||
{
|
||||
if (!sContentUnbinder) {
|
||||
sContentUnbinder = new ContentUnbinder();
|
||||
nsCOMPtr<nsIRunnable> e = sContentUnbinder;
|
||||
NS_DispatchToMainThread(e);
|
||||
}
|
||||
|
||||
if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
|
||||
SUBTREE_UNBINDINGS_PER_RUNNABLE) {
|
||||
sContentUnbinder->mLast->mNext = new ContentUnbinder();
|
||||
sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
|
||||
}
|
||||
sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoTArray<nsCOMPtr<nsIContent>,
|
||||
SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
|
||||
nsRefPtr<ContentUnbinder> mNext;
|
||||
ContentUnbinder* mLast;
|
||||
static ContentUnbinder* sContentUnbinder;
|
||||
};
|
||||
|
||||
ContentUnbinder* ContentUnbinder::sContentUnbinder = nsnull;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
||||
nsINode::Unlink(tmp);
|
||||
|
||||
|
@ -4383,16 +4481,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
|||
}
|
||||
|
||||
// Unlink child content (and unbind our subtree).
|
||||
{
|
||||
if (UnoptimizableCCNode(tmp) || !nsCCUncollectableMarker::sGeneration) {
|
||||
PRUint32 childCount = tmp->mAttrsAndChildren.ChildCount();
|
||||
if (childCount) {
|
||||
// Don't allow script to run while we're unbinding everything.
|
||||
nsAutoScriptBlocker scriptBlocker;
|
||||
while (childCount-- > 0) {
|
||||
// Once we have XPCOMGC we shouldn't need to call UnbindFromTree.
|
||||
// We could probably do a non-deep unbind here when IsInDoc is false
|
||||
// for better performance.
|
||||
|
||||
// Hold a strong ref to the node when we remove it, because we may be
|
||||
// the last reference to it. We need to call TakeChildAt() and
|
||||
// update mFirstChild before calling UnbindFromTree, since this last
|
||||
|
@ -4405,7 +4499,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericElement)
|
|||
child->UnbindFromTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
|
||||
ContentUnbinder::Append(tmp);
|
||||
} /* else {
|
||||
The subtree root will end up to a ContentUnbinder, and that will
|
||||
unbind the child nodes.
|
||||
} */
|
||||
|
||||
// Unlink any DOM slots of interest.
|
||||
{
|
||||
|
|
|
@ -82,6 +82,7 @@ class nsIScrollableFrame;
|
|||
class nsAttrValueOrString;
|
||||
class nsContentList;
|
||||
class nsDOMTokenList;
|
||||
class ContentUnbinder;
|
||||
struct nsRect;
|
||||
|
||||
typedef PRUptrdiff PtrBits;
|
||||
|
@ -956,6 +957,7 @@ protected:
|
|||
*/
|
||||
virtual void GetLinkTarget(nsAString& aTarget);
|
||||
|
||||
friend class ContentUnbinder;
|
||||
/**
|
||||
* Array containing all attributes and children for this element
|
||||
*/
|
||||
|
|
|
@ -64,6 +64,10 @@
|
|||
|
||||
#include "CheckedInt.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "ForceDiscreteGPUHelperCGL.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=686732
|
||||
|
@ -943,6 +947,16 @@ protected:
|
|||
bool mContextLostErrorSet;
|
||||
bool mContextLostDueToTest;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime
|
||||
// Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy
|
||||
// these objects at high frequency. Having WebGLContext's hold one such object seems fine,
|
||||
// because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
|
||||
// If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
|
||||
ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
// console logging helpers
|
||||
static void LogMessage(const char *fmt, ...);
|
||||
|
|
|
@ -94,6 +94,26 @@ function start() {
|
|||
}
|
||||
}
|
||||
|
||||
// we currently disable this test on version of Mac OSX older than 10.6,
|
||||
// due to various weird failures, including one making getRenderbufferParameter tests
|
||||
// on DEPTH_STENCIL fail
|
||||
var kDarwinVersion = 0;
|
||||
if (kIsMac) {
|
||||
// code borrowed from browser/modules/test/browser_taskbar_preview.js
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
kDarwinVersion = parseFloat(Components.classes["@mozilla.org/system-info;1"]
|
||||
.getService(Components.interfaces.nsIPropertyBag2)
|
||||
.getProperty("version"));
|
||||
// the next line is correct: Mac OSX 10.6 corresponds to Darwin version 10 !
|
||||
// Mac OSX 10.5 would be Darwin version 9. the |version| string we've got here
|
||||
// is the Darwin version.
|
||||
if (kDarwinVersion < 10.0) {
|
||||
todo(false, "Test disabled on Mac OSX versions older than 10.6.");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function getEnv(env) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var envsvc = Components.classes["@mozilla.org/process/environment;1"].getService(Components.interfaces.nsIEnvironment);
|
||||
|
@ -130,14 +150,32 @@ function start() {
|
|||
this.elem = li;
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates whether this test page results are not to be ignored.
|
||||
*/
|
||||
Page.prototype.shouldBeAccountedFor = function() {
|
||||
return testsToIgnore.indexOf(this.url) == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether all this test page results are expected not to fail,
|
||||
* if not ignored.
|
||||
*/
|
||||
Page.prototype.isExpectedToFullyPass = function() {
|
||||
return testsExpectedToFail.indexOf(this.url) == -1 && testsToIgnore.indexOf(this.url) == -1;
|
||||
return this.shouldBeAccountedFor() &&
|
||||
testsExpectedToFail.indexOf(this.url) == -1;
|
||||
}
|
||||
|
||||
Page.prototype.errormsg = function(msg) {
|
||||
return msg + ' (URL: ' + this.url + ')';
|
||||
/**
|
||||
* Returns log message with added test page url.
|
||||
*/
|
||||
Page.prototype.logMsg = function(msg) {
|
||||
return '[' + this.url + '] ' + msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an individual test result of test page.
|
||||
*/
|
||||
Page.prototype.addResult = function(msg, success) {
|
||||
++this.totalTests;
|
||||
if (success === undefined) {
|
||||
|
@ -146,19 +184,28 @@ function start() {
|
|||
var css = "timeout";
|
||||
// only few timeouts are actually caught here --- most are caught in finishPage().
|
||||
if (this.isExpectedToFullyPass()) {
|
||||
ok(false, this.errormsg('Test timed out, "' + msg + '"'));
|
||||
ok(false, this.logMsg('Test timed out'), msg);
|
||||
} else {
|
||||
todo(false, this.logMsg('Test timed out'), msg);
|
||||
}
|
||||
} else if (success) {
|
||||
++this.totalSuccessful;
|
||||
var result = "success";
|
||||
var css = "success";
|
||||
// don't report success.
|
||||
if (this.shouldBeAccountedFor()) {
|
||||
ok(true, this.logMsg('Test passed'), msg);
|
||||
} else {
|
||||
todo(false, this.logMsg('Test passed, but is ignored'), msg);
|
||||
}
|
||||
// Don't report individual success to UI, to keep it light.
|
||||
return;
|
||||
} else {
|
||||
var result = "failed";
|
||||
var css = "fail";
|
||||
if (this.isExpectedToFullyPass()) {
|
||||
ok(false, this.errormsg('Test failed, "' + msg + '"'));
|
||||
ok(false, this.logMsg('Test failed'), msg);
|
||||
} else {
|
||||
todo(false, this.logMsg('Test failed'), msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,6 +228,9 @@ function start() {
|
|||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reports test page result summary.
|
||||
*/
|
||||
Page.prototype.finishPage = function(success) {
|
||||
var msg = ' (' + this.totalSuccessful + ' of ' +
|
||||
this.totalTests + ' passed)';
|
||||
|
@ -189,23 +239,31 @@ function start() {
|
|||
msg = '(*timeout*)';
|
||||
++this.totalTests;
|
||||
++this.totalTimeouts;
|
||||
// Most timeouts are only caught here --- though a few are (already) caught in addResult().
|
||||
if (this.isExpectedToFullyPass()) {
|
||||
ok(false, this.errormsg('Unexpected timeout in this test page'));
|
||||
window.dump('WebGL test error: test page timeout: ' + this.url + '\n');
|
||||
ok(false, this.logMsg('Timeout in this test page'));
|
||||
} else {
|
||||
todo(false, this.logMsg('Timeout in this test page'));
|
||||
}
|
||||
} else if (this.totalSuccessful != this.totalTests) {
|
||||
var css = 'testpagefail';
|
||||
var totalFailed = this.totalTests - this.totalTimeouts - this.totalSuccessful;
|
||||
if (this.isExpectedToFullyPass()) {
|
||||
window.dump('WebGL test error: test page failure: ' + this.url + '\n');
|
||||
ok(false, this.logMsg("(WebGL test error) " + totalFailed + ' failure(s) and ' + this.totalTimeouts + ' timeout(s)'));
|
||||
} else {
|
||||
todo(false, this.logMsg("(WebGL test error) " + totalFailed + ' failure(s) and ' + this.totalTimeouts + ' timeout(s)'));
|
||||
}
|
||||
// failures have already been reported for the sub-tests
|
||||
} else {
|
||||
var css = 'testpagesuccess';
|
||||
if (this.isExpectedToFullyPass()) {
|
||||
ok(true, this.errormsg('Successful test page'));
|
||||
ok(true, this.logMsg('All ' + this.totalSuccessful + ' test(s) passed'));
|
||||
} else {
|
||||
if (this.shouldBeAccountedFor()) {
|
||||
todo(true, this.logMsg('Test page expected to fail, but all ' + this.totalSuccessful + ' tests passed'));
|
||||
} else {
|
||||
todo(false, this.logMsg('All ' + this.totalSuccessful + ' test(s) passed, but test page is ignored'));
|
||||
}
|
||||
}
|
||||
window.dump('WebGL test page successful: ' + this.url + '\n');
|
||||
testsSuccessful.push(this.url);
|
||||
}
|
||||
this.elem.setAttribute('class', css);
|
||||
this.totalsElem.textContent = msg;
|
||||
|
@ -246,7 +304,7 @@ function start() {
|
|||
};
|
||||
|
||||
Reporter.prototype.startPage = function(url) {
|
||||
dump('WebGL mochitest: starting page ' + url + '\n');
|
||||
info("[" + url + "] (WebGL mochitest) Starting test page");
|
||||
|
||||
// Calling garbageCollect before each test page fixes intermittent failures with
|
||||
// out-of-memory errors, often failing to create a WebGL context.
|
||||
|
@ -266,14 +324,11 @@ function start() {
|
|||
return page.startPage();
|
||||
};
|
||||
|
||||
Reporter.prototype.totalFailed = function() {
|
||||
return this.totalTests - this.totalSuccessful;
|
||||
};
|
||||
|
||||
Reporter.prototype.displayStats = function() {
|
||||
var totalFailed = this.totalTests - this.totalTimeouts - this.totalSuccessful;
|
||||
this.fullResultsNode.textContent =
|
||||
this.totalSuccessful + ' passed, ' +
|
||||
this.totalFailed() + ' failed, ' +
|
||||
totalFailed + ' failed, ' +
|
||||
this.totalTimeouts + ' timed out';
|
||||
};
|
||||
|
||||
|
@ -295,9 +350,6 @@ function start() {
|
|||
};
|
||||
|
||||
Reporter.prototype.finishedTestSuite = function() {
|
||||
for (var i = 0; i < testsExpectedToFail.length; ++i)
|
||||
if (testsSuccessful.indexOf(testsExpectedToFail[i]) != -1)
|
||||
todo(true, 'Test expected to fail, but passed: ' + testsExpectedToFail[i]);
|
||||
statusTextNode.textContent = 'Finished';
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
@ -353,10 +405,12 @@ function start() {
|
|||
// try to create a dummy WebGL context, just to catch context creation failures once here,
|
||||
// rather than having them result in 100's of failures (one in each test page)
|
||||
var canvas = document.getElementById("webglcheck-default");
|
||||
var ctx = null;
|
||||
var ctx;
|
||||
try {
|
||||
ctx = canvas.getContext("experimental-webgl");
|
||||
} catch(e) {}
|
||||
} catch(e) {
|
||||
ok(false, "canvas.getContext() failed", e);
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
statusTextNode.textContent = 'Loading test lists...';
|
||||
|
@ -435,8 +489,6 @@ function start() {
|
|||
|
||||
var testsToIgnore = [];
|
||||
|
||||
var testsSuccessful = [];
|
||||
|
||||
runTestSuite();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef ForceDiscreteGPUHelperCGL_h_
|
||||
#define ForceDiscreteGPUHelperCGL_h_
|
||||
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
/** This RAII helper guarantees that we're on the discrete GPU during its lifetime.
|
||||
*
|
||||
* As long as any ForceDiscreteGPUHelperCGL object is alive, we're on the discrete GPU.
|
||||
*/
|
||||
class ForceDiscreteGPUHelperCGL
|
||||
{
|
||||
CGLPixelFormatObj mPixelFormatObj;
|
||||
|
||||
public:
|
||||
ForceDiscreteGPUHelperCGL()
|
||||
{
|
||||
// the code in this function is taken from Chromium, src/ui/gfx/gl/gl_context_cgl.cc, r122013
|
||||
// BSD-style license, (c) The Chromium Authors
|
||||
CGLPixelFormatAttribute attribs[1];
|
||||
attribs[0] = static_cast<CGLPixelFormatAttribute>(0);
|
||||
GLint num_pixel_formats = 0;
|
||||
CGLChoosePixelFormat(attribs, &mPixelFormatObj, &num_pixel_formats);
|
||||
}
|
||||
|
||||
~ForceDiscreteGPUHelperCGL()
|
||||
{
|
||||
CGLReleasePixelFormat(mPixelFormatObj);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ForceDiscreteGPUHelperCGL_h_
|
|
@ -52,6 +52,7 @@ EXPORTS = \
|
|||
GLContextProvider.h \
|
||||
GLContextProviderImpl.h \
|
||||
EGLUtils.h \
|
||||
ForceDiscreteGPUHelperCGL.h \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_X11
|
||||
|
|
|
@ -70,7 +70,7 @@ HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of J
|
|||
HISTOGRAM(CYCLE_COLLECTOR_COLLECTED, 1, 100000, 50, EXPONENTIAL, "Number of objects collected by the cycle collector")
|
||||
HISTOGRAM_BOOLEAN(CYCLE_COLLECTOR_NEED_GC, "Needed garbage collection before cycle collection.")
|
||||
HISTOGRAM(CYCLE_COLLECTOR_TIME_BETWEEN, 1, 120, 50, EXPONENTIAL, "Time spent in between cycle collections (seconds)")
|
||||
|
||||
HISTOGRAM(CYCLE_COLLECTOR_CONTENT_UNBIND, 1, 10000, 50, EXPONENTIAL, "Time spent on one ContentUnbinder (ms)")
|
||||
HISTOGRAM(FORGET_SKIPPABLE_MAX, 1, 10000, 50, EXPONENTIAL, "Max time spent on one forget skippable (ms)")
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче