This commit is contained in:
Marco Bonardo 2012-03-01 16:05:54 +01:00
Родитель b9e5d41e63 f64b21e054
Коммит 3aa6f0a8fc
7 изменённых файлов: 266 добавлений и 33 удалений

Просмотреть файл

@ -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)")
/**