Merge central to inbound
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 01 Mar 2012 16:05:54 +0100
changeset 88095 21e7e8e9d75ab2ccf5d3104d6bec760a378596cb
parent 88094 9b944a4b62309b34e0b451f1ca353de89be02aa3 (current diff)
parent 88050 04caf36509e70e70a5b228bb11e86904775642c6 (diff)
child 88096 41205fe435777a75a89b120a05fb12051c6dbd7f
push id22171
push usermak77@bonardo.net
push dateFri, 02 Mar 2012 13:56:30 +0000
treeherdermozilla-central@343ec916dfd5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge central to inbound
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -152,16 +152,18 @@
 #include "prprf.h"
 
 #include "nsSVGFeatures.h"
 #include "nsDOMMemoryReporter.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsCycleCollector.h"
 #include "xpcpublic.h"
 #include "xpcprivate.h"
+#include "nsLayoutStatics.h"
+#include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
 
 PRInt32 nsIContent::sTabFocusModel = eTabFocus_any;
 bool nsIContent::sTabFocusModelAppliesToXUL = false;
@@ -4369,48 +4371,145 @@ nsINode::IsEqualNode(nsIDOMNode* aOther,
 }
 
 //----------------------------------------------------------------------
 
 // nsISupports implementation
 
 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);
 
   if (tmp->HasProperties() && tmp->IsXUL()) {
     tmp->DeleteProperty(nsGkAtoms::contextmenulistener);
     tmp->DeleteProperty(nsGkAtoms::popuplistener);
   }
 
   // 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
         // can notify various observers and they should really see consistent
         // tree state.
         nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
         if (childCount == 0) {
           tmp->mFirstChild = nsnull;
         }
         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.
   {
     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
     if (slots) {
       slots->Unlink(tmp->IsXUL());
     }
   }
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -77,16 +77,17 @@ class nsIDOMCSSStyleDeclaration;
 class nsIURI;
 class nsINodeInfo;
 class nsIControllers;
 class nsEventListenerManager;
 class nsIScrollableFrame;
 class nsAttrValueOrString;
 class nsContentList;
 class nsDOMTokenList;
+class ContentUnbinder;
 struct nsRect;
 
 typedef PRUptrdiff PtrBits;
 
 /**
  * Class that implements the nsIDOMNodeList interface (a list of children of
  * the content), by holding a reference to the content and delegating GetLength
  * and Item to its existing child list.
@@ -951,16 +952,17 @@ protected:
    *
    * Note: for HTML this gets the value of the 'target' attribute; for XLink
    * this gets the value of the xlink:_moz_target attribute, or failing that,
    * the value of xlink:show, converted to a suitably equivalent named target
    * (e.g. _blank).
    */
   virtual void GetLinkTarget(nsAString& aTarget);
 
+  friend class ContentUnbinder;
   /**
    * Array containing all attributes and children for this element
    */
   nsAttrAndChildArray mAttrsAndChildren;
 
 private:
   /**
    * Get this element's client area rect in app units.
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -59,16 +59,20 @@
 #include "nsIJSNativeInitializer.h"
 #include "nsContentUtils.h"
 
 #include "GLContextProvider.h"
 #include "Layers.h"
 
 #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
  * 
  * Exceptions: some of the following values are set to higher values than in the spec because
  * the values in the spec are ridiculously low. They are explicitly marked below
 */
 #define MINVALUE_GL_MAX_TEXTURE_SIZE                  1024  // Different from the spec, which sets it to 64 on page 162
@@ -938,16 +942,26 @@ protected:
     nsCOMPtr<nsITimer> mContextRestorer;
     bool mAllowRestore;
     bool mRobustnessTimerRunning;
     bool mDrawSinceRobustnessTimerSet;
     ContextStatus mContextStatus;
     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, ...);
     static void LogMessage(const char *fmt, va_list ap);
     void LogMessageIfVerbose(const char *fmt, ...);
     void LogMessageIfVerbose(const char *fmt, va_list ap);
 
     friend class WebGLTexture;
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
@@ -89,16 +89,36 @@ function start() {
     is106orHigher = (kDarwinVersion >= 10.0);
     if (!is106orHigher) {
       dump("WebGL mochitest disabled on Mac OSX versions older than 10.6\n");
       SimpleTest.finish();
       return;
     }
   }
 
+  // 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);
     var val = envsvc.get(env);
     if (val == "")
       return null;
     return val;
   }
@@ -125,45 +145,72 @@ function start() {
     var node = reporter.localDoc.createTextNode('');
     li.appendChild(ul);
     div.appendChild(node);
     this.totalsElem = node;
     this.resultElem = ul;
     this.elem = li;
   };
 
-  Page.prototype.isExpectedToFullyPass = function() {
-    return testsExpectedToFail.indexOf(this.url) == -1 && testsToIgnore.indexOf(this.url) == -1;
+  /**
+   * Indicates whether this test page results are not to be ignored.
+   */
+  Page.prototype.shouldBeAccountedFor = function() {
+    return testsToIgnore.indexOf(this.url) == -1;
   }
 
-  Page.prototype.errormsg = function(msg) {
-    return msg + ' (URL: ' + this.url + ')';
+  /**
+   * Indicates whether all this test page results are expected not to fail,
+   * if not ignored.
+   */
+  Page.prototype.isExpectedToFullyPass = function() {
+    return this.shouldBeAccountedFor() &&
+           testsExpectedToFail.indexOf(this.url) == -1;
   }
 
+  /**
+   * 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) {
       ++this.totalTimeouts;
       var result = "timeout";
       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);
       }
     }
 
     var node = this.reporter.localDoc.createTextNode(result + ': ' + msg);
     var li = this.reporter.localDoc.createElement('li');
     li.appendChild(node);
     li.setAttribute('class', css);
     this.resultElem.appendChild(li);
@@ -176,41 +223,52 @@ function start() {
     // remove previous results.
     while (this.resultElem.hasChildNodes()) {
       this.resultElem.removeChild(this.resultElem.childNodes[0]);
     }
     this.totalsElem.textContent = '';
     return true;
   };
 
+  /**
+   * Reports test page result summary.
+   */
   Page.prototype.finishPage = function(success) {
     var msg = ' (' + this.totalSuccessful + ' of ' +
               this.totalTests + ' passed)';
     if (success === undefined) {
       var css = 'testpagetimeout';
       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;
   };
 
   var Reporter = function() {
     this.localDoc = document;
 
@@ -241,17 +299,17 @@ function start() {
   Reporter.prototype.addPage = function(url) {
     this.currentPage = new Page(this, url, this.resultElem);
     this.resultElem.appendChild(this.currentPage.elem);
     ++this.totalPages;
     this.pagesByURL[url] = this.currentPage;
   };
 
   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.
     // The explanation is that the JS engine keeps unreferenced WebGL contexts around
     // for too long before GCing (bug 617453), so that during this mochitest dozens of unreferenced
     // WebGL contexts can accumulate at a given time.
     SpecialPowers.DOMWindowUtils.garbageCollect();
 
@@ -261,24 +319,21 @@ function start() {
     expectedtofailTextNode.textContent = testsExpectedToFail.length +
                                          ' test pages are expected to fail out of ' +
                                          this.totalPages;
     ignoredtestsTextNode.textContent = testsToIgnore.length +
                                          ' test pages have their results ignored';
     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';
   };
 
   Reporter.prototype.addResult = function(msg, success) {
     if (this.currentPage != null) {
       this.currentPage.addResult(msg, success);
     }
   };
@@ -290,19 +345,16 @@ function start() {
       this.totalSuccessful += this.currentPage.totalSuccessful;
       this.totalTimeouts += this.currentPage.totalTimeouts;
       this.currentPage = null;
       this.displayStats();
     }
   };
 
   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();
   }
 
   Reporter.prototype.ready = function() {
     statusTextNode.textContent = 'Loaded test lists. Starting tests...';
     window.webglTestHarness.runTests();
   }
@@ -348,20 +400,22 @@ function start() {
   getURLOptions(OPTIONS);
 
   function runTestSuite() {
     var reporter = new Reporter();
 
     // 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...';
         var iframe = document.getElementById("testframe");
         var testHarness = new WebGLTestHarnessModule.TestHarness(
             iframe,
             '00_test_list.txt',
             function(type, msg, success) {
@@ -430,18 +484,16 @@ function start() {
 
   if (kIsMac && kDarwinVersion >= 11.0) {
     testsExpectedToFail.push('conformance/textures/texture-mips.html');
     testsExpectedToFail.push('conformance/textures/texture-npot.html');
   }
 
   var testsToIgnore = [];
 
-  var testsSuccessful = [];
-
   runTestSuite();
 }
 
 </script>
 </head>
 <body onload="start();">
 <p id="display"></p>
 <div id="content" style="display: none">
new file mode 100644
--- /dev/null
+++ b/gfx/gl/ForceDiscreteGPUHelperCGL.h
@@ -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_
--- a/gfx/gl/Makefile.in
+++ b/gfx/gl/Makefile.in
@@ -47,16 +47,17 @@ EXPORT_LIBRARY	= 1
 
 EXPORTS	= \
 	GLDefs.h \
 	GLContext.h \
 	GLContextSymbols.h \
 	GLContextProvider.h \
 	GLContextProviderImpl.h \
 	EGLUtils.h \
+	ForceDiscreteGPUHelperCGL.h \
 	$(NULL)
 
 ifdef MOZ_X11
 EXPORTS += \
 	GLXLibrary.h \
 	$(NULL)
 endif
 
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -65,17 +65,17 @@ HISTOGRAM_BOOLEAN(A11Y_XFORMS_USAGE, "ha
  * Cycle collector telemetry
  */
 HISTOGRAM(CYCLE_COLLECTOR, 1, 10000, 50, EXPONENTIAL, "Time spent on one cycle collection (ms)")
 HISTOGRAM(CYCLE_COLLECTOR_VISITED_REF_COUNTED, 1, 300000, 50, EXPONENTIAL, "Number of ref counted objects visited by the cycle collector")
 HISTOGRAM(CYCLE_COLLECTOR_VISITED_GCED, 1, 300000, 50, EXPONENTIAL, "Number of JS objects visited by the cycle collector")
 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)")
 
 /**
  * GC telemetry
  */
 HISTOGRAM(GC_REASON, 1, 20, 20, LINEAR, "Reason (enum value) for initiating a GC")
 HISTOGRAM_BOOLEAN(GC_IS_COMPARTMENTAL, "Is it a compartmental GC?")
 HISTOGRAM(GC_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC (ms)")