Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 31 Jan 2012 12:17:35 -0800
changeset 105633 ce25bca1ad68dd9e4b87644c001804071a4bf600
parent 105632 990ee31c370a209355cb2d85d07be97b085108b8 (current diff)
parent 85842 29514d9b42165ca04c0d5a8d7dae9934c0dc1955 (diff)
child 105634 4e487dfde168fd6bd9ba69aaf747e6d5e6cd2536
push id23447
push userdanderson@mozilla.com
push dateTue, 11 Sep 2012 17:34:27 +0000
treeherdermozilla-central@fdfaef738a00 [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 from mozilla-central.
.hgtags
accessible/src/base/nsAccessible.cpp
accessible/src/base/nsRootAccessible.cpp
accessible/src/base/nsRootAccessible.h
accessible/tests/mochitest/relations/test_general.html
b2g/confvars.sh
b2g/installer/package-manifest.in
browser/app/profile/firefox.js
browser/config/version.txt
config/milestone.txt
config/system-headers
configure.in
content/base/public/nsContentUtils.h
content/base/public/nsIContent.h
content/base/public/nsINode.h
content/base/src/nsCCUncollectableMarker.cpp
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/base/test/Makefile.in
content/base/test/test_XHR.html
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
content/canvas/test/webgl/test_webgl_conformance_test_suite.html
content/canvas/test/webgl/undo-r15330-async-test-list-loading.patch
content/events/src/nsContentEventHandler.cpp
content/html/document/src/nsHTMLDocument.cpp
content/smil/nsSMILTimedElement.cpp
content/smil/nsSMILTimedElement.h
dom/Makefile.in
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
dom/dom-config.mk
dom/interfaces/base/domstubs.idl
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/workers/WorkerPrivate.cpp
embedding/android/AndroidManifest.xml.in
embedding/android/Makefile.in
extensions/auth/nsAuthGSSAPI.cpp
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxWindowsPlatform.cpp
image/src/RasterImage.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/Makefile.in
js/src/MemoryMetrics.cpp
js/src/config/milestone.txt
js/src/config/system-headers
js/src/frontend/BytecodeEmitter.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/methodjit/PolyIC.cpp
js/src/shell/js.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
layout/base/FrameLayerBuilder.cpp
layout/base/crashtests/crashtests.list
layout/base/nsBidiPresUtils.cpp
layout/base/nsLayoutUtils.cpp
layout/build/Makefile.in
layout/build/nsLayoutModule.cpp
layout/forms/nsFormControlFrame.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/reftests/bugs/reftest.list
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/xul/app/mobile.js
mobile/xul/confvars.sh
modules/libpref/src/init/all.js
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
testing/mochitest/runtestsremote.py
testing/mochitest/specialpowers/content/specialpowers.js
testing/testsuite-targets.mk
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
toolkit/content/tests/chrome/test_bug649840.xul
toolkit/themes/pinstripe/global/media/videocontrols.css
toolkit/themes/winstripe/global/media/videocontrols.css
widget/cocoa/nsCocoaWindow.mm
widget/tests/window_bug596600.xul
xpcom/base/nsMemoryReporterManager.cpp
xpcom/components/Module.h
--- a/.hgtags
+++ b/.hgtags
@@ -68,8 +68,11 @@ 138f593553b66c9f815e8f57870c19d6347f7702
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
+bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
+0000000000000000000000000000000000000000 AURORA_BASE_20120131
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -588,40 +588,23 @@ nsAccessible::TranslateString(const nsAS
   aStringOut.Assign(xsValue);
 }
 
 PRUint64
 nsAccessible::VisibilityState()
 {
   PRUint64 vstates = states::INVISIBLE | states::OFFSCREEN;
 
-  // We need to check the parent chain for visibility.
-  nsAccessible* accessible = this;
-  do {
-    // We don't want background tab page content to be aggressively invisible.
-    // Otherwise this foils screen reader virtual buffer caches.
-    roles::Role role = accessible->Role();
-    if (role == roles::PROPERTYPAGE || role == roles::PANE)
-      break;
-
-    nsIFrame* frame = accessible->GetFrame();
-    if (!frame)
-      return vstates;
-
-    const nsIView* view = frame->GetView();
-    if (view && view->GetVisibility() == nsViewVisibility_kHide)
-      return vstates;
-    
-  } while (accessible = accessible->Parent());
-
   nsIFrame* frame = GetFrame();
   if (!frame)
     return vstates;
 
   const nsCOMPtr<nsIPresShell> shell(GetPresShell());
+  if (!shell)
+    return vstates;
 
   // We need to know if at least a kMinPixels around the object is visible,
   // otherwise it will be marked states::OFFSCREEN.
   const PRUint16 kMinPixels  = 12;
   const nsSize frameSize = frame->GetSize();
   const nsRectVisibility rectVisibility =
     shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
                              nsPresContext::CSSPixelsToAppUnits(kMinPixels));
@@ -639,16 +622,20 @@ nsAccessible::VisibilityState()
       frame->GetRect().IsEmpty()) {
     nsAutoString renderedText;
     frame->GetRenderedText(&renderedText, nsnull, nsnull, 0, 1);
     if (renderedText.IsEmpty())
       return vstates;
 
   }
 
+  // XXX Do we really need to cross from content to chrome ancestor?
+  if (!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY))
+    return vstates;
+
   // Assume we are visible enough.
   return vstates &= ~states::INVISIBLE;
 }
 
 PRUint64
 nsAccessible::NativeState()
 {
   PRUint64 state = 0;
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -569,84 +569,42 @@ nsRootAccessible::Shutdown()
 {
   // Called manually or by nsAccessNode::LastRelease()
   if (!mWeakShell)
     return;  // Already shutdown
 
   nsDocAccessibleWrap::Shutdown();
 }
 
-// nsRootAccessible protected member
-already_AddRefed<nsIDocShellTreeItem>
-nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
-{
-  if (!aStart) {
-    return nsnull;
-  }
-
-  PRInt32 itemType;
-  aStart->GetItemType(&itemType);
-  if (itemType == nsIDocShellTreeItem::typeContent) {
-    nsDocAccessible *accDoc = nsAccUtils::GetDocAccessibleFor(aStart);
-
-    // Hidden documents don't have accessibles (like SeaMonkey's sidebar),
-    // they are of no interest for a11y.
-    if (!accDoc)
-      return nsnull;
-
-    // If ancestor chain of accessibles is not completely visible,
-    // don't use this one. This happens for example if it's inside
-    // a background tab (tabbed browsing)
-    nsAccessible* parent = accDoc->Parent();
-    while (parent) {
-      if (parent->State() & states::INVISIBLE)
-        return nsnull;
-
-      if (parent == this)
-        break; // Don't check past original root accessible we started with
-
-      parent = parent->Parent();
-    }
-
-    NS_ADDREF(aStart);
-    return aStart;
-  }
-  nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
-  if (treeNode) {
-    PRInt32 subDocuments;
-    treeNode->GetChildCount(&subDocuments);
-    for (PRInt32 count = 0; count < subDocuments; count ++) {
-      nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem;
-      treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
-      NS_ENSURE_TRUE(treeItemChild, nsnull);
-      contentTreeItem = GetContentDocShell(treeItemChild);
-      if (contentTreeItem) {
-        NS_ADDREF(aStart = contentTreeItem);
-        return aStart;
-      }
-    }
-  }
-  return nsnull;
-}
-
 // nsIAccessible method
 Relation
 nsRootAccessible::RelationByType(PRUint32 aType)
 {
   if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS)
     return nsDocAccessibleWrap::RelationByType(aType);
 
-  nsCOMPtr<nsIDocShellTreeItem> treeItem =
-    nsCoreUtils::GetDocShellTreeItemFor(mDocument);
-  nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem);
-  // there may be no content area, so we need a null check
-  if (!contentTreeItem)
-    return Relation();
+  nsIDOMWindow* rootWindow = mDocument->GetWindow();
+  if (rootWindow) {
+    nsCOMPtr<nsIDOMWindow> contentWindow;
+    rootWindow->GetContent(getter_AddRefs(contentWindow));
+    if (contentWindow) {
+      nsCOMPtr<nsIDOMDocument> contentDOMDocument;
+      contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
+      nsCOMPtr<nsIDocument> contentDocumentNode =
+        do_QueryInterface(contentDOMDocument);
+      if (contentDocumentNode) {
+        nsDocAccessible* contentDocument =
+          GetAccService()->GetDocAccessible(contentDocumentNode);
+        if (contentDocument)
+          return Relation(contentDocument);
+      }
+    }
+  }
 
-  return Relation(nsAccUtils::GetDocAccessibleFor(contentTreeItem));
+  return Relation();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
 {
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -122,18 +122,17 @@ protected:
 #ifdef MOZ_XUL
     void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
                                         nsXULTreeAccessible* aAccessible);
     void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
                                     nsXULTreeAccessible* aAccessible);
 
     PRUint32 GetChromeFlags();
 #endif
-    already_AddRefed<nsIDocShellTreeItem>
-           GetContentDocShell(nsIDocShellTreeItem *aStart);
+
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
 
 inline nsRootAccessible*
 nsAccessible::AsRoot()
 {
--- a/accessible/tests/mochitest/relations/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -43,16 +43,17 @@ VPATH		= @srcdir@
 relativesrcdir  = accessible/relations
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 # test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
 
 _TEST_FILES =\
+		test_embeds.xul \
 		test_general.html \
 		test_general.xul \
 		test_tree.xul \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_embeds.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        title="Embeds relation tests">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../relations.js"></script>
+
+  <script type="application/javascript">
+  <![CDATA[
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers
+
+    function tabBrowser()
+    {
+      return gBrowserWnd.gBrowser;
+    }
+
+    function currentBrowser()
+    {
+      return tabBrowser().selectedBrowser;
+    }
+
+    function currentTabDocument()
+    {
+      return currentBrowser().contentDocument;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function loadURI(aURI)
+    {
+      this.invoke = function loadURI_invoke()
+      {
+        tabBrowser().loadURI(aURI);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, currentBrowser)
+      ];
+
+      this.finalCheck = function loadURI_finalCheck()
+      {
+        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                     getAccessible(currentTabDocument()));
+      }
+
+      this.getID = function loadURI_getID()
+      {
+        return "load uri " + aURI;
+      }
+    }
+
+    function loadOneTab(aURI)
+    {
+      this.invoke = function loadOneTab_invoke()
+      {
+        tabBrowser().loadOneTab(aURI, null, null, null, false);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, currentBrowser)
+      ];
+
+      this.finalCheck = function loadURI_finalCheck()
+      {
+        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                     getAccessible(currentTabDocument()));
+      }
+
+      this.getID = function loadOneTab_getID()
+      {
+        return "load uri '" + aURI + "' in new tab";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Testing
+
+    var gBrowserWnd = null;
+    function loadBrowser()
+    {
+      gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
+                                      "chrome,all,dialog=no", "about:");
+
+      addA11yLoadEvent(startTests, gBrowserWnd);
+    }
+
+    function startTests()
+    {
+      // Wait for tab load.
+      var browser = currentBrowser();
+      addA11yLoadEvent(doTests, browser.contentWindow);
+    }
+
+    //gA11yEventDumpToConsole = true; // debug
+
+    var gQueue = null;
+    function doTests()
+    {
+      testRelation(gBrowserWnd.document, RELATION_EMBEDS,
+                   getAccessible(currentTabDocument()));
+
+      gQueue = new eventQueue();
+
+      gQueue.push(new loadURI("about:about"));
+      gQueue.push(new loadOneTab("about:mozilla"));
+
+      gQueue.onFinish = function()
+      {
+        gBrowserWnd.close();
+      }
+      gQueue.invoke();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addLoadEvent(loadBrowser);
+  ]]>
+  </script>
+
+  <vbox flex="1" style="overflow: auto;">
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <a target="_blank"
+       href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654"
+       title="Embeds relation on root accessible can return not content document">
+      Mozilla Bug 707654
+    </a>
+    <p id="display"></p>
+    <div id="content" style="display: none">
+    </div>
+    <pre id="test">
+    </pre>
+  </body>
+  </vbox>
+</window>
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -121,29 +121,16 @@
       testRelation("caption", RELATION_LABEL_FOR, "table");
       testRelation("table", RELATION_LABELLED_BY, "caption");
 
       // 'labelled by'/'label for' relation for html:fieldset and
       // html:legend
       testRelation("legend", RELATION_LABEL_FOR, "fieldset");
       testRelation("fieldset", RELATION_LABELLED_BY, "legend");
 
-      // 'embeds' relation for root accessible
-      var docAcc = null;
-      var parentOfDocAcc = null;
-      var parentDocAcc = getAccessible(document);
-      do {
-        docAcc = parentDocAcc;
-        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.document,
-                                     [nsIAccessible]);
-      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
-
-      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
-
       // finish test
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/accessible/tests/mochitest/relations/test_general.xul
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -98,29 +98,16 @@
       // aria-flowto, multiple relations
       testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
       testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
       testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
 
       // 'default button' relation
       testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit");
 
-      // 'embeds' relation for root accessible
-      var docAcc = null;
-      var parentOfDocAcc = null;
-      var parentDocAcc = getAccessible(document);
-      do {
-        docAcc = parentDocAcc;
-        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
-        parentDocAcc = getAccessible(parentOfDocAcc.document,
-                                     [nsIAccessible]);
-      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
-
-      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
-
       // 'labelled by'/'label for' relation for xul:goupbox and xul:label of
       // xul:caption
       var groupboxAcc = getAccessible("groupbox");
       var labelAcc = groupboxAcc.firstChild;
       testRelation(labelAcc, RELATION_LABEL_FOR, groupboxAcc);
       testRelation(groupboxAcc, RELATION_LABELLED_BY, labelAcc);
 
       // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -42,16 +42,17 @@ MOZ_APP_VERSION=11.0a1
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=
 
+MOZ_WEBSMS_BACKEND=1
 MOZ_DISABLE_DOMCRYPTO=1
 MOZ_APP_STATIC_INI=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
 MOZ_RAW=1
 fi
 
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -103,21 +103,16 @@ include $(topsrcdir)/config/config.mk
 
 ifdef _MSC_VER
 # Always enter a Windows program through wmain, whether or not we're
 # a console application.
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
 
 ifeq ($(OS_ARCH),WINNT)
-OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool)
-OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32)
-endif
-
-ifeq ($(OS_ARCH),WINNT)
 RCINCLUDE = splash.rc
 ifndef GNU_CC
 RCFLAGS += -DMOZ_PHOENIX -I$(srcdir)
 else
 RCFLAGS += -DMOZ_PHOENIX --include-dir $(srcdir)
 endif
 ifdef DEBUG
 RCFLAGS += -DDEBUG
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -275,30 +275,30 @@ pref("browser.urlbar.clickSelectsAll", f
 #else
 pref("browser.urlbar.clickSelectsAll", true);
 #endif
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.urlbar.doubleClickSelectsAll", true);
 #else
 pref("browser.urlbar.doubleClickSelectsAll", false);
 #endif
-pref("browser.urlbar.autoFill", true);
+pref("browser.urlbar.autoFill", false);
 // 0: Match anywhere (e.g., middle of words)
 // 1: Match on word boundaries and then try matching anywhere
 // 2: Match only on word boundaries (e.g., after / or .)
 // 3: Match at the beginning of the url or title
 pref("browser.urlbar.matchBehavior", 1);
 pref("browser.urlbar.filter.javascript", true);
 
 // the maximum number of results to show in autocomplete when doing richResults
 pref("browser.urlbar.maxRichResults", 12);
 // The amount of time (ms) to wait after the user has stopped typing
 // before starting to perform autocomplete.  50 is the default set in
 // autocomplete.xml.
-pref("browser.urlbar.delay", 0);
+pref("browser.urlbar.delay", 50);
 
 // The special characters below can be typed into the urlbar to either restrict
 // the search to visited history, bookmarked, tagged pages; or force a match on
 // just the title text or url.
 pref("browser.urlbar.restrict.history", "^");
 pref("browser.urlbar.restrict.bookmark", "*");
 pref("browser.urlbar.restrict.tag", "+");
 pref("browser.urlbar.restrict.openpage", "%");
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -88,16 +88,17 @@ endif
 #
 # browser_sanitizeDialog_treeView.js is disabled until the tree view is added
 # back to the clear recent history dialog (sanitize.xul), if it ever is (bug
 # 480169)
 
 # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
 
 # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
+# browser_urlbarAutoFillTrimURLs.js is disabled till bug 720792 is fixed
 
 _BROWSER_FILES = \
                  head.js \
                  browser_typeAheadFind.js \
                  browser_keywordSearch.js \
                  browser_allTabsPanel.js \
                  browser_alltabslistener.js \
                  browser_bug304198.js \
@@ -215,17 +216,16 @@ endif
                  browser_scope.js \
                  browser_selectTabAtIndex.js \
                  browser_tab_dragdrop.js \
                  browser_tab_dragdrop2.js \
                  browser_tab_dragdrop2_frame1.xul \
                  browser_tabfocus.js \
                  browser_tabs_isActive.js \
                  browser_tabs_owner.js \
-                 browser_urlbarAutoFillTrimURLs.js \
                  browser_urlbarCopying.js \
                  browser_urlbarEnter.js \
                  browser_urlbarTrimURLs.js \
                  browser_urlHighlight.js \
                  browser_visibleFindSelection.js \
                  browser_visibleTabs.js \
                  browser_visibleTabs_contextMenu.js \
                  browser_visibleTabs_bookmarkAllPages.js \
--- a/browser/config/mozconfigs/linux32/release
+++ b/browser/config/mozconfigs/linux32/release
@@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/linux64/release
+++ b/browser/config/mozconfigs/linux64/release
@@ -13,8 +13,11 @@ mk_add_options PROFILE_GEN_SCRIPT='$(PYT
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/macosx-universal/release
+++ b/browser/config/mozconfigs/macosx-universal/release
@@ -9,8 +9,11 @@ ac_add_options --enable-official-brandin
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 ac_add_options --enable-warnings-as-errors
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
--- a/browser/config/mozconfigs/win32/debug
+++ b/browser/config/mozconfigs/win32/debug
@@ -1,7 +1,9 @@
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/l10n-mozconfig
+++ b/browser/config/mozconfigs/win32/l10n-mozconfig
@@ -1,4 +1,6 @@
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --enable-official-branding
 ac_add_options --with-l10n-base=../../l10n-central
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/nightly
+++ b/browser/config/mozconfigs/win32/nightly
@@ -9,8 +9,10 @@ ac_add_options --enable-jemalloc
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 mk_add_options MOZ_MAKE_FLAGS=-j1
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/mozconfigs/win32/release
+++ b/browser/config/mozconfigs/win32/release
@@ -6,8 +6,10 @@ ac_add_options --enable-update-channel=$
 ac_add_options --enable-update-packaging
 ac_add_options --enable-jemalloc
 ac_add_options --enable-official-branding
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
+
+. $topsrcdir/browser/config/mozconfigs/win32/vs2010-mozconfig
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-12.0a1
+13.0a1
--- a/browser/devtools/styleeditor/styleeditor.css
+++ b/browser/devtools/styleeditor/styleeditor.css
@@ -68,16 +68,24 @@ li.error > .stylesheet-info > .styleshee
   display: inline;
   cursor: pointer;
 }
 
 .splitview-nav > li > hgroup.stylesheet-info {
   -moz-box-pack: center;
 }
 
+.stylesheet-name {
+  white-space: nowrap;
+}
+
+li.unsaved > hgroup > h1 > .stylesheet-name:before {
+  content: "*";
+}
+
 .stylesheet-enabled {
   display: -moz-box;
 }
 
 .stylesheet-saveButton {
   display: none;
 }
 
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js
@@ -29,16 +29,27 @@ function run(aChrome)
   is(aChrome.editors.length, 2,
      "there is 2 stylesheets initially");
 }
 
 let gAddedCount = 0;  // to add new stylesheet after the 2 initial stylesheets
 let gNewEditor;       // to make sure only one new stylesheet got created
 let gUpdateCount = 0; // to make sure only one Update event is triggered
 let gCommitCount = 0; // to make sure only one Commit event is triggered
+let gTransitionEndCount = 0;
+
+function finishOnTransitionEndAndCommit() {
+  if (gCommitCount && gTransitionEndCount) {
+    is(gUpdateCount, 1, "received one Update event");
+    is(gCommitCount, 1, "received one Commit event");
+    is(gTransitionEndCount, 1, "received one transitionend event");
+
+    finish();
+  }
+}
 
 function testEditorAdded(aChrome, aEditor)
 {
   gAddedCount++;
   if (gAddedCount == 2) {
     waitForFocus(function () { // create a new style sheet
       let newButton = gChromeWindow.document.querySelector(".style-editor-newButton");
       EventUtils.synthesizeMouseAtCenter(newButton, {}, gChromeWindow);
@@ -81,16 +92,26 @@ function testEditorAdded(aChrome, aEdito
 
         for each (let c in TESTCASE_CSS_SOURCE) {
           EventUtils.synthesizeKey(c, {}, gChromeWindow);
         }
 
         is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
            "rule bracket has been auto-closed");
 
+        // we know that the testcase above will start a CSS transition
+        content.addEventListener("transitionend", function () {
+          gTransitionEndCount++;
+
+          let computedStyle = content.getComputedStyle(content.document.body, null);
+          is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
+             "content's background color has been updated to red");
+
+          executeSoon(finishOnTransitionEndAndCommit);
+        }, false);
       }, gChromeWindow) ;
     },
 
     onUpdate: function (aEditor) {
       gUpdateCount++;
 
       ok(content.document.documentElement.classList.contains(TRANSITION_CLASS),
          "StyleEditor's transition class has been added to content");
@@ -104,32 +125,23 @@ function testEditorAdded(aChrome, aEdito
       ok(aEditor.hasFlag("unsaved"),
          "new editor has UNSAVED flag after modification");
 
       let summary = aChrome.getSummaryElementForEditor(aEditor);
       let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
       is(parseInt(ruleCount), 1,
          "new editor shows 1 rule after modification");
 
-      let computedStyle = content.getComputedStyle(content.document.body, null);
-      is(computedStyle.backgroundColor, "rgb(255, 0, 0)",
-         "content's background color has been updated to red");
-
       ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS),
          "StyleEditor's transition class has been removed from content");
 
-      executeSoon(function () {
-        is(gUpdateCount, 1, "received only one Update event (throttle)");
-        is(gCommitCount, 1, "received only one Commit event (throttle)");
+      aEditor.removeActionListener(listener);
+      gNewEditor = null;
 
-        aEditor.removeActionListener(listener);
-
-        gNewEditor = null;
-        finish();
-      });
+      executeSoon(finishOnTransitionEndAndCommit);
     }
   };
 
   aEditor.addActionListener(listener);
   if (aEditor.sourceEditor) {
     listener.onAttach(aEditor);
   }
 }
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -225,35 +225,33 @@ Tilt.prototype = {
     this.tiltButton.checked = false;
   },
 
   /**
    * Handles the event fired when a tab is selected.
    */
   _onTabSelect: function T__onTabSelect()
   {
-    if (this.visualizers[this.currentWindowId]) {
+    if (this.currentInstance) {
       Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.SHOWN, null);
     } else {
       Services.obs.notifyObservers(null, TILT_NOTIFICATIONS.HIDDEN, null);
     }
   },
 
   /**
    * A node was selected in the Inspector.
    * Called from InspectorUI.
    *
    * @param {Element} aNode
    *                  the newly selected node
    */
   update: function T_update(aNode) {
-    let id = this.currentWindowId;
-
-    if (this.visualizers[id]) {
-      this.visualizers[id].presenter.highlightNode(aNode);
+    if (this.currentInstance) {
+      this.currentInstance.presenter.highlightNode(aNode);
     }
   },
 
   /**
    * Add the browser event listeners to handle state changes.
    * Called from InspectorUI.
    */
   setup: function T_setup()
@@ -272,27 +270,28 @@ Tilt.prototype = {
     Services.obs.addObserver(
       this._whenInitializing.bind(this), TILT_NOTIFICATIONS.INITIALIZING, false);
     Services.obs.addObserver(
       this._whenDestroyed.bind(this), TILT_NOTIFICATIONS.DESTROYED, false);
     Services.obs.addObserver(
       this._whenShown.bind(this), TILT_NOTIFICATIONS.SHOWN, false);
     Services.obs.addObserver(
       this._whenHidden.bind(this), TILT_NOTIFICATIONS.HIDDEN, false);
+
     Services.obs.addObserver(function(aSubject, aTopic, aWinId) {
       this.destroy(aWinId); }.bind(this),
       this.chromeWindow.InspectorUI.INSPECTOR_NOTIFICATIONS.DESTROYED, false);
 
     this.chromeWindow.gBrowser.tabContainer.addEventListener("TabSelect",
       this._onTabSelect.bind(this), false);
 
 
     // FIXME: this shouldn't be done here, see bug #705131
     let onOpened = function() {
-      if (this.visualizers[this.currentWindowId]) {
+      if (this.currentInstance) {
         this.chromeWindow.InspectorUI.stopInspecting();
         this.inspectButton.disabled = true;
         this.highlighterContainer.style.display = "none";
       }
     }.bind(this);
 
     let onClosed = function() {
       this.inspectButton.disabled = false;
@@ -321,18 +320,26 @@ Tilt.prototype = {
            (TiltGL.isWebGLForceEnabled() || TiltGL.isWebGLSupported()));
   },
 
   /**
    * Gets the ID of the current window object to identify the visualizer.
    */
   get currentWindowId()
   {
-    let gBrowser = this.chromeWindow.gBrowser;
-    return TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
+    return TiltUtils.getWindowId(
+      this.chromeWindow.gBrowser.selectedBrowser.contentWindow);
+  },
+
+  /**
+   * Gets the visualizer instance for the current tab.
+   */
+  get currentInstance()
+  {
+    return this.visualizers[this.currentWindowId];
   },
 
   /**
    * Gets the Tilt button in the Inspector toolbar.
    */
   get tiltButton()
   {
     return this.chromeWindow.document.getElementById(
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -1047,16 +1047,17 @@ TiltVisualizer.Controller.prototype = {
     canvas.addEventListener("mousedown", this.onMouseDown, false);
     canvas.addEventListener("mouseup", this.onMouseUp, false);
     canvas.addEventListener("mousemove", this.onMouseMove, false);
     canvas.addEventListener("mouseover", this.onMouseOver, false);
     canvas.addEventListener("mouseout", this.onMouseOut, false);
     canvas.addEventListener("MozMousePixelScroll", this.onMozScroll, false);
     canvas.addEventListener("keydown", this.onKeyDown, false);
     canvas.addEventListener("keyup", this.onKeyUp, false);
+    canvas.addEventListener("keypress", this.onKeyPress, true);
     canvas.addEventListener("blur", this.onBlur, false);
 
     // handle resize events to change the arcball dimensions
     presenter.contentWindow.addEventListener("resize", this.onResize, false);
   },
 
   /**
    * Removes all added events listeners required by this controller.
@@ -1069,16 +1070,17 @@ TiltVisualizer.Controller.prototype = {
     canvas.removeEventListener("mousedown", this.onMouseDown, false);
     canvas.removeEventListener("mouseup", this.onMouseUp, false);
     canvas.removeEventListener("mousemove", this.onMouseMove, false);
     canvas.removeEventListener("mouseover", this.onMouseOver, false);
     canvas.removeEventListener("mouseout", this.onMouseOut, false);
     canvas.removeEventListener("MozMousePixelScroll", this.onMozScroll, false);
     canvas.removeEventListener("keydown", this.onKeyDown, false);
     canvas.removeEventListener("keyup", this.onKeyUp, false);
+    canvas.removeEventListener("keypress", this.onKeyPress, true);
     canvas.removeEventListener("blur", this.onBlur, false);
 
     presenter.contentWindow.removeEventListener("resize", this.onResize,false);
   },
 
   /**
    * Function called each frame, updating the visualization camera transforms.
    *
@@ -1212,34 +1214,42 @@ TiltVisualizer.Controller.prototype = {
   },
 
   /**
    * Called when a key is released.
    */
   onKeyUp: function TVC_onKeyUp(e)
   {
     let code = e.keyCode || e.which;
-    let tilt = this.presenter.chromeWindow.Tilt;
 
-    if (code === e.DOM_VK_ESCAPE) {
-      tilt.destroy(tilt.currentWindowId, true);
-      return;
-    }
     if (code === e.DOM_VK_X) {
       this.presenter.deleteNode();
     }
-
     if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
       e.preventDefault();
       e.stopPropagation();
       this.arcball.keyUp(code);
     }
   },
 
   /**
+   * Called when a key is pressed.
+   */
+  onKeyPress: function TVC_onKeyPress(e)
+  {
+    let tilt = this.presenter.chromeWindow.Tilt;
+
+    if (e.keyCode === e.DOM_VK_ESCAPE) {
+      e.preventDefault();
+      e.stopPropagation();
+      tilt.destroy(tilt.currentWindowId, true);
+    }
+  },
+
+  /**
    * Called when the canvas looses focus.
    */
   onBlur: function TVC_onBlur(e) {
     this.arcball.cancelKeyEvents();
   },
 
   /**
    * Called when the content window of the current browser is resized.
--- a/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
+++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-esc.js
@@ -26,12 +26,15 @@ function test() {
 }
 
 function cleanup() {
   let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
 
   is(Tilt.visualizers[id], null,
     "The current instance of the visualizer wasn't destroyed properly.");
 
+  ok(InspectorUI.highlighter && InspectorUI.breadcrumbs,
+    "The Inspector should not close while Tilt is opened.");
+
   Services.obs.removeObserver(cleanup, DESTROYED);
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2101,16 +2101,21 @@ panel[dimmed="true"] {
   padding: 0 3px;
   /* A fake 1px-shadow is included in the border-images of the
      inspector-breadcrumbs-buttons, to match toolbar-buttons style.
      This negative margin compensate the extra row of pixels created
      by the shadow.*/
   margin-bottom: -1px;
 }
 
+#inspector-breadcrumbs > .scrollbutton-up,
+#inspector-breadcrumbs > .scrollbutton-down {
+  -moz-appearance: none;
+}
+
 .inspector-breadcrumbs-button {
   -moz-appearance: none;
   background-color: transparent;
   border-style: solid;
   border-width: 1px 13px 2px 13px;
   color: hsl(210,30%,85%);
   max-width: 85px;
   /* The content of the button can be larger than the button */
--- a/browser/themes/gnomestripe/newtab/newTab.css
+++ b/browser/themes/gnomestripe/newtab/newTab.css
@@ -73,16 +73,22 @@
 }
 
 /* GRID */
 #grid {
   padding: 1px;
   margin: 0 auto;
 }
 
+/* CELLS */
+.cell {
+  outline: 1px dashed #ccc;
+  outline-offset: -1px;
+}
+
 /* SITES */
 .site {
   background-color: #ececec;
   -moz-transition: 200ms ease-out;
   -moz-transition-property: top, left, box-shadow, opacity;
 }
 
 .site[dragged] {
--- a/browser/themes/pinstripe/newtab/newTab.css
+++ b/browser/themes/pinstripe/newtab/newTab.css
@@ -73,16 +73,22 @@
 }
 
 /* GRID */
 #grid {
   padding: 1px;
   margin: 0 auto;
 }
 
+/* CELLS */
+.cell {
+  outline: 1px dashed #ccc;
+  outline-offset: -1px;
+}
+
 /* SITES */
 .site {
   background-color: #ececec;
   -moz-transition: 200ms ease-out;
   -moz-transition-property: top, left, box-shadow, opacity;
 }
 
 .site[dragged] {
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -2781,16 +2781,21 @@ panel[dimmed="true"] {
   padding: 0 6px;
   /* A fake 1px-shadow is included in the border-images of the
      inspector-breadcrumbs-buttons, to match toolbar-buttons style.
      This negative margin compensate the extra row of pixels created
      by the shadow.*/
   margin: -1px 0;
 }
 
+#inspector-breadcrumbs > .scrollbutton-up,
+#inspector-breadcrumbs > .scrollbutton-down {
+  -moz-appearance: none;
+}
+
 .inspector-breadcrumbs-button {
   -moz-appearance: none;
   background-color: transparent;
   border-style: solid;
   border-width: 2px 13px;
   outline: none;
   color: hsl(210,30%,85%);
   max-width: 85px;
--- a/browser/themes/winstripe/newtab/newTab.css
+++ b/browser/themes/winstripe/newtab/newTab.css
@@ -73,16 +73,22 @@
 }
 
 /* GRID */
 #grid {
   padding: 1px;
   margin: 0 auto;
 }
 
+/* CELLS */
+.cell {
+  outline: 1px dashed #ccc;
+  outline-offset: -1px;
+}
+
 /* SITES */
 .site {
   background-color: #ececec;
   -moz-transition: 200ms ease-out;
   -moz-transition-property: top, left, box-shadow, opacity;
 }
 
 .site[dragged] {
--- a/build/mobile/robocop/Actions.java.in
+++ b/build/mobile/robocop/Actions.java.in
@@ -55,15 +55,21 @@ public interface Actions {
   /**
    * Listens for a gecko event to be sent from the Gecko instance.
    * The returned object can be used to test if the event has been
    * received. Note that only one event is listened for.
    * 
    * @param geckoEvent The geckoEvent JSONObject's type
    */
   EventExpecter expectGeckoEvent(String geckoEvent);
+
+  /**
+   * Listens for a paint event.
+   */
+  EventExpecter expectPaint();
+
   // Send the string kewsToSend to the application 
   void sendKeys(String keysToSend);
   //Send any of the above keys to the element
   void sendSpecialKey(SpecialKey button);
 
   void drag(int startingX, int endingX, int startingY, int endingY);
 }
--- a/build/mobile/robocop/Assert.java.in
+++ b/build/mobile/robocop/Assert.java.in
@@ -47,9 +47,12 @@ public interface Assert {
   void finalize();
   void ok(boolean condition, String name, String diag);
   void is(Object a, Object b, String name);
   void isnot(Object a, Object b, String name);
   void todo(boolean condition, String name, String diag);
   void todo_is(Object a, Object b, String name);
   void todo_isnot(Object a, Object b, String name);
   void info(String name, String message);
+
+  // robocop-specific asserts
+  void ispixel(int actual, int r, int g, int b, String name);
 }
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -63,9 +63,16 @@ public interface Driver {
   int getHeight();
   int getGeckoTop();
   int getGeckoLeft();
   int getGeckoWidth();
   int getGeckoHeight();
 
   void startFrameRecording();
   int stopFrameRecording();
+
+  /**
+   * Get a copy of the painted content region.
+   * @return A 2-D array of pixels (indexed by y, then x). The pixels
+   * are in ARGB-8888 format.
+   */
+  int[][] getPaintedSurface();
 }
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -68,44 +68,53 @@ import java.util.concurrent.SynchronousQ
 import org.json.*;
 
 import com.jayway.android.robotium.solo.Solo;
 
 public class FennecNativeActions implements Actions {
   // Map of IDs to element names.
   private Solo solo;
   private Instrumentation instr;
+  private Activity geckoApp;
 
   // Objects for reflexive access of fennec classes.
   private ClassLoader classLoader;
   private Class gel;
   private Class ge;
   private Class gas;
+  private Class drawListener;
   private Method registerGEL;
   private Method unregisterGEL;
   private Method sendGE;
-
+  private Method getLayerClient;
+  private Method setDrawListener;
 
   public FennecNativeActions(Activity activity, Solo robocop, Instrumentation instrumentation){
     this.solo = robocop;
     this.instr = instrumentation;
+    this.geckoApp = activity;
     // Set up reflexive access of java classes and methods.
     try {
       classLoader = activity.getClassLoader();
       gel = classLoader.loadClass("org.mozilla.gecko.GeckoEventListener");
       ge = classLoader.loadClass("org.mozilla.gecko.GeckoEvent");
       gas = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell");
       Class [] parameters = new Class[2];
       parameters[0] = String.class;
       parameters[1] = gel;
       registerGEL = gas.getMethod("registerGeckoEventListener", parameters);
       unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
       parameters = new Class[1];
       parameters[0] = ge;
       sendGE = gas.getMethod("sendEventToGecko", parameters);
+
+      getLayerClient = activity.getClass().getMethod("getSoftwareLayerClient");
+      Class gslc = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient");
+      drawListener = classLoader.loadClass("org.mozilla.gecko.gfx.GeckoSoftwareLayerClient$DrawListener");
+      setDrawListener = gslc.getDeclaredMethod("setDrawListener", drawListener);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (SecurityException e) {
        e.printStackTrace();
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
@@ -200,16 +209,85 @@ public class FennecNativeActions impleme
     } catch (IllegalAccessException e) {
       e.printStackTrace();
     } catch (InvocationTargetException e) {
       e.printStackTrace();
     }
     return null;
   }
 
+  class DrawListenerProxy implements InvocationHandler {
+    private final PaintExpecter mPaintExpecter;
+
+    DrawListenerProxy(PaintExpecter paintExpecter) {
+      mPaintExpecter = paintExpecter;
+    }
+
+    public Object invoke(Object proxy, Method method, Object[] args) {
+      String methodName = method.getName();
+      if ("drawFinished".equals(methodName)) {
+        Log.i("Robocop", "Received drawFinished notification");
+        mPaintExpecter.notifyOfEvent();
+      } else if ("toString".equals(methodName)) {
+        return "DrawListenerProxy";
+      } else if ("equals".equals(methodName)) {
+        return false;
+      } else if ("hashCode".equals(methodName)) {
+        return 0;
+      }
+      return null;
+    }
+  }
+
+  class PaintExpecter implements EventExpecter {
+    private Object mLayerClient;
+    private boolean mPaintDone;
+
+    PaintExpecter() throws IllegalAccessException, InvocationTargetException {
+      mLayerClient = getLayerClient.invoke(geckoApp);
+      setDrawListener.invoke(mLayerClient, Proxy.newProxyInstance(classLoader, new Class[] { drawListener }, new DrawListenerProxy(this)));
+    }
+
+    void notifyOfEvent() {
+      try {
+        setDrawListener.invoke(mLayerClient, (Object)null);
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+      synchronized (this) {
+        mPaintDone = true;
+        this.notifyAll();
+      }
+    }
+
+    public synchronized void blockForEvent() {
+      while (! mPaintDone) {
+        try {
+          this.wait();
+        } catch (InterruptedException ie) {
+          ie.printStackTrace();
+          break;
+        }
+      }
+    }
+
+    public synchronized boolean eventReceived() {
+      return mPaintDone;
+    }
+  }
+
+  public EventExpecter expectPaint() {
+    try {
+      return new PaintExpecter();
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
   public void sendSpecialKey(SpecialKey button) {
     switch( button) {
       case DOWN:
         instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_DOWN);
         break;
       case UP:
         instr.sendCharacterSync(KeyEvent.KEYCODE_DPAD_UP);
         break;
--- a/build/mobile/robocop/FennecNativeAssert.java.in
+++ b/build/mobile/robocop/FennecNativeAssert.java.in
@@ -238,16 +238,33 @@ public class FennecNativeAssert implemen
     boolean pass = !a.equals(b);
     String diag = "didn't expect " + a.toString() + ", but got it";
     if(pass) {
       diag = a.toString() + " should not equal " + b.toString();
     }
     ok(pass, name, diag);
   }
 
+  public void ispixel(int actual, int r, int g, int b, String name) {
+    // When we read GL pixels the GPU has already processed them and they
+    // are usually off by a little bit. For example a CSS-color pixel of color #64FFF5
+    // was turned into #63FFF7 when it came out of glReadPixels. So in order to compare
+    // against the expected value, we use a little fuzz factor. For the alpha we just
+    // make sure it is always 0xFF.
+    int aAlpha = ((actual >> 24) & 0xFF);
+    int aR = ((actual >> 16) & 0xFF);
+    int aG = ((actual >> 8) & 0xFF);
+    int aB = (actual & 0xFF);
+    boolean pass = (aAlpha == 0xFF) /* alpha */
+                && (Math.abs(aR - r) < 8) /* red */
+                && (Math.abs(aG - g) < 8) /* green */
+                && (Math.abs(aB - b) < 8); /* blue */
+    ok(pass, name, "Color rgba(" + aR + "," + aG + "," + aB + "," + aAlpha + ")" + (pass ? " " : " not") + " close enough to expected rgb(" + r + "," + g + "," + b + ")");
+  }
+
   public void todo(boolean condition, String name, String diag) {
     testInfo test = new testInfo(condition, name, diag, true);
     _logMochitestResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
     testList.add(test);
   }
 
   public void todo_is(Object a, Object b, String name) {
     boolean pass = a.equals(b);
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -40,29 +40,31 @@
 package @ANDROID_PACKAGE_NAME@;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.IntBuffer;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.HashMap;
 import java.util.List;
 
 import java.lang.Class;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.Long;
 
 import android.app.Activity;
+import android.opengl.GLSurfaceView;
 import android.util.Log;
 import android.view.View;
 
 import org.json.*;
 
 import com.jayway.android.robotium.solo.Solo;
 
 public class FennecNativeDriver implements Driver {
@@ -76,16 +78,17 @@ public class FennecNativeDriver implemen
   private Class gel;
   private Class ge;
   private Class gas;
   private Method registerGEL;
   private Method unregisterGEL;
   private Method sendGE;
   private Method _startFrameRecording;
   private Method _stopFrameRecording;
+  private Method _getPixels;
 
   public FennecNativeDriver(Activity activity, Solo robocop){
     this.activity = activity;
     this.solo = robocop;
 
     // Set up table of fennec_ids.
     locators = convertTextToTable(getFile("/mnt/sdcard/fennec_ids.txt"));
 
@@ -102,16 +105,19 @@ public class FennecNativeDriver implemen
       unregisterGEL = gas.getMethod("unregisterGeckoEventListener", parameters);
       parameters = new Class[1];
       parameters[0] = ge;
       sendGE = gas.getMethod("sendEventToGecko", parameters);
 
       Class gfx = classLoader.loadClass("org.mozilla.gecko.gfx.PanningPerfAPI");
       _startFrameRecording = gfx.getDeclaredMethod("startFrameTimeRecording");
       _stopFrameRecording = gfx.getDeclaredMethod("stopFrameTimeRecording");
+
+      Class layerView = classLoader.loadClass("org.mozilla.gecko.gfx.LayerView");
+      _getPixels = layerView.getDeclaredMethod("getPixels");
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (SecurityException e) {
        e.printStackTrace();
      } catch (NoSuchMethodException e) {
        e.printStackTrace();
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
@@ -207,16 +213,53 @@ public class FennecNativeDriver implemen
       e.printStackTrace();
     } catch (InvocationTargetException e) {
       e.printStackTrace();
     }
 
     return 0;
   }
 
+  private GLSurfaceView getSurfaceView() {
+    for (View v : solo.getCurrentViews()) {
+      if (v instanceof GLSurfaceView) {
+        return (GLSurfaceView)v;
+      }
+    }
+    return null;
+  }
+
+  public int[][] getPaintedSurface() {
+    GLSurfaceView view = getSurfaceView();
+    if (view == null) {
+      return null;
+    }
+    IntBuffer pixelBuffer;
+    try {
+      pixelBuffer = (IntBuffer)_getPixels.invoke(view);
+    } catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+
+    // now we need to (1) flip the image, because GL likes to do things up-side-down,
+    // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
+    int w = view.getWidth();
+    int h = view.getHeight();
+    pixelBuffer.position(0);
+    int[][] pixels = new int[h][w];
+    for (int y = h - 1; y >= 0; y--) {
+      for (int x = 0; x < w; x++) {
+        int agbr = pixelBuffer.get();
+        pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+      }
+    }
+    return pixels;
+  }
+
   class scrollHandler implements InvocationHandler {
     public scrollHandler(){};
     public Object invoke(Object proxy, Method method, Object[] args) {
       try{
         //Disect the JSON object into the appropriate variables 
         JSONObject jo = ((JSONObject)args[1]);
         scrollHeight = jo.getInt("y");
         height = jo.getInt("cheight");
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -63,16 +63,17 @@ JAVAFILES = \
   $(NULL)
 
 _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in))
 
 _TEST_FILES = \
   $(TESTPATH)/robocop_blank_01.html \
   $(TESTPATH)/robocop_blank_02.html \
   $(TESTPATH)/robocop_blank_03.html \
+  $(TESTPATH)/robocop_boxes.html \
   $(NULL)
 
 _ROBOCOP_TOOLS = \
   $(TESTPATH)/robocop.ini \
   parse_ids.py \
   $(NULL)
 
 GARBAGE += \
--- a/build/unix/build-toolchain/glibc-deterministic.patch
+++ b/build/unix/build-toolchain/glibc-deterministic.patch
@@ -17,16 +17,32 @@ diff -ru a/csu/Makefile b/csu/Makefile
  		     os=Linux; \
  		   fi; \
 -		   printf '"Compiled on a %s %s system on %s.\\n"\n' \
 -			  "$$os" "$$version" "`date +%Y-%m-%d`";; \
 +                   ;; \
  	   *) ;; \
  	 esac; \
  	 files="$(all-Banner-files)";				\
+diff -ru a/Makerules b/Makerules
+--- a/Makerules	2011-01-17 23:34:07.000000000 -0500
++++ b/Makerules	2012-01-30 08:47:56.565068903 -0500
+@@ -992,9 +992,9 @@
+ 	 echo '   Use the shared library, but some functions are only in';\
+ 	 echo '   the static library, so try that secondarily.  */';\
+ 	 cat $<; \
+-	 echo 'GROUP ( $(slibdir)/libc.so$(libc.so-version)' \
+-	      '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\
+-	      ' AS_NEEDED (' $(slibdir)/$(rtld-installed-name) ') )' \
++	 echo 'GROUP ( libc.so$(libc.so-version)' \
++	      '$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\
++	      ' AS_NEEDED (' $(rtld-installed-name) ') )' \
+ 	) > $@.new
+ 	mv -f $@.new $@
+ 
 diff -ru a/nscd/nscd_stat.c b/nscd/nscd_stat.c
 --- a/nscd/nscd_stat.c	2011-01-17 23:34:07.000000000 -0500
 +++ b/nscd/nscd_stat.c	2012-01-23 15:54:45.231607606 -0500
 @@ -38,7 +38,7 @@
  
  
  /* We use this to make sure the receiver is the same.  */
 -static const char compilation[21] = __DATE__ " " __TIME__;
--- a/config/android-common.mk
+++ b/config/android-common.mk
@@ -65,11 +65,11 @@ ifndef JAVA_VERSION
 endif
 
 JAVAC_FLAGS = \
   -target $(JAVA_VERSION) \
   -source $(JAVA_VERSION) \
   -classpath $(JAVA_CLASSPATH) \
   -bootclasspath $(JAVA_BOOTCLASSPATH) \
   -encoding UTF8 \
-  -g \
+  -g:source,lines \
   -Werror \
   $(NULL)
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.pl.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-12.0a1
+13.0a1
--- a/configure.in
+++ b/configure.in
@@ -4901,22 +4901,24 @@ cairo-android)
     if test "$MOZ_BUILD_APP" = "mobile/xul"; then
         MOZ_OLD_LINKER=1
     fi
     MOZ_TOUCH=1
     ;;
 
 cairo-gonk)
     AC_DEFINE(MOZ_WIDGET_GONK)
+    AC_DEFINE(MOZ_TOUCH)
     MOZ_WIDGET_TOOLKIT=gonk
     TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)'
     TK_LIBS='$(MOZ_CAIRO_LIBS)'
     MOZ_WEBGL=1
     MOZ_PDF_PRINTING=1
     MOZ_B2G_RIL=1
+    MOZ_TOUCH=1
     ;;
 
 esac
 
 AC_SUBST(MOZ_OLD_LINKER)
 AC_SUBST(MOZ_PDF_PRINTING)
 if test "$MOZ_PDF_PRINTING"; then
    PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1"
@@ -5664,41 +5666,41 @@ if test -n "$MOZ_WEBM"; then
             [if test ! -f "${LIBVPX_DIR}/include/vpx/vpx_decoder.h"; then
              AC_MSG_ERROR([vpx/vpx_decoder.h found, but is not in ${LIBVPX_DIR}/include])
             fi],
             AC_MSG_ERROR([--with-system-libvpx requested but vpx/vpx_decoder.h not found]))
         AC_CHECK_LIB(vpx, vpx_codec_dec_init_ver,
                      [MOZ_NATIVE_LIBVPX_DEC_TEST=1],
                      ([--with-system-libvpx requested but symbol vpx_codec_dec_init_ver not found]))
         if test -n "$MOZ_NATIVE_LIBVPX_DEC_TEST" ; then
-            AC_MSG_CHECKING([for libvpx version >= v0.9.7])
-            dnl We need at least v0.9.7 to fix several crash bugs (for which we
-            dnl had local patches prior to v0.9.7).
+            AC_MSG_CHECKING([for libvpx version >= v1.0.0])
+            dnl We need at least v1.0.0 to fix several crash bugs (for which we
+            dnl had local patches prior to v1.0.0).
             dnl
             dnl This is a terrible test for the library version, but we don't
             dnl have a good one. There is no version number in a public header,
             dnl and testing the headers still doesn't guarantee we link against
             dnl the right version. While we could call vpx_codec_version() at
             dnl run-time, that would break cross-compiling. There are no
-            dnl additional exported symbols between the v0.9.7 release and the
-            dnl v0.9.6 one to check for.
+            dnl additional exported decoder symbols between the v1.0.0 release
+            dnl and the v0.9.7 one to check for.
             AC_TRY_COMPILE([
                 #include <vpx/vpx_decoder.h>
-                #if !defined(VPX_CODEC_USE_INPUT_PARTITION)
+                #if !defined(VPX_CODEC_USE_INPUT_FRAGMENTS)
                     #error "test failed."
                 #endif
                 ],
                 [return 0;],
                 [AC_MSG_RESULT([yes])
                  MOZ_NATIVE_LIBVPX=1
                  AC_DEFINE(MOZ_NATIVE_LIBVPX)
                  MOZ_LIBVPX_INCLUDES="-I${LIBVPX_DIR}/include"
                  MOZ_LIBVPX_LIBS="-L${LIBVPX_DIR}/lib -lvpx"],
                 [AC_MSG_RESULT([no])
-                 AC_MSG_ERROR([--with-system-libvpx requested but it is not v0.9.7 or later])])
+                 AC_MSG_ERROR([--with-system-libvpx requested but it is not v1.0.0 or later])])
         fi
         CFLAGS=$_SAVE_CFLAGS
         LDFLAGS=$_SAVE_LDFLAGS
         LIBS=$_SAVE_LIBS
     fi
 fi
 
 AC_SUBST(MOZ_NATIVE_LIBVPX)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -173,16 +173,58 @@ enum EventNameType {
   EventNameType_SVGGraphic = 0x0004, // svg graphic elements
   EventNameType_SVGSVG = 0x0008, // the svg element
   EventNameType_SMIL = 0x0016, // smil elements
 
   EventNameType_HTMLXUL = 0x0003,
   EventNameType_All = 0xFFFF
 };
 
+/**
+ * Information retrieved from the <meta name="viewport"> tag. See
+ * GetViewportInfo for more information on this functionality.
+ */
+struct ViewportInfo
+{
+    // Default zoom indicates the level at which the display is 'zoomed in'
+    // initially for the user, upon loading of the page.
+    double defaultZoom;
+
+    // The minimum zoom level permitted by the page.
+    double minZoom;
+
+    // The maximum zoom level permitted by the page.
+    double maxZoom;
+
+    // The width of the viewport, specified by the <meta name="viewport"> tag,
+    // in CSS pixels.
+    PRUint32 width;
+
+    // The height of the viewport, specified by the <meta name="viewport"> tag,
+    // in CSS pixels.
+    PRUint32 height;
+
+    // Whether or not we should automatically size the viewport to the device's
+    // width. This is true if the document has been optimized for mobile, and
+    // the width property of a specified <meta name="viewport"> tag is either
+    // not specified, or is set to the special value 'device-width'.
+    bool autoSize;
+
+    // Whether or not the user can zoom in and out on the page. Default is true.
+    bool allowZoom;
+
+    // This is a holdover from e10s fennec, and might be removed in the future.
+    // It's a hack to work around bugs that didn't allow zooming of documents
+    // from within the parent process. It is still used in native Fennec for XUL
+    // documents, but it should probably be removed.
+    // Currently, from, within GetViewportInfo(), This is only set to false
+    // if the document is a XUL document.
+    bool autoScale;
+};
+
 struct EventNameMapping
 {
   nsIAtom* mAtom;
   PRUint32 mId;
   PRInt32  mType;
   PRUint32 mStructType;
 };
 
@@ -1484,16 +1526,28 @@ public:
    *
    * The only known case where this lies is mutation events. They run, and can
    * run anything else, when this function returns false, but this is ok.
    */
   static bool IsSafeToRunScript() {
     return sScriptBlockerCount == 0;
   }
 
+  /**
+   * Retrieve information about the viewport as a data structure.
+   * This will return information in the viewport META data section
+   * of the document. This can be used in lieu of ProcessViewportInfo(),
+   * which places the viewport information in the document header instead
+   * of returning it directly.
+   *
+   * NOTE: If the site is optimized for mobile (via the doctype), this
+   * will return viewport information that specifies default information.
+   */
+  static ViewportInfo GetViewportInfo(nsIDocument* aDocument);
+
   /* Process viewport META data. This gives us information for the scale
    * and zoom of a page on mobile devices. We stick the information in
    * the document header and use it later on after rendering.
    *
    * See Bug #436083
    */
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -73,18 +73,18 @@ enum nsLinkState {
   eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
   eLinkState_Visited    = 2,
   eLinkState_NotLink    = 3
 };
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID \
-{ 0xdc68f070, 0x226d, 0x11e1, \
- { 0xbf, 0xc2, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
+{ 0x94671671, 0x9e1b, 0x447a, \
+  { 0xad, 0xb7, 0xc3, 0x2e, 0x05, 0x6a, 0x96, 0xc9 } }
 
 /**
  * A node of content in a document's content model. This interface
  * is supported by all content objects.
  */
 class nsIContent : public nsINode {
 public:
   typedef mozilla::widget::IMEState IMEState;
@@ -943,16 +943,19 @@ public:
     }
   }
 
   // Overloaded from nsINode
   virtual already_AddRefed<nsIURI> GetBaseURI() const;
 
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
 
+  virtual bool IsPurple() = 0;
+  virtual void RemovePurple() = 0;
+
 protected:
   /**
    * Hook for implementing GetID.  This is guaranteed to only be
    * called if HasID() is true.
    */
   virtual nsIAtom* DoGetID() const = 0;
 
 private:
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -283,18 +283,18 @@ private:
 // Categories of node properties
 // 0 is global.
 #define DOM_USER_DATA         1
 #define DOM_USER_DATA_HANDLER 2
 #define SMIL_MAPPED_ATTR_ANIMVAL 3
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
-{ 0xd026d280, 0x5b25, 0x41c0, \
-  { 0x92, 0xcf, 0x6, 0xf6, 0xf, 0xb, 0x9a, 0xfe } }
+{ 0xfcd3b0d1, 0x75db, 0x46c4, \
+  { 0xa1, 0xf5, 0x07, 0xc2, 0x09, 0xf8, 0x1f, 0x44 } }
 
 /**
  * An internal interface that abstracts some DOMNode-related parts that both
  * nsIContent and nsIDocument share.  An instance of this interface has a list
  * of nsIContent children and provides access to them.
  */
 class nsINode : public nsIDOMEventTarget,
                 public nsWrapperCache
@@ -1218,16 +1218,23 @@ private:
     ElementHasName,
     // Set if the element might have a contenteditable attribute set.
     ElementMayHaveContentEditableAttr,
     // Set if the node is the common ancestor of the start/end nodes of a Range
     // that is in a Selection.
     NodeIsCommonAncestorForRangeInSelection,
     // Set if the node is a descendant of a node with the above bit set.
     NodeIsDescendantOfCommonAncestorForRangeInSelection,
+    // Set if CanSkipInCC check has been done for this subtree root.
+    NodeIsCCMarkedRoot,
+    // Maybe set if this node is in black subtree.
+    NodeIsCCBlackTree,
+    // Maybe set if the node is a root of a subtree 
+    // which needs to be kept in the purple buffer.
+    NodeIsPurpleRoot,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
   }
@@ -1265,16 +1272,26 @@ public:
     { ClearBoolFlag(NodeIsCommonAncestorForRangeInSelection); }
   bool IsDescendantOfCommonAncestorForRangeInSelection() const
     { return GetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
   void SetDescendantOfCommonAncestorForRangeInSelection()
     { SetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
   void ClearDescendantOfCommonAncestorForRangeInSelection()
     { ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
 
+  void SetCCMarkedRoot(bool aValue)
+    { SetBoolFlag(NodeIsCCMarkedRoot, aValue); }
+  bool CCMarkedRoot() const { return GetBoolFlag(NodeIsCCMarkedRoot); }
+  void SetInCCBlackTree(bool aValue)
+    { SetBoolFlag(NodeIsCCBlackTree, aValue); }
+  bool InCCBlackTree() const { return GetBoolFlag(NodeIsCCBlackTree); }
+  void SetIsPurpleRoot(bool aValue)
+    { SetBoolFlag(NodeIsPurpleRoot, aValue); }
+  bool IsPurpleRoot() const { return GetBoolFlag(NodeIsPurpleRoot); }
+
 protected:
   void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
   void SetInDocument() { SetBoolFlag(IsInDocument); }
   void ClearInDocument() { ClearBoolFlag(IsInDocument); }
   void SetIsElement() { SetBoolFlag(NodeIsElement); }
   void ClearIsElement() { ClearBoolFlag(NodeIsElement); }
   void SetHasID() { SetBoolFlag(ElementHasID); }
   void ClearHasID() { ClearBoolFlag(ElementHasID); }
--- a/content/base/src/nsCCUncollectableMarker.cpp
+++ b/content/base/src/nsCCUncollectableMarker.cpp
@@ -49,16 +49,23 @@
 #include "nsISHistory.h"
 #include "nsISHEntry.h"
 #include "nsISHContainer.h"
 #include "nsIWindowWatcher.h"
 #include "mozilla/Services.h"
 #include "nsIXULWindow.h"
 #include "nsIAppShellService.h"
 #include "nsAppShellCID.h"
+#include "nsEventListenerManager.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsJSEnvironment.h"
+#include "nsInProcessTabChildGlobal.h"
+#include "nsFrameLoader.h"
+#include "nsGenericElement.h"
 
 static bool sInited = 0;
 PRUint32 nsCCUncollectableMarker::sGeneration = 0;
 #ifdef MOZ_XUL
 #include "nsXULPrototypeCache.h"
 #endif
 
 NS_IMPL_ISUPPORTS1(nsCCUncollectableMarker, nsIObserver)
@@ -82,114 +89,208 @@ nsCCUncollectableMarker::Init()
   nsresult rv;
 
   // This makes the observer service hold an owning reference to the marker
   rv = obs->AddObserver(marker, "xpcom-shutdown", false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = obs->AddObserver(marker, "cycle-collector-begin", false);
   NS_ENSURE_SUCCESS(rv, rv);
+  rv = obs->AddObserver(marker, "cycle-collector-forget-skippable", false);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   sInited = true;
 
   return NS_OK;
 }
 
+static void
+MarkUserData(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
+{
+  nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
+  if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
+    nsGenericElement::MarkUserData(aNode, aKey, aValue, aData);
+  }
+}
+
+static void
+MarkUserDataHandler(void* aNode, nsIAtom* aKey, void* aValue, void* aData)
+{
+  nsIDocument* d = static_cast<nsINode*>(aNode)->GetCurrentDoc();
+  if (d && nsCCUncollectableMarker::InGeneration(d->GetMarkedCCGeneration())) {
+    nsGenericElement::MarkUserDataHandler(aNode, aKey, aValue, aData);
+  }
+}
+
+static void
+MarkMessageManagers()
+{
+  nsCOMPtr<nsIChromeFrameMessageManager> globalMM =
+    do_GetService("@mozilla.org/globalmessagemanager;1");
+  if (!globalMM) {
+    return;
+  }
+
+  globalMM->MarkForCC();
+  PRUint32 childCount = 0;
+  globalMM->GetChildCount(&childCount);
+  for (PRUint32 i = 0; i < childCount; ++i) {
+    nsCOMPtr<nsITreeItemFrameMessageManager> windowMM;
+    globalMM->GetChildAt(i, getter_AddRefs(windowMM));
+    if (!windowMM) {
+      continue;
+    }
+    windowMM->MarkForCC();
+    PRUint32 tabChildCount = 0;
+    windowMM->GetChildCount(&tabChildCount);
+    for (PRUint32 j = 0; j < tabChildCount; ++j) {
+      nsCOMPtr<nsITreeItemFrameMessageManager> tabMM;
+      windowMM->GetChildAt(j, getter_AddRefs(tabMM));
+      if (!tabMM) {
+        continue;
+      }
+      tabMM->MarkForCC();
+      //XXX hack warning, but works, since we know that
+      //    callback data is frameloader.
+      void* cb = static_cast<nsFrameMessageManager*>(tabMM.get())->
+        GetCallbackData();
+      nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
+      if (fl) {
+        nsIDOMEventTarget* et = fl->GetTabChildGlobalAsEventTarget();
+        if (!et) {
+          continue;
+        }
+        static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
+        nsEventListenerManager* elm = et->GetListenerManager(false);
+        if (elm) {
+          elm->UnmarkGrayJSListeners();
+        }
+      }
+    }
+  }
+}
+
 void
-MarkContentViewer(nsIContentViewer* aViewer)
+MarkContentViewer(nsIContentViewer* aViewer, bool aCleanupJS,
+                  bool aPrepareForCC)
 {
   if (!aViewer) {
     return;
   }
 
   nsIDocument *doc = aViewer->GetDocument();
-  if (doc) {
+  if (doc &&
+      doc->GetMarkedCCGeneration() != nsCCUncollectableMarker::sGeneration) {
     doc->MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
+    if (aCleanupJS) {
+      nsEventListenerManager* elm = doc->GetListenerManager(false);
+      if (elm) {
+        elm->UnmarkGrayJSListeners();
+      }
+      nsCOMPtr<nsIDOMEventTarget> win = do_QueryInterface(doc->GetInnerWindow());
+      if (win) {
+        elm = win->GetListenerManager(false);
+        if (elm) {
+          elm->UnmarkGrayJSListeners();
+        }
+        static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
+      }
+
+      doc->PropertyTable(DOM_USER_DATA_HANDLER)->
+        EnumerateAll(MarkUserDataHandler, &nsCCUncollectableMarker::sGeneration);
+    } else if (aPrepareForCC) {
+      // Unfortunately we need to still mark user data just before running CC so
+      // that it has the right generation. 
+      doc->PropertyTable(DOM_USER_DATA)->
+        EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
+    }
   }
 }
 
-void MarkDocShell(nsIDocShellTreeNode* aNode);
+void MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS,
+                  bool aPrepareForCC);
 
 void
-MarkSHEntry(nsISHEntry* aSHEntry)
+MarkSHEntry(nsISHEntry* aSHEntry, bool aCleanupJS, bool aPrepareForCC)
 {
   if (!aSHEntry) {
     return;
   }
 
   nsCOMPtr<nsIContentViewer> cview;
   aSHEntry->GetContentViewer(getter_AddRefs(cview));
-  MarkContentViewer(cview);
+  MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
 
   nsCOMPtr<nsIDocShellTreeItem> child;
   PRInt32 i = 0;
   while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, getter_AddRefs(child))) &&
          child) {
-    MarkDocShell(child);
+    MarkDocShell(child, aCleanupJS, aPrepareForCC);
   }
 
   nsCOMPtr<nsISHContainer> shCont = do_QueryInterface(aSHEntry);
   PRInt32 count;
   shCont->GetChildCount(&count);
   for (i = 0; i < count; ++i) {
     nsCOMPtr<nsISHEntry> childEntry;
     shCont->GetChildAt(i, getter_AddRefs(childEntry));
-    MarkSHEntry(childEntry);
+    MarkSHEntry(childEntry, aCleanupJS, aPrepareForCC);
   }
   
 }
 
 void
-MarkDocShell(nsIDocShellTreeNode* aNode)
+MarkDocShell(nsIDocShellTreeNode* aNode, bool aCleanupJS, bool aPrepareForCC)
 {
   nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aNode);
   if (!shell) {
     return;
   }
 
   nsCOMPtr<nsIContentViewer> cview;
   shell->GetContentViewer(getter_AddRefs(cview));
-  MarkContentViewer(cview);
+  MarkContentViewer(cview, aCleanupJS, aPrepareForCC);
 
   nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(shell);
   nsCOMPtr<nsISHistory> history;
   webNav->GetSessionHistory(getter_AddRefs(history));
   if (history) {
     PRInt32 i, historyCount;
     history->GetCount(&historyCount);
     for (i = 0; i < historyCount; ++i) {
       nsCOMPtr<nsIHistoryEntry> historyEntry;
       history->GetEntryAtIndex(i, false, getter_AddRefs(historyEntry));
       nsCOMPtr<nsISHEntry> shEntry = do_QueryInterface(historyEntry);
 
-      MarkSHEntry(shEntry);
+      MarkSHEntry(shEntry, aCleanupJS, aPrepareForCC);
     }
   }
 
   PRInt32 i, childCount;
   aNode->GetChildCount(&childCount);
   for (i = 0; i < childCount; ++i) {
     nsCOMPtr<nsIDocShellTreeItem> child;
     aNode->GetChildAt(i, getter_AddRefs(child));
-    MarkDocShell(child);
+    MarkDocShell(child, aCleanupJS, aPrepareForCC);
   }
 }
 
 void
-MarkWindowList(nsISimpleEnumerator* aWindowList)
+MarkWindowList(nsISimpleEnumerator* aWindowList, bool aCleanupJS,
+               bool aPrepareForCC)
 {
   nsCOMPtr<nsISupports> iter;
   while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) &&
          iter) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(iter);
     if (window) {
       nsCOMPtr<nsIDocShellTreeNode> rootDocShell =
         do_QueryInterface(window->GetDocShell());
 
-      MarkDocShell(rootDocShell);
+      MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
     }
   }
 }
 
 nsresult
 nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
                                  const PRUnichar* aData)
 {
@@ -197,64 +298,79 @@ nsCCUncollectableMarker::Observe(nsISupp
     nsCOMPtr<nsIObserverService> obs =
       mozilla::services::GetObserverService();
     if (!obs)
       return NS_ERROR_FAILURE;
 
     // No need for kungFuDeathGrip here, yay observerservice!
     obs->RemoveObserver(this, "xpcom-shutdown");
     obs->RemoveObserver(this, "cycle-collector-begin");
+    obs->RemoveObserver(this, "cycle-collector-forget-skippable");
     
     sGeneration = 0;
     
     return NS_OK;
   }
 
-  NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin"), "wrong topic");
+  NS_ASSERTION(!strcmp(aTopic, "cycle-collector-begin") ||
+               !strcmp(aTopic, "cycle-collector-forget-skippable"), "wrong topic");
+
+  // JS cleanup can be slow. Do it only if there has been a GC.
+  bool cleanupJS =
+    !nsJSContext::CleanupSinceLastGC() &&
+    !strcmp(aTopic, "cycle-collector-forget-skippable");
+
+  bool prepareForCC = !strcmp(aTopic, "cycle-collector-begin");
+    
 
   // Increase generation to effectivly unmark all current objects
   if (!++sGeneration) {
     ++sGeneration;
   }
 
   nsresult rv;
 
   // Iterate all toplevel windows
   nsCOMPtr<nsISimpleEnumerator> windowList;
   nsCOMPtr<nsIWindowMediator> med =
     do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   if (med) {
     rv = med->GetEnumerator(nsnull, getter_AddRefs(windowList));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    MarkWindowList(windowList);
+    MarkWindowList(windowList, cleanupJS, prepareForCC);
   }
 
   nsCOMPtr<nsIWindowWatcher> ww =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID);
   if (ww) {
     rv = ww->GetWindowEnumerator(getter_AddRefs(windowList));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    MarkWindowList(windowList);
+    MarkWindowList(windowList, cleanupJS, prepareForCC);
   }
 
   nsCOMPtr<nsIAppShellService> appShell = 
     do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
   if (appShell) {
     nsCOMPtr<nsIXULWindow> hw;
     appShell->GetHiddenWindow(getter_AddRefs(hw));
     if (hw) {
       nsCOMPtr<nsIDocShell> shell;
       hw->GetDocShell(getter_AddRefs(shell));
       nsCOMPtr<nsIDocShellTreeNode> shellTreeNode = do_QueryInterface(shell);
-      MarkDocShell(shellTreeNode);
+      MarkDocShell(shellTreeNode, cleanupJS, prepareForCC);
     }
   }
 
+  if (cleanupJS) {
+    nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration);
+    MarkMessageManagers();
+  }
+
 #ifdef MOZ_XUL
   nsXULPrototypeCache* xulCache = nsXULPrototypeCache::GetInstance();
   if (xulCache) {
     xulCache->MarkInCCGeneration(sGeneration);
   }
 #endif
 
   return NS_OK;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -206,26 +206,39 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIScriptElement.h"
 #include "nsIContentViewer.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Preferences.h"
 
 #include "nsWrapperCacheInlines.h"
+#include "nsIDOMDocumentType.h"
+#include "nsIDOMWindowUtils.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsUnicharUtils.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla;
 
 const char kLoadAsData[] = "loadAsData";
 
+/**
+ * Default values for the ViewportInfo structure.
+ */
+static const float    kViewportMinScale = 0.0;
+static const float    kViewportMaxScale = 10.0;
+static const PRUint32 kViewportMinWidth = 200;
+static const PRUint32 kViewportMaxWidth = 10000;
+static const PRUint32 kViewportMinHeight = 223;
+static const PRUint32 kViewportMaxHeight = 10000;
+static const PRInt32  kViewportDefaultScreenWidth = 980;
+
 static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 nsIDOMScriptObjectFactory *nsContentUtils::sDOMScriptObjectFactory = nsnull;
 nsIXPConnect *nsContentUtils::sXPConnect;
 nsIScriptSecurityManager *nsContentUtils::sSecurityManager;
 nsIThreadJSContextStack *nsContentUtils::sThreadJSContextStack;
@@ -416,16 +429,18 @@ nsContentUtils::Init()
 
   Preferences::AddBoolVarCache(&sFullScreenKeyInputRestricted,
                                "full-screen-api.key-input-restricted");
 
   Preferences::AddUintVarCache(&sHandlingInputTimeout,
                                "dom.event.handling-user-input-time-limit",
                                1000);
 
+  nsGenericElement::InitCCCallbacks();
+
   sInitialized = true;
 
   return NS_OK;
 }
 
 void
 nsContentUtils::GetShiftText(nsAString& text)
 {
@@ -4550,16 +4565,208 @@ static void ProcessViewportToken(nsIDocu
   else if (key_atom == nsGkAtoms::user_scalable)
     aDocument->SetHeaderData(nsGkAtoms::viewport_user_scalable, value);
 }
 
 #define IS_SEPARATOR(c) ((c == '=') || (c == ',') || (c == ';') || \
                          (c == '\t') || (c == '\n') || (c == '\r'))
 
 /* static */
+ViewportInfo
+nsContentUtils::GetViewportInfo(nsIDocument *aDocument)
+{
+  ViewportInfo ret;
+  ret.defaultZoom = 1.0;
+  ret.autoSize = true;
+  ret.allowZoom = true;
+  ret.autoScale = true;
+
+  // If the docType specifies that we are on a site optimized for mobile,
+  // then we want to return specially crafted defaults for the viewport info.
+  nsCOMPtr<nsIDOMDocument>
+    domDoc(do_QueryInterface(aDocument));
+
+  nsCOMPtr<nsIDOMDocumentType> docType;
+  nsresult rv = domDoc->GetDoctype(getter_AddRefs(docType));
+  if (NS_SUCCEEDED(rv) && docType) {
+    nsAutoString docId;
+    rv = docType->GetPublicId(docId);
+    if (NS_SUCCEEDED(rv)) {
+      if ((docId.Find("WAP") != -1) ||
+          (docId.Find("Mobile") != -1) ||
+          (docId.Find("WML") != -1))
+      {
+        return ret;
+      }
+    }
+  }
+
+  if (aDocument->IsXUL()) {
+    ret.autoScale = false;
+    return ret;
+  }
+
+  nsIDOMWindow* window = aDocument->GetWindow();
+  nsCOMPtr<nsIDOMWindowUtils> windowUtils(do_GetInterface(window));
+
+  if (!windowUtils) {
+    return ret;
+  }
+
+  nsAutoString handheldFriendly;
+  aDocument->GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
+
+  if (handheldFriendly.EqualsLiteral("true")) {
+    return ret;
+  }
+
+  PRInt32 errorCode;
+
+  nsAutoString minScaleStr;
+  aDocument->GetHeaderData(nsGkAtoms::minimum_scale, minScaleStr);
+
+  float scaleMinFloat = minScaleStr.ToFloat(&errorCode);
+
+  if (errorCode) {
+    scaleMinFloat = kViewportMinScale;
+  }
+
+  scaleMinFloat = NS_MIN(scaleMinFloat, kViewportMaxScale);
+  scaleMinFloat = NS_MAX(scaleMinFloat, kViewportMinScale);
+
+  nsAutoString maxScaleStr;
+  aDocument->GetHeaderData(nsGkAtoms::maximum_scale, maxScaleStr);
+
+  // We define a special error code variable for the scale and max scale,
+  // because they are used later (see the width calculations).
+  PRInt32 scaleMaxErrorCode;
+  float scaleMaxFloat = maxScaleStr.ToFloat(&scaleMaxErrorCode);
+
+  if (scaleMaxErrorCode) {
+    scaleMaxFloat = kViewportMaxScale;
+  }
+
+  scaleMaxFloat = NS_MIN(scaleMaxFloat, kViewportMaxScale);
+  scaleMaxFloat = NS_MAX(scaleMaxFloat, kViewportMinScale);
+
+  nsAutoString scaleStr;
+  aDocument->GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
+
+  PRInt32 scaleErrorCode;
+  float scaleFloat = scaleStr.ToFloat(&scaleErrorCode);
+  scaleFloat = NS_MIN(scaleFloat, scaleMaxFloat);
+  scaleFloat = NS_MAX(scaleFloat, scaleMinFloat);
+
+  nsAutoString widthStr, heightStr;
+
+  aDocument->GetHeaderData(nsGkAtoms::viewport_height, heightStr);
+  aDocument->GetHeaderData(nsGkAtoms::viewport_width, widthStr);
+
+  bool autoSize = false;
+
+  if (widthStr.EqualsLiteral("device-width")) {
+    autoSize = true;
+  }
+
+  if (widthStr.IsEmpty() &&
+     (heightStr.EqualsLiteral("device-height") ||
+          scaleFloat == 1.0))
+  {
+    autoSize = true;
+  }
+
+  // XXXjwir3:
+  // See bug 706918, comment 23 for more information on this particular section
+  // of the code. We're using "screen size" in place of the size of the content
+  // area, because on mobile, these are close or equal. This will work for our
+  // purposes (bug 706198), but it will need to be changed in the future to be
+  // more correct when we bring the rest of the viewport code into platform.
+  // We actually want the size of the content area, in the event that we don't
+  // have any metadata about the width and/or height. On mobile, the screen size
+  // and the size of the content area are very close, or the same value.
+  // In XUL fennec, the content area is the size of the <browser> widget, but
+  // in native fennec, the content area is the size of the Gecko LayerView
+  // object.
+
+  // TODO:
+  // Once bug 716575 has been resolved, this code should be changed so that it
+  // does the right thing on all platforms.
+  nsresult result;
+  PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
+  nsCOMPtr<nsIScreenManager> screenMgr =
+    do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
+
+  nsCOMPtr<nsIScreen> screen;
+  screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+  screen->GetRect(&screenLeft, &screenTop, &screenWidth, &screenHeight);
+
+  PRUint32 width = widthStr.ToInteger(&errorCode);
+  if (errorCode) {
+    if (autoSize) {
+      width = screenWidth;
+    } else {
+      width = Preferences::GetInt("browser.viewport.desktopWidth", 0);
+    }
+  }
+
+  width = NS_MIN(width, kViewportMaxWidth);
+  width = NS_MAX(width, kViewportMinWidth);
+
+  // Also recalculate the default zoom, if it wasn't specified in the metadata,
+  // and the width is specified.
+  if (scaleStr.IsEmpty() && !widthStr.IsEmpty()) {
+    scaleFloat = NS_MAX(scaleFloat, (float)(screenWidth/width));
+  }
+
+  PRUint32 height = heightStr.ToInteger(&errorCode);
+
+  if (errorCode) {
+    height = width * ((float)screenHeight / screenWidth);
+  }
+
+  // If height was provided by the user, but width wasn't, then we should
+  // calculate the width.
+  if (widthStr.IsEmpty() && !heightStr.IsEmpty()) {
+    width = (PRUint32) ((height * screenWidth) / screenHeight);
+  }
+
+  height = NS_MIN(height, kViewportMaxHeight);
+  height = NS_MAX(height, kViewportMinHeight);
+
+  // We need to perform a conversion, but only if the initial or maximum
+  // scale were set explicitly by the user.
+  if (!scaleStr.IsEmpty() && !scaleErrorCode) {
+    width = NS_MAX(width, (PRUint32)(screenWidth / scaleFloat));
+    height = NS_MAX(height, (PRUint32)(screenHeight / scaleFloat));
+  } else if (!maxScaleStr.IsEmpty() && !scaleMaxErrorCode) {
+    width = NS_MAX(width, (PRUint32)(screenWidth / scaleMaxFloat));
+    height = NS_MAX(height, (PRUint32)(screenHeight / scaleMaxFloat));
+  }
+
+  bool allowZoom = true;
+  nsAutoString userScalable;
+  aDocument->GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
+
+  if ((userScalable.EqualsLiteral("0")) ||
+      (userScalable.EqualsLiteral("no")) ||
+      (userScalable.EqualsLiteral("false"))) {
+    allowZoom = false;
+  }
+
+  ret.allowZoom = allowZoom;
+  ret.width = width;
+  ret.height = height;
+  ret.defaultZoom = scaleFloat;
+  ret.minZoom = scaleMinFloat;
+  ret.maxZoom = scaleMaxFloat;
+  ret.autoSize = autoSize;
+  return ret;
+}
+
+/* static */
 nsresult
 nsContentUtils::ProcessViewportInfo(nsIDocument *aDocument,
                                     const nsAString &viewportInfo) {
 
   /* We never fail. */
   nsresult rv = NS_OK;
 
   /* Iterators. */
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -30,61 +30,28 @@
  * 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 ***** */
 
+#include "nsDOMBlobBuilder.h"
 #include "jstypedarray.h"
 #include "nsAutoPtr.h"
 #include "nsDOMClassInfoID.h"
-#include "nsDOMFile.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsStringStream.h"
 #include "nsTArray.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
-#include "CheckedInt.h"
-
-#include "mozilla/StdInt.h"
 
 using namespace mozilla;
 
-class nsDOMMultipartFile : public nsDOMFileBase
-{
-public:
-  // Create as a file
-  nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
-                     const nsAString& aName,
-                     const nsAString& aContentType)
-    : nsDOMFileBase(aName, aContentType, UINT64_MAX),
-      mBlobs(aBlobs)
-  {
-  }
-
-  // Create as a blob
-  nsDOMMultipartFile(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
-                     const nsAString& aContentType)
-    : nsDOMFileBase(aContentType, UINT64_MAX),
-      mBlobs(aBlobs)
-  {
-  }
-
-  already_AddRefed<nsIDOMBlob>
-  CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType);
-
-  NS_IMETHOD GetSize(PRUint64*);
-  NS_IMETHOD GetInternalStream(nsIInputStream**);
-
-protected:
-  nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
-};
-
 NS_IMETHODIMP
 nsDOMMultipartFile::GetSize(PRUint64* aLength)
 {
   if (mLength == UINT64_MAX) {
     CheckedUint64 length = 0;
   
     PRUint32 i;
     PRUint32 len = mBlobs.Length();
@@ -194,89 +161,28 @@ nsDOMMultipartFile::CreateSlice(PRUint64
     length -= NS_MIN<PRUint64>(l, length);
   }
 
   // we can create our blob now
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType);
   return blob.forget();
 }
 
-class nsDOMBlobBuilder : public nsIDOMMozBlobBuilder
-{
-public:
-  nsDOMBlobBuilder()
-    : mData(nsnull), mDataLen(0), mDataBufferLen(0)
-  {}
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMMOZBLOBBUILDER
-protected:
-  nsresult AppendVoidPtr(void* aData, PRUint32 aLength);
-  nsresult AppendString(JSString* aString, JSContext* aCx);
-  nsresult AppendBlob(nsIDOMBlob* aBlob);
-  nsresult AppendArrayBuffer(JSObject* aBuffer);
-
-  bool ExpandBufferSize(PRUint64 aSize)
-  {
-    if (mDataBufferLen >= mDataLen + aSize) {
-      mDataLen += aSize;
-      return true;
-    }
-
-    // Start at 1 or we'll loop forever.
-    CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1);
-    while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize)
-      bufferLen *= 2;
-
-    if (!bufferLen.valid())
-      return false;
-
-    // PR_ memory functions are still fallible
-    void* data = PR_Realloc(mData, bufferLen.value());
-    if (!data)
-      return false;
-
-    mData = data;
-    mDataBufferLen = bufferLen.value();
-    mDataLen += aSize;
-    return true;
-  }
-
-  void Flush() {
-    if (mData) {
-      // If we have some data, create a blob for it
-      // and put it on the stack
-
-      nsCOMPtr<nsIDOMBlob> blob =
-        new nsDOMMemoryFile(mData, mDataLen, EmptyString(), EmptyString());
-      mBlobs.AppendElement(blob);
-      mData = nsnull; // The nsDOMMemoryFile takes ownership of the buffer
-      mDataLen = 0;
-      mDataBufferLen = 0;
-    }
-  }
-
-  nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
-  void* mData;
-  PRUint64 mDataLen;
-  PRUint64 mDataBufferLen;
-};
-
 DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder)
 
 NS_IMPL_ADDREF(nsDOMBlobBuilder)
 NS_IMPL_RELEASE(nsDOMBlobBuilder)
 NS_INTERFACE_MAP_BEGIN(nsDOMBlobBuilder)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozBlobBuilder)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozBlobBuilder)
 NS_INTERFACE_MAP_END
 
 nsresult
-nsDOMBlobBuilder::AppendVoidPtr(void* aData, PRUint32 aLength)
+nsDOMBlobBuilder::AppendVoidPtr(const void* aData, PRUint32 aLength)
 {
   NS_ENSURE_ARG_POINTER(aData);
 
   PRUint64 offset = mDataLen;
 
   if (!ExpandBufferSize(aLength))
     return NS_ERROR_OUT_OF_MEMORY;
 
@@ -315,29 +221,39 @@ nsDOMBlobBuilder::AppendArrayBuffer(JSOb
   return AppendVoidPtr(JS_GetArrayBufferData(aBuffer), JS_GetArrayBufferByteLength(aBuffer));
 }
 
 /* nsIDOMBlob getBlob ([optional] in DOMString contentType); */
 NS_IMETHODIMP
 nsDOMBlobBuilder::GetBlob(const nsAString& aContentType,
                           nsIDOMBlob** aBlob)
 {
+  return GetBlobInternal(aContentType, true, aBlob);
+}
+
+nsresult
+nsDOMBlobBuilder::GetBlobInternal(const nsAString& aContentType,
+                                  bool aClearBuffer,
+                                  nsIDOMBlob** aBlob)
+{
   NS_ENSURE_ARG(aBlob);
 
   Flush();
 
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(mBlobs,
                                                      aContentType);
   blob.forget(aBlob);
 
   // NB: This is a willful violation of the spec.  The spec says that
   // the existing contents of the BlobBuilder should be included
   // in the next blob produced.  This seems silly and has been raised
   // on the WHATWG listserv.
-  mBlobs.Clear();
+  if (aClearBuffer) {
+    mBlobs.Clear();
+  }
 
   return NS_OK;
 }
 
 /* nsIDOMBlob getFile (in DOMString name, [optional] in DOMString contentType); */
 NS_IMETHODIMP
 nsDOMBlobBuilder::GetFile(const nsAString& aName,
                           const nsAString& aContentType,
copy from content/base/src/nsDOMBlobBuilder.cpp
copy to content/base/src/nsDOMBlobBuilder.h
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.h
@@ -30,25 +30,20 @@
  * 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 ***** */
 
-#include "jstypedarray.h"
-#include "nsAutoPtr.h"
-#include "nsDOMClassInfoID.h"
+#ifndef nsDOMBlobBuilder_h
+#define nsDOMBlobBuilder_h
+
 #include "nsDOMFile.h"
-#include "nsIMultiplexInputStream.h"
-#include "nsStringStream.h"
-#include "nsTArray.h"
-#include "nsJSUtils.h"
-#include "nsContentUtils.h"
 #include "CheckedInt.h"
 
 #include "mozilla/StdInt.h"
 
 using namespace mozilla;
 
 class nsDOMMultipartFile : public nsDOMFileBase
 {
@@ -75,146 +70,31 @@ public:
 
   NS_IMETHOD GetSize(PRUint64*);
   NS_IMETHOD GetInternalStream(nsIInputStream**);
 
 protected:
   nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
 };
 
-NS_IMETHODIMP
-nsDOMMultipartFile::GetSize(PRUint64* aLength)
-{
-  if (mLength == UINT64_MAX) {
-    CheckedUint64 length = 0;
-  
-    PRUint32 i;
-    PRUint32 len = mBlobs.Length();
-    for (i = 0; i < len; i++) {
-      nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
-      PRUint64 l = 0;
-  
-      nsresult rv = blob->GetSize(&l);
-      NS_ENSURE_SUCCESS(rv, rv);
-  
-      length += l;
-    }
-  
-    NS_ENSURE_TRUE(length.valid(), NS_ERROR_FAILURE);
-
-    mLength = length.value();
-  }
-
-  *aLength = mLength;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMMultipartFile::GetInternalStream(nsIInputStream** aStream)
-{
-  nsresult rv;
-  *aStream = nsnull;
-
-  nsCOMPtr<nsIMultiplexInputStream> stream =
-    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
-  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
-
-  PRUint32 i;
-  for (i = 0; i < mBlobs.Length(); i++) {
-    nsCOMPtr<nsIInputStream> scratchStream;
-    nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
-
-    rv = blob->GetInternalStream(getter_AddRefs(scratchStream));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = stream->AppendStream(scratchStream);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return CallQueryInterface(stream, aStream);
-}
-
-already_AddRefed<nsIDOMBlob>
-nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength,
-                                const nsAString& aContentType)
-{
-  // If we clamped to nothing we create an empty blob
-  nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
-
-  PRUint64 length = aLength;
-  PRUint64 skipStart = aStart;
-
-  // Prune the list of blobs if we can
-  PRUint32 i;
-  for (i = 0; length && skipStart && i < mBlobs.Length(); i++) {
-    nsIDOMBlob* blob = mBlobs[i].get();
-
-    PRUint64 l;
-    nsresult rv = blob->GetSize(&l);
-    NS_ENSURE_SUCCESS(rv, nsnull);
-
-    if (skipStart < l) {
-      PRUint64 upperBound = NS_MIN<PRUint64>(l - skipStart, length);
-
-      nsCOMPtr<nsIDOMBlob> firstBlob;
-      rv = blob->MozSlice(skipStart, skipStart + upperBound,
-                          aContentType, 3,
-                          getter_AddRefs(firstBlob));
-      NS_ENSURE_SUCCESS(rv, nsnull);
-
-      // Avoid wrapping a single blob inside an nsDOMMultipartFile
-      if (length == upperBound) {
-        return firstBlob.forget();
-      }
-
-      blobs.AppendElement(firstBlob);
-      length -= upperBound;
-      i++;
-      break;
-    }
-    skipStart -= l;
-  }
-
-  // Now append enough blobs until we're done
-  for (; length && i < mBlobs.Length(); i++) {
-    nsIDOMBlob* blob = mBlobs[i].get();
-
-    PRUint64 l;
-    nsresult rv = blob->GetSize(&l);
-    NS_ENSURE_SUCCESS(rv, nsnull);
-
-    if (length < l) {
-      nsCOMPtr<nsIDOMBlob> lastBlob;
-      rv = blob->MozSlice(0, length, aContentType, 3,
-                          getter_AddRefs(lastBlob));
-      NS_ENSURE_SUCCESS(rv, nsnull);
-
-      blobs.AppendElement(lastBlob);
-    } else {
-      blobs.AppendElement(blob);
-    }
-    length -= NS_MIN<PRUint64>(l, length);
-  }
-
-  // we can create our blob now
-  nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(blobs, aContentType);
-  return blob.forget();
-}
-
 class nsDOMBlobBuilder : public nsIDOMMozBlobBuilder
 {
 public:
   nsDOMBlobBuilder()
     : mData(nsnull), mDataLen(0), mDataBufferLen(0)
   {}
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMMOZBLOBBUILDER
+
+  nsresult GetBlobInternal(const nsAString& aContentType,
+                           bool aClearBuffer, nsIDOMBlob** aBlob);
+  nsresult AppendVoidPtr(const void* aData, PRUint32 aLength);
+
 protected:
-  nsresult AppendVoidPtr(void* aData, PRUint32 aLength);
   nsresult AppendString(JSString* aString, JSContext* aCx);
   nsresult AppendBlob(nsIDOMBlob* aBlob);
   nsresult AppendArrayBuffer(JSObject* aBuffer);
 
   bool ExpandBufferSize(PRUint64 aSize)
   {
     if (mDataBufferLen >= mDataLen + aSize) {
       mDataLen += aSize;
@@ -255,150 +135,9 @@ protected:
   }
 
   nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
   void* mData;
   PRUint64 mDataLen;
   PRUint64 mDataBufferLen;
 };
 
-DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder)
-
-NS_IMPL_ADDREF(nsDOMBlobBuilder)
-NS_IMPL_RELEASE(nsDOMBlobBuilder)
-NS_INTERFACE_MAP_BEGIN(nsDOMBlobBuilder)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMMozBlobBuilder)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozBlobBuilder)
-NS_INTERFACE_MAP_END
-
-nsresult
-nsDOMBlobBuilder::AppendVoidPtr(void* aData, PRUint32 aLength)
-{
-  NS_ENSURE_ARG_POINTER(aData);
-
-  PRUint64 offset = mDataLen;
-
-  if (!ExpandBufferSize(aLength))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  memcpy((char*)mData + offset, aData, aLength);
-  return NS_OK;
-}
-
-nsresult
-nsDOMBlobBuilder::AppendString(JSString* aString, JSContext* aCx)
-{
-  nsDependentJSString xpcomStr;
-  if (!xpcomStr.init(aCx, aString)) {
-    return NS_ERROR_XPC_BAD_CONVERT_JS;
-  }
-
-  NS_ConvertUTF16toUTF8 utf8Str(xpcomStr);
-
-  return AppendVoidPtr((void*)utf8Str.Data(),
-                       utf8Str.Length());
-}
-
-nsresult
-nsDOMBlobBuilder::AppendBlob(nsIDOMBlob* aBlob)
-{
-  NS_ENSURE_ARG_POINTER(aBlob);
-
-  Flush();
-  mBlobs.AppendElement(aBlob);
-
-  return NS_OK;
-}
-
-nsresult
-nsDOMBlobBuilder::AppendArrayBuffer(JSObject* aBuffer)
-{
-  return AppendVoidPtr(JS_GetArrayBufferData(aBuffer), JS_GetArrayBufferByteLength(aBuffer));
-}
-
-/* nsIDOMBlob getBlob ([optional] in DOMString contentType); */
-NS_IMETHODIMP
-nsDOMBlobBuilder::GetBlob(const nsAString& aContentType,
-                          nsIDOMBlob** aBlob)
-{
-  NS_ENSURE_ARG(aBlob);
-
-  Flush();
-
-  nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartFile(mBlobs,
-                                                     aContentType);
-  blob.forget(aBlob);
-
-  // NB: This is a willful violation of the spec.  The spec says that
-  // the existing contents of the BlobBuilder should be included
-  // in the next blob produced.  This seems silly and has been raised
-  // on the WHATWG listserv.
-  mBlobs.Clear();
-
-  return NS_OK;
-}
-
-/* nsIDOMBlob getFile (in DOMString name, [optional] in DOMString contentType); */
-NS_IMETHODIMP
-nsDOMBlobBuilder::GetFile(const nsAString& aName,
-                          const nsAString& aContentType,
-                          nsIDOMFile** aFile)
-{
-  NS_ENSURE_ARG(aFile);
-
-  Flush();
-
-  nsCOMPtr<nsIDOMFile> file = new nsDOMMultipartFile(mBlobs,
-                                                     aName,
-                                                     aContentType);
-  file.forget(aFile);
-
-  // NB: This is a willful violation of the spec.  The spec says that
-  // the existing contents of the BlobBuilder should be included
-  // in the next blob produced.  This seems silly and has been raised
-  // on the WHATWG listserv.
-  mBlobs.Clear();
-
-  return NS_OK;
-}
-
-/* [implicit_jscontext] void append (in jsval data); */
-NS_IMETHODIMP
-nsDOMBlobBuilder::Append(const jsval& aData, JSContext* aCx)
-{
-  // We need to figure out what our jsval is
-
-  // Is it an object?
-  if (JSVAL_IS_OBJECT(aData)) {
-    JSObject* obj = JSVAL_TO_OBJECT(aData);
-    if (!obj) {
-      // We got passed null.  Just do nothing.
-      return NS_OK;
-    }
-
-    // Is it a Blob?
-    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(
-      nsContentUtils::XPConnect()->
-        GetNativeOfWrapper(aCx, obj));
-    if (blob)
-      return AppendBlob(blob);
-
-    // Is it an array buffer?
-    if (js_IsArrayBuffer(obj)) {
-      JSObject* buffer = js::ArrayBuffer::getArrayBuffer(obj);
-      if (buffer)
-        return AppendArrayBuffer(buffer);
-    }
-  }
-
-  // If it's not a Blob or an ArrayBuffer, coerce it to a string
-  JSString* str = JS_ValueToString(aCx, aData);
-  NS_ENSURE_TRUE(str, NS_ERROR_FAILURE);
-
-  return AppendString(str, aCx);
-}
-
-nsresult NS_NewBlobBuilder(nsISupports* *aSupports)
-{
-  nsDOMBlobBuilder* builder = new nsDOMBlobBuilder();
-  return CallQueryInterface(builder, aSupports);
-}
+#endif
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1717,16 +1717,28 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
   else
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(nsDocument, 
                                               nsNodeUtils::LastRelease(this))
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
+  return nsGenericElement::CanSkip(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
+  return nsGenericElement::CanSkipInCC(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
+  return nsGenericElement::CanSkipThis(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
 static PLDHashOperator
 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, PRUint32 number,
                 void *arg)
 {
   SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(arg);
 
@@ -5842,34 +5854,31 @@ NS_IMETHODIMP
 nsDocument::SetTextContent(const nsAString & aTextContent)
 {
   return nsINode::SetTextContent(aTextContent);
 }
 
 NS_IMETHODIMP
 nsDocument::LookupPrefix(const nsAString & namespaceURI, nsAString & aResult)
 {
-  SetDOMStringToNull(aResult);
-  return NS_OK;
+  return nsINode::LookupPrefix(namespaceURI, aResult);
 }
 
 NS_IMETHODIMP
 nsDocument::IsDefaultNamespace(const nsAString & namespaceURI,
                               bool *aResult)
 {
-  *aResult = namespaceURI.IsEmpty();
-  return NS_OK;
+  return nsINode::IsDefaultNamespace(namespaceURI, aResult);
 }
 
 NS_IMETHODIMP
 nsDocument::LookupNamespaceURI(const nsAString & prefix,
                               nsAString & aResult)
 {
-  SetDOMStringToNull(aResult);
-  return NS_OK;
+  return nsINode::LookupNamespaceURI(prefix, aResult);
 }
 
 NS_IMETHODIMP
 nsDocument::SetUserData(const nsAString & key,
                        nsIVariant *data, nsIDOMUserDataHandler *handler,
                        nsIVariant **aResult)
 {
   return nsINode::SetUserData(key, data, handler, aResult);
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -875,18 +875,18 @@ public:
 
   virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents);
   
   void DecreaseEventSuppression() {
     --mEventsSuppressed;
     MaybeRescheduleAnimationFrameNotifications();
   }
 
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
-                                                         nsIDocument)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
+                                                                   nsIDocument)
 
   void DoNotifyPossibleTitleChange();
 
   nsExternalResourceMap& ExternalResourceMap()
   {
     return mExternalResourceMap;
   }
 
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -92,16 +92,28 @@ nsGenericDOMDataNode::~nsGenericDOMDataN
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericDOMDataNode)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericDOMDataNode)
   nsINode::Trace(tmp, aCallback, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericDOMDataNode)
+  return nsGenericElement::CanSkip(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericDOMDataNode)
+  return nsGenericElement::CanSkipInCC(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericDOMDataNode)
+  return nsGenericElement::CanSkipThis(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode)
   // Always need to traverse script objects, so do that before we check
   // if we're uncollectable.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 
   if (!nsINode::Traverse(tmp, cb)) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -267,17 +267,17 @@ public:
                      bool aCloneAfterOriginal = true);
 
   //----------------------------------------
 
 #ifdef DEBUG
   void ToCString(nsAString& aBuf, PRInt32 aOffset, PRInt32 aLen) const;
 #endif
 
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericDOMDataNode)
 
 protected:
   virtual mozilla::dom::Element* GetNameSpaceElement()
   {
     nsINode *parent = GetNodeParent();
 
     return parent && parent->IsElement() ? parent->AsElement() : nsnull;
   }
@@ -343,13 +343,23 @@ protected:
    * @param aCloneText if true the text content will be cloned too
    * @return the clone
    */
   virtual nsGenericDOMDataNode *CloneDataNode(nsINodeInfo *aNodeInfo,
                                               bool aCloneText) const = 0;
 
   nsTextFragment mText;
 
+public:
+  virtual bool IsPurple()
+  {
+    return mRefCnt.IsPurple();
+  }
+  virtual void RemovePurple()
+  {
+    mRefCnt.RemovePurple();
+  }
+  
 private:
   already_AddRefed<nsIAtom> GetCurrentValueAtom();
 };
 
 #endif /* nsGenericDOMDataNode_h___ */
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -148,17 +148,17 @@
 #include "mozAutoDocUpdate.h"
 
 #include "nsCSSParser.h"
 #include "prprf.h"
 
 #include "nsSVGFeatures.h"
 #include "nsDOMMemoryReporter.h"
 #include "nsWrapperCacheInlines.h"
-
+#include "nsCycleCollector.h"
 #include "xpcpublic.h"
 #include "xpcprivate.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
 
@@ -1203,55 +1203,57 @@ nsINode::GetContextForEventHandlers(nsre
 
 /* static */
 void
 nsINode::Trace(nsINode *tmp, TraceCallback cb, void *closure)
 {
   nsContentUtils::TraceWrapper(tmp, cb, closure);
 }
 
-static bool
-IsXBL(nsINode* aNode)
-{
-  return aNode->IsElement() &&
-         aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL);
+
+static
+bool UnoptimizableCCNode(nsINode* aNode)
+{
+  const PtrBits problematicFlags = (NODE_IS_ANONYMOUS |
+                                    NODE_IS_IN_ANONYMOUS_SUBTREE |
+                                    NODE_IS_NATIVE_ANONYMOUS_ROOT |
+                                    NODE_MAY_BE_IN_BINDING_MNGR |
+                                    NODE_IS_INSERTION_PARENT);
+  return aNode->HasFlag(problematicFlags) ||
+         aNode->NodeType() == nsIDOMNode::ATTRIBUTE_NODE ||
+         // For strange cases like xbl:content/xbl:children
+         (aNode->IsElement() &&
+          aNode->AsElement()->IsInNamespace(kNameSpaceID_XBL));
 }
 
 /* static */
 bool
 nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
 {
   nsIDocument *currentDoc = tmp->GetCurrentDoc();
   if (currentDoc &&
       nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
     return false;
   }
 
   if (nsCCUncollectableMarker::sGeneration) {
     // If we're black no need to traverse.
-    if (tmp->IsBlack()) {
+    if (tmp->IsBlack() || tmp->InCCBlackTree()) {
       return false;
     }
 
-    const PtrBits problematicFlags =
-      (NODE_IS_ANONYMOUS |
-       NODE_IS_IN_ANONYMOUS_SUBTREE |
-       NODE_IS_NATIVE_ANONYMOUS_ROOT |
-       NODE_MAY_BE_IN_BINDING_MNGR |
-       NODE_IS_INSERTION_PARENT);
-
-    if (!tmp->HasFlag(problematicFlags) && !IsXBL(tmp)) {
+    if (!UnoptimizableCCNode(tmp)) {
       // If we're in a black document, return early.
       if ((currentDoc && currentDoc->IsBlack())) {
         return false;
       }
       // If we're not in anonymous content and we have a black parent,
       // return early.
       nsIContent* parent = tmp->GetParent();
-      if (parent && !IsXBL(parent) && parent->IsBlack()) {
+      if (parent && !UnoptimizableCCNode(parent) && parent->IsBlack()) {
         NS_ABORT_IF_FALSE(parent->IndexOf(tmp) >= 0, "Parent doesn't own us?");
         return false;
       }
     }
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNodeInfo)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
@@ -4251,16 +4253,392 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     }
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericElement)
   nsINode::Trace(tmp, aCallback, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
+static JSObject*
+GetJSObjectChild(nsINode* aNode)
+{
+  if (aNode->PreservingWrapper()) {
+    return aNode->GetWrapperPreserveColor();
+  }
+  return aNode->GetExpandoObjectPreserveColor();
+}
+                                  
+static bool
+NeedsScriptTraverse(nsINode* aNode)
+{
+  JSObject* o = GetJSObjectChild(aNode);
+  return o && xpc_IsGrayGCThing(o);
+}
+
+void
+nsGenericElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
+                               void* aData)
+{
+  PRUint32* gen = static_cast<PRUint32*>(aData);
+  xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
+}
+
+void
+nsGenericElement::MarkUserDataHandler(void* aObject, nsIAtom* aKey,
+                                      void* aChild, void* aData)
+{
+  nsCOMPtr<nsIXPConnectWrappedJS> wjs =
+    do_QueryInterface(static_cast<nsISupports*>(aChild));
+  xpc_UnmarkGrayObject(wjs);
+}
+
+static void
+MarkNodeChildren(nsINode* aNode)
+{
+  JSObject* o = GetJSObjectChild(aNode);
+  xpc_UnmarkGrayObject(o);
+
+  nsEventListenerManager* elm = aNode->GetListenerManager(false);
+  if (elm) {
+    elm->UnmarkGrayJSListeners();
+  }
+
+  if (aNode->HasProperties()) {
+    nsIDocument* ownerDoc = aNode->OwnerDoc();
+    ownerDoc->PropertyTable(DOM_USER_DATA)->
+      Enumerate(aNode, nsGenericElement::MarkUserData,
+                &nsCCUncollectableMarker::sGeneration);
+    ownerDoc->PropertyTable(DOM_USER_DATA_HANDLER)->
+      Enumerate(aNode, nsGenericElement::MarkUserDataHandler,
+                &nsCCUncollectableMarker::sGeneration);
+  }
+}
+
+nsINode*
+FindOptimizableSubtreeRoot(nsINode* aNode)
+{
+  nsINode* p;
+  while ((p = aNode->GetNodeParent())) {
+    if (UnoptimizableCCNode(aNode)) {
+      return nsnull;
+    }
+    aNode = p;
+  }
+  
+  if (UnoptimizableCCNode(aNode)) {
+    return nsnull;
+  }
+  return aNode;
+}
+
+nsAutoTArray<nsINode*, 1020>* gCCBlackMarkedNodes = nsnull;
+
+void
+ClearBlackMarkedNodes()
+{
+  if (!gCCBlackMarkedNodes) {
+    return;
+  }
+  PRUint32 len = gCCBlackMarkedNodes->Length();
+  for (PRUint32 i = 0; i < len; ++i) {
+    nsINode* n = gCCBlackMarkedNodes->ElementAt(i);
+    n->SetCCMarkedRoot(false);
+    n->SetInCCBlackTree(false);
+  }
+  delete gCCBlackMarkedNodes;
+  gCCBlackMarkedNodes = nsnull;
+}
+
+// static
+bool
+nsGenericElement::CanSkipInCC(nsINode* aNode)
+{
+  // Don't try to optimize anything during shutdown.
+  if (nsCCUncollectableMarker::sGeneration == 0) {
+    return false;
+  }
+
+  // Bail out early if aNode is somewhere in anonymous content,
+  // or otherwise unusual.
+  if (UnoptimizableCCNode(aNode)) {
+    return false;
+  }
+
+  nsIDocument* currentDoc = aNode->GetCurrentDoc();
+  if (currentDoc &&
+      nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
+    return !NeedsScriptTraverse(aNode);
+  }
+
+  nsINode* root =
+    currentDoc ? static_cast<nsINode*>(currentDoc) :
+                 FindOptimizableSubtreeRoot(aNode);
+  if (!root) {
+    return false;
+  }
+  
+  // Subtree has been traversed already.
+  if (root->CCMarkedRoot()) {
+    return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
+  }
+
+  if (!gCCBlackMarkedNodes) {
+    gCCBlackMarkedNodes = new nsAutoTArray<nsINode*, 1020>;
+  }
+
+  // nodesToUnpurple contains nodes which will be removed
+  // from the purple buffer if the DOM tree is black.
+  nsAutoTArray<nsIContent*, 1020> nodesToUnpurple;
+  // grayNodes need script traverse, so they aren't removed from
+  // the purple buffer, but are marked to be in black subtree so that
+  // traverse is faster.
+  nsAutoTArray<nsINode*, 1020> grayNodes;
+
+  bool foundBlack = root->IsBlack();
+  if (root != currentDoc) {
+    currentDoc = nsnull;
+    if (NeedsScriptTraverse(root)) {
+      grayNodes.AppendElement(root);
+    } else if (static_cast<nsIContent*>(root)->IsPurple()) {
+      nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
+    }
+  }
+
+  // Traverse the subtree and check if we could know without CC
+  // that it is black.
+  // Note, this traverse is non-virtual and inline, so it should be a lot faster
+  // than CC's generic traverse.
+  for (nsIContent* node = root->GetFirstChild(); node;
+       node = node->GetNextNode(root)) {
+    foundBlack = foundBlack || node->IsBlack();
+    if (foundBlack && currentDoc) {
+      // If we can mark the whole document black, no need to optimize
+      // so much, since when the next purple node in the document will be
+      // handled, it is fast to check that currentDoc is in CCGeneration.
+      break;
+    }
+    if (NeedsScriptTraverse(node)) {
+      // Gray nodes need real CC traverse.
+      grayNodes.AppendElement(node);
+    } else if (node->IsPurple()) {
+      nodesToUnpurple.AppendElement(node);
+    }
+  }
+
+  root->SetCCMarkedRoot(true);
+  root->SetInCCBlackTree(foundBlack);
+  gCCBlackMarkedNodes->AppendElement(root);
+
+  if (!foundBlack) {
+    return false;
+  }
+
+  if (currentDoc) {
+    // Special case documents. If we know the document is black,
+    // we can mark the document to be in CCGeneration.
+    currentDoc->
+      MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
+  } else {
+    for (PRUint32 i = 0; i < grayNodes.Length(); ++i) {
+      nsINode* node = grayNodes[i];
+      node->SetInCCBlackTree(true);
+    }
+    gCCBlackMarkedNodes->AppendElements(grayNodes);
+  }
+
+  // Subtree is black, we can remove non-gray purple nodes from
+  // purple buffer.
+  for (PRUint32 i = 0; i < nodesToUnpurple.Length(); ++i) {
+    nsIContent* purple = nodesToUnpurple[i];
+    // Can't remove currently handled purple node.
+    if (purple != aNode) {
+      purple->RemovePurple();
+    }
+  }
+  return !NeedsScriptTraverse(aNode);
+}
+
+nsAutoTArray<nsINode*, 1020>* gPurpleRoots = nsnull;
+
+void ClearPurpleRoots()
+{
+  if (!gPurpleRoots) {
+    return;
+  }
+  PRUint32 len = gPurpleRoots->Length();
+  for (PRUint32 i = 0; i < len; ++i) {
+    nsINode* n = gPurpleRoots->ElementAt(i);
+    n->SetIsPurpleRoot(false);
+  }
+  delete gPurpleRoots;
+  gPurpleRoots = nsnull;
+}
+
+static bool
+ShouldClearPurple(nsIContent* aContent)
+{
+  if (aContent && aContent->IsPurple()) {
+    return true;
+  }
+
+  JSObject* o = GetJSObjectChild(aContent);
+  if (o && xpc_IsGrayGCThing(o)) {
+    return true;
+  }
+
+  if (aContent->GetListenerManager(false)) {
+    return true;
+  }
+
+  return aContent->HasProperties();
+}
+
+// CanSkip checks if aNode is black, and if it is, returns
+// true. If aNode is in a black DOM tree, CanSkip may also remove other objects
+// from purple buffer and unmark event listeners and user data.
+// If the root of the DOM tree is a document, less optimizations are done
+// since checking the blackness of the current document is usually fast and we
+// don't want slow down such common cases.
+bool
+nsGenericElement::CanSkip(nsINode* aNode)
+{
+  // Don't try to optimize anything during shutdown.
+  if (nsCCUncollectableMarker::sGeneration == 0) {
+    return false;
+  }
+
+  // Bail out early if aNode is somewhere in anonymous content,
+  // or otherwise unusual.
+  if (UnoptimizableCCNode(aNode)) {
+    return false;
+  }
+
+  nsIDocument* currentDoc = aNode->GetCurrentDoc();
+  if (currentDoc &&
+      nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
+    MarkNodeChildren(aNode);
+    return true;
+  }
+
+  nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
+                               FindOptimizableSubtreeRoot(aNode);
+  if (!root) {
+    return false;
+  }
+ 
+  // Subtree has been traversed already, and aNode
+  // wasn't removed from purple buffer. No need to do more here.
+  if (root->IsPurpleRoot()) {
+    return false;
+  }
+
+  // nodesToClear contains nodes which are either purple or
+  // gray.
+  nsAutoTArray<nsIContent*, 1020> nodesToClear;
+
+  bool foundBlack = root->IsBlack();
+  if (root != currentDoc) {
+    currentDoc = nsnull;
+    if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
+      nodesToClear.AppendElement(static_cast<nsIContent*>(root));
+    }
+  }
+
+  // Traverse the subtree and check if we could know without CC
+  // that it is black.
+  // Note, this traverse is non-virtual and inline, so it should be a lot faster
+  // than CC's generic traverse.
+  for (nsIContent* node = root->GetFirstChild(); node;
+       node = node->GetNextNode(root)) {
+    foundBlack = foundBlack || node->IsBlack();
+    if (foundBlack) {
+      if (currentDoc) {
+        // If we can mark the whole document black, no need to optimize
+        // so much, since when the next purple node in the document will be
+        // handled, it is fast to check that the currentDoc is in CCGeneration.
+        break;
+      }
+      // No need to put stuff to the nodesToClear array, if we can clear it
+      // already here.
+      if (node->IsPurple() && node != aNode) {
+        node->RemovePurple();
+      }
+      MarkNodeChildren(node);
+    } else if (ShouldClearPurple(node)) {
+      // Collect interesting nodes which we can clear if we find that
+      // they are kept alive in a black tree.
+      nodesToClear.AppendElement(node);
+    }
+  }
+
+  if (!foundBlack) {
+    if (!gPurpleRoots) {
+      gPurpleRoots = new nsAutoTArray<nsINode*, 1020>();
+    }
+    root->SetIsPurpleRoot(true);
+    gPurpleRoots->AppendElement(root);
+    return false;
+  }
+
+  if (currentDoc) {
+    // Special case documents. If we know the document is black,
+    // we can mark the document to be in CCGeneration.
+    currentDoc->
+      MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
+    MarkNodeChildren(currentDoc);
+  }
+
+  // Subtree is black, so we can remove purple nodes from
+  // purple buffer and mark stuff that to be certainly alive.
+  for (PRUint32 i = 0; i < nodesToClear.Length(); ++i) {
+    nsIContent* n = nodesToClear[i];
+    MarkNodeChildren(n);
+    // Can't remove currently handled purple node.
+    if (n != aNode && n->IsPurple()) {
+      n->RemovePurple();
+    }
+  }
+  return true;
+}
+
+bool
+nsGenericElement::CanSkipThis(nsINode* aNode)
+{
+  if (nsCCUncollectableMarker::sGeneration == 0) {
+    return false;
+  }
+  if (aNode->IsBlack()) {
+    return true;
+  }
+  nsIDocument* c = aNode->GetCurrentDoc();
+  return 
+    ((c && nsCCUncollectableMarker::InGeneration(c->GetMarkedCCGeneration())) ||
+     aNode->InCCBlackTree()) && !NeedsScriptTraverse(aNode);
+}
+
+void
+nsGenericElement::InitCCCallbacks()
+{
+  nsCycleCollector_setForgetSkippableCallback(ClearPurpleRoots);
+  nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericElement)
+  return nsGenericElement::CanSkip(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericElement)
+  return nsGenericElement::CanSkipInCC(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericElement)
+  return nsGenericElement::CanSkipThis(tmp);
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
 static const char* kNSURIs[] = {
   " ([none])",
   " (xmlns)",
   " (xml)",
   " (xhtml)",
   " (XLink)",
   " (XSLT)",
   " (XBL)",
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -597,27 +597,45 @@ public:
    * namespace ID must not be kNameSpaceID_Unknown and the name must not be
    * null.  Note that this can only return info on attributes that actually
    * live on this element (and is only virtual to handle XUL prototypes).  That
    * is, this should only be called from methods that only care about attrs
    * that effectively live in mAttrsAndChildren.
    */
   virtual nsAttrInfo GetAttrInfo(PRInt32 aNamespaceID, nsIAtom* aName) const;
 
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsGenericElement)
 
   virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
   {
   }
 
   /**
    * Fire a DOMNodeRemoved mutation event for all children of this node
    */
   void FireNodeRemovedForChildren();
 
+  virtual bool IsPurple()
+  {
+    return mRefCnt.IsPurple();
+  }
+
+  virtual void RemovePurple()
+  {
+    mRefCnt.RemovePurple();
+  }
+
+  static bool CanSkip(nsINode* aNode);
+  static bool CanSkipInCC(nsINode* aNode);
+  static bool CanSkipThis(nsINode* aNode);
+  static void InitCCCallbacks();
+  static void MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
+                           void *aData);
+  static void MarkUserDataHandler(void* aObject, nsIAtom* aKey, void* aChild,
+                                  void* aData);
 protected:
   /**
    * Set attribute and (if needed) notify documentobservers and fire off
    * mutation events.  This will send the AttributeChanged notification.
    * Callers of this method are responsible for calling AttributeWillChange,
    * since that needs to happen before the new attr value has been set, and
    * in particular before it has been parsed.
    *
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -577,16 +577,18 @@ nsXMLHttpRequest::Initialize(nsISupports
 
 void
 nsXMLHttpRequest::ResetResponse()
 {
   mResponseXML = nsnull;
   mResponseBody.Truncate();
   mResponseText.Truncate();
   mResponseBlob = nsnull;
+  mDOMFile = nsnull;
+  mBuilder = nsnull;
   mResultArrayBuffer = nsnull;
   mResultJSON = JSVAL_VOID;
   mLoadTransferred = 0;
   mResponseBodyDecodedPos = 0;
 }
 
 void
 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
@@ -962,16 +964,38 @@ nsXMLHttpRequest::CreateResponseParsedJS
                     (jschar*)mResponseText.get(),
                     mResponseText.Length(), &mResultJSON)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
+nsresult
+nsXMLHttpRequest::CreatePartialBlob()
+{
+  if (mDOMFile) {
+    if (mLoadTotal == mLoadTransferred) {
+      mResponseBlob = mDOMFile;
+    } else {
+      mResponseBlob =
+        mDOMFile->CreateSlice(0, mLoadTransferred, EmptyString());
+    }
+    return NS_OK;
+  }
+
+  nsCAutoString contentType;
+  if (mLoadTotal == mLoadTransferred) {
+    mChannel->GetContentType(contentType);
+  }
+
+  return mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType),
+                                   false, getter_AddRefs(mResponseBlob));
+}
+
 /* attribute AString responseType; */
 NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
 {
   switch (mResponseType) {
   case XML_HTTP_RESPONSE_TYPE_DEFAULT:
     aResponseType.Truncate();
     break;
   case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
@@ -990,16 +1014,19 @@ NS_IMETHODIMP nsXMLHttpRequest::GetRespo
     aResponseType.AssignLiteral("json");
     break;
   case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
     aResponseType.AssignLiteral("moz-chunked-text");
     break;
   case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
     aResponseType.AssignLiteral("moz-chunked-arraybuffer");
     break;
+  case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
+    aResponseType.AssignLiteral("moz-blob");
+    break;
   default:
     NS_ERROR("Should not happen");
   }
 
   return NS_OK;
 }
 
 /* attribute AString responseType; */
@@ -1036,29 +1063,32 @@ NS_IMETHODIMP nsXMLHttpRequest::SetRespo
       return NS_ERROR_DOM_INVALID_STATE_ERR;
     }
     mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT;
   } else if (aResponseType.EqualsLiteral("moz-chunked-arraybuffer")) {
     if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
       return NS_ERROR_DOM_INVALID_STATE_ERR;
     }
     mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
+  } else if (aResponseType.EqualsLiteral("moz-blob")) {
+    mResponseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB;
   }
   // If the given value is not the empty string, "arraybuffer",
   // "blob", "document", or "text" terminate these steps.
 
   // If the state is OPENED, SetCacheAsFile would have no effect here
   // because the channel hasn't initialized the cache entry yet.
   // SetCacheAsFile will be called from OnStartRequest.
   // If the state is HEADERS_RECEIVED, however, we need to call
   // it immediately because OnStartRequest is already dispatched.
   if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
     nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
     if (cc) {
-      cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB);
+      cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
+                         mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB);
     }
   }
 
   return NS_OK;
 }
 
 /* readonly attribute jsval response; */
 NS_IMETHODIMP nsXMLHttpRequest::GetResponse(JSContext *aCx, jsval *aResult)
@@ -1092,22 +1122,32 @@ NS_IMETHODIMP nsXMLHttpRequest::GetRespo
       }
       *aResult = OBJECT_TO_JSVAL(mResultArrayBuffer);
     } else {
       *aResult = JSVAL_NULL;
     }
     break;
 
   case XML_HTTP_RESPONSE_TYPE_BLOB:
-    if (mState & XML_HTTP_REQUEST_DONE && mResponseBlob) {
+  case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
+    *aResult = JSVAL_NULL;
+    if (mState & XML_HTTP_REQUEST_DONE) {
+      // do nothing here
+    } else if (mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
+      if (!mResponseBlob) {
+        rv = CreatePartialBlob();
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    } else {
+      return rv;
+    }
+    if (mResponseBlob) {
       JSObject* scope = JS_GetGlobalForScopeChain(aCx);
       rv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, aResult,
                                       nsnull, true);
-    } else {
-      *aResult = JSVAL_NULL;
     }
     break;
 
   case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
     if (mState & XML_HTTP_REQUEST_DONE && mResponseXML) {
       JSObject* scope = JS_GetGlobalForScopeChain(aCx);
       rv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, aResult,
                                       nsnull, true);
@@ -1707,44 +1747,55 @@ nsXMLHttpRequest::StreamReaderFunc(nsIIn
                                    PRUint32 *writeCount)
 {
   nsXMLHttpRequest* xmlHttpRequest = static_cast<nsXMLHttpRequest*>(closure);
   if (!xmlHttpRequest || !writeCount) {
     NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
     return NS_ERROR_FAILURE;
   }
 
-  if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB &&
-      xmlHttpRequest->mResponseBlob) {
-    *writeCount = count;
-    return NS_OK;
+  nsresult rv = NS_OK;
+
+  if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
+      xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
+    if (!xmlHttpRequest->mDOMFile) {
+      if (!xmlHttpRequest->mBuilder) {
+        xmlHttpRequest->mBuilder = new nsDOMBlobBuilder();
+      }
+      rv = xmlHttpRequest->mBuilder->AppendVoidPtr(fromRawSegment, count);
+    }
+    // Clear the cache so that the blob size is updated.
+    if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
+      xmlHttpRequest->mResponseBlob = nsnull;
+    }
+    if (NS_SUCCEEDED(rv)) {
+      *writeCount = count;
+    }
+    return rv;
   }
 
   if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
        xmlHttpRequest->mResponseXML) ||
       xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
-      xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
       xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
     // Copy for our own use
     PRUint32 previousLength = xmlHttpRequest->mResponseBody.Length();
     xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
     if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_TEXT ||
              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_JSON ||
              xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
     NS_ASSERTION(!xmlHttpRequest->mResponseXML,
                  "We shouldn't be parsing a doc here");
     xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
   }
 
-  nsresult rv = NS_OK;
-
   if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
     // Give the same data to the parser.
 
     // We need to wrap the data in a new lightweight stream and pass that
     // to the parser, because calling ReadSegments() recursively on the same
     // stream is not supported.
     nsCOMPtr<nsIInputStream> copyStream;
     rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
@@ -1768,17 +1819,17 @@ nsXMLHttpRequest::StreamReaderFunc(nsIIn
     *writeCount = count;
   } else {
     *writeCount = 0;
   }
 
   return rv;
 }
 
-bool nsXMLHttpRequest::CreateResponseBlob(nsIRequest *request)
+bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request)
 {
   nsCOMPtr<nsIFile> file;
   nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(request));
   if (cc) {
     cc->GetCacheFile(getter_AddRefs(file));
   } else {
     nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request);
     if (fc) {
@@ -1796,19 +1847,20 @@ bool nsXMLHttpRequest::CreateResponseBlo
       // fully cached (i.e. whether we can skip reading the response).
       cc->IsFromCache(&fromFile);
     } else {
       // If the response is coming from the local resource, we can skip
       // reading the response unconditionally.
       fromFile = true;
     }
 
-    mResponseBlob =
+    mDOMFile =
       new nsDOMFileFile(file, NS_ConvertASCIItoUTF16(contentType), cacheToken);
-    mResponseBody.Truncate();
+    mBuilder = nsnull;
+    NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
   }
   return fromFile;
 }
 
 NS_IMETHODIMP
 nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
                                   nsISupports *ctxt,
                                   nsIInputStream *inStr,
@@ -1817,30 +1869,31 @@ nsXMLHttpRequest::OnDataAvailable(nsIReq
 {
   NS_ENSURE_ARG_POINTER(inStr);
 
   NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
 
   mProgressSinceLastProgressEvent = true;
 
   bool cancelable = false;
-  if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB && !mResponseBlob) {
-    cancelable = CreateResponseBlob(request);
+  if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
+       mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) {
+    cancelable = CreateDOMFile(request);
     // The nsIStreamListener contract mandates us
     // to read from the stream before returning.
   }
 
   PRUint32 totalRead;
   nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc,
                                     (void*)this, count, &totalRead);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (cancelable) {
     // We don't have to read from the local file for the blob response
-    mResponseBlob->GetSize(&mLoadTransferred);
+    mDOMFile->GetSize(&mLoadTransferred);
     ChangeState(XML_HTTP_REQUEST_LOADING);
     return request->Cancel(NS_OK);
   }
 
   mLoadTransferred += totalRead;
 
   ChangeState(XML_HTTP_REQUEST_LOADING);
   
@@ -1931,17 +1984,18 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
   }
 
   mReadRequest = request;
   mContext = ctxt;
   mState |= XML_HTTP_REQUEST_PARSEBODY;
   mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
   ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
 
-  if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
+  if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
+      mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
     nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
     if (cc) {
       cc->SetCacheAsFile(true);
     }
   }
 
   ResetResponse();
 
@@ -2127,44 +2181,41 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
 
   // If we're received data since the last progress event, make sure to fire
   // an event for it, except in the HTML case, defer the last progress event
   // until the parser is done.
   if (!mIsHtml) {
     MaybeDispatchProgressEvents(true);
   }
 
-  nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
-  NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
-
-  if (NS_SUCCEEDED(status) && mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB) {
-    if (!mResponseBlob) {
-      CreateResponseBlob(request);
+  if (NS_SUCCEEDED(status) &&
+      (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
+       mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) {
+    if (!mDOMFile) {
+      CreateDOMFile(request);
     }
-    if (!mResponseBlob) {
+    if (mDOMFile) {
+      mResponseBlob = mDOMFile;
+      mDOMFile = nsnull;
+    } else {
       // Smaller files may be written in cache map instead of separate files.
       // Also, no-store response cannot be written in persistent cache.
       nsCAutoString contentType;
       mChannel->GetContentType(contentType);
-      // XXX We should change mResponseBody to be a raw malloc'ed buffer
-      //     to avoid copying the data.
-      PRUint32 blobLen = mResponseBody.Length();
-      void *blobData = PR_Malloc(blobLen);
-      if (blobData) {
-        memcpy(blobData, mResponseBody.BeginReading(), blobLen);
-
-        mResponseBlob =
-          new nsDOMMemoryFile(blobData, blobLen,
-                              NS_ConvertASCIItoUTF16(contentType));
-        mResponseBody.Truncate();
-      }
-      NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
+      mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType),
+                                false, getter_AddRefs(mResponseBlob));
+      mBuilder = nsnull;
     }
+    NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
+    NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   }
 
+  nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
+  NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
+
   channel->SetNotificationCallbacks(nsnull);
   mNotificationCallbacks = nsnull;
   mChannelEventSink = nsnull;
   mProgressEventSink = nsnull;
 
   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
 
   if (NS_FAILED(status)) {
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -60,16 +60,18 @@
 #include "nsIJSNativeInitializer.h"
 #include "nsIDOMLSProgressEvent.h"
 #include "nsIDOMNSEvent.h"
 #include "nsITimer.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsDOMProgressEvent.h"
 #include "nsDOMEventTargetWrapperCache.h"
 #include "nsContentUtils.h"
+#include "nsDOMFile.h"
+#include "nsDOMBlobBuilder.h"
 
 class nsILoadGroup;
 class AsyncVerifyRedirectCallbackForwarder;
 class nsIUnicodeDecoder;
 
 class nsXHREventTarget : public nsDOMEventTargetWrapperCache,
                          public nsIXMLHttpRequestEventTarget
 {
@@ -212,17 +214,18 @@ protected:
   nsresult AppendToResponseText(const char * aBuffer, PRUint32 aBufferLen);
   static NS_METHOD StreamReaderFunc(nsIInputStream* in,
                 void* closure,
                 const char* fromRawSegment,
                 PRUint32 toOffset,
                 PRUint32 count,
                 PRUint32 *writeCount);
   nsresult CreateResponseParsedJSON(JSContext* aCx);
-  bool CreateResponseBlob(nsIRequest *request);
+  nsresult CreatePartialBlob(void);
+  bool CreateDOMFile(nsIRequest *request);
   // Change the state of the object with this. The broadcast argument
   // determines if the onreadystatechange listener should be called.
   nsresult ChangeState(PRUint32 aState, bool aBroadcast = true);
   already_AddRefed<nsILoadGroup> GetLoadGroup() const;
   nsIURI *GetBaseURI();
 
   nsresult RemoveAddEventListener(const nsAString& aType,
                                   nsRefPtr<nsDOMEventListenerWrapper>& aCurrent,
@@ -304,20 +307,30 @@ protected:
   enum {
     XML_HTTP_RESPONSE_TYPE_DEFAULT,
     XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER,
     XML_HTTP_RESPONSE_TYPE_BLOB,
     XML_HTTP_RESPONSE_TYPE_DOCUMENT,
     XML_HTTP_RESPONSE_TYPE_TEXT,
     XML_HTTP_RESPONSE_TYPE_JSON,
     XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT,
-    XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER
+    XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER,
+    XML_HTTP_RESPONSE_TYPE_MOZ_BLOB
   } mResponseType;
 
+  // It is either a cached blob-response from the last call to GetResponse,
+  // but is also explicitly set in OnStopRequest.
   nsCOMPtr<nsIDOMBlob> mResponseBlob;
+  // Non-null only when we are able to get a os-file representation of the
+  // response, i.e. when loading from a file, or when the http-stream
+  // caches into a file or is reading from a cached file.
+  nsRefPtr<nsDOMFileBase> mDOMFile;
+  // We stream data to mBuilder when response type is "blob" or "moz-blob"
+  // and mDOMFile is null.
+  nsRefPtr<nsDOMBlobBuilder> mBuilder;
 
   nsCString mOverrideMimeType;
 
   /**
    * The notification callbacks the channel had when Send() was
    * called.  We want to forward things here as needed.
    */
   nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -524,16 +524,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug684671.html \
 		test_bug685798.html \
 		test_bug686449.xhtml \
 		test_bug690056.html \
 		test_bug692434.html \
 		file_bug692434.xml \
 		test_bug693615.html \
 		test_bug693875.html \
+		test_bug694754.xhtml \
 		test_bug698384.html \
 		test_nodelist_holes.html \
 		test_xhr_abort_after_load.html \
 		test_bug702439.html \
 		test_bug702439.html^headers^ \
 		file_bug702439.html \
 		test_bug707142.html \
 		file_bug707142_baseline.json \
--- a/content/base/test/test_XHR.html
+++ b/content/base/test/test_XHR.html
@@ -207,23 +207,26 @@ is(xhr.status, 200, "wrong status");
 checkResponseTextAccessThrows(xhr);
 checkResponseXMLAccessThrows(xhr);
 is(xhr.response, null, "Bad JSON should result in null response.");
 is(xhr.response, null, "Bad JSON should result in null response even 2nd time.");
 
 // test response (responseType='blob')
 var onloadCount = 0;
 function checkOnloadCount() {
-  if (++onloadCount >= 3) SimpleTest.finish();
+  if (++onloadCount >= 6) SimpleTest.finish();
 };
 
+var responseTypes = ['blob', 'moz-blob'];
+for (var i = 0; i < responseTypes.length; i++) {
+var t = responseTypes[i];
 // with a simple text file
 xhr = new XMLHttpRequest();
 xhr.open("GET", 'file_XHR_pass2.txt'); 
-xhr.responseType = 'blob';
+xhr.responseType = t;
 xhr.onloadend = continueTest;
 xhr.send(null);
 yield;
 is(xhr.status, 200, "wrong status");
 checkResponseTextAccessThrows(xhr);
 checkResponseXMLAccessThrows(xhr);
 b = xhr.response;
 ok(b, "should have a non-null blob");
@@ -240,17 +243,17 @@ fr.readAsBinaryString(b);
 
 // with a binary file
 (function(){
 var xhr = new XMLHttpRequest();
 xhr.onreadystatechange = function() {
   switch (xhr.readyState) {
   case 2:
     is(xhr.status, 200, "wrong status");
-    xhr.responseType = 'blob';
+    xhr.responseType = t;
     break;
   case 4:
     b = xhr.response;
     ok(b != null, "should have a non-null blob");
     is(b.size, 12, "wrong blob size");
 
     var fr = new FileReader();
     fr.onload = function() {
@@ -288,19 +291,20 @@ xhr.onreadystatechange = function() {
       checkOnloadCount();
     };
     xhr = null; // kill the XHR object
     SpecialPowers.gc();
     fr.readAsArrayBuffer(b);
   }
 };
 xhr.open("GET", 'file_XHR_binary2.bin', true);
-xhr.responseType = 'blob';
+xhr.responseType = t;
 xhr.send(null);
 })();
+}
 
 var client = new XMLHttpRequest();
 client.onreadystatechange = function() {
     if(client.readyState == 4) {
       try {
         is(client.responseXML, null, "responseXML should be null.");
         is(client.responseText, "", "responseText should be empty string.");
         is(client.response, "", "response should be empty string.");
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug694754.xhtml
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:test="http://example.com/test">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=694754
+-->
+<head>
+  <title>Test for Bug 694754</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=694754">Mozilla Bug 694754</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 694754 **/
+/*
+The following code tests if calling the DOM methods Document::lookupNamespaceURI
+and Document::lookupPrefix directly (with quickstubs) and through XPCOM leads
+to the same result.
+
+This test makes use of the bug/feature that deleting a method from the 
+prototype forces the engine to go through XPCOM.
+*/
+
+// Document::lookupPrefix called directly (quickstubs)
+var prefixDirect = document.lookupPrefix("http://example.com/test");
+is(prefixDirect, "test", 
+   "calling Document::lookupPrefix through quickstubs works");
+
+// Document::lookupPrefix called via XPCOM
+var proto = Object.getPrototypeOf(document);
+delete(proto.lookupPrefix);
+var prefixThroughXPCOM = document.lookupPrefix("http://example.com/test");
+is(prefixThroughXPCOM, "test", 
+   "calling Document::lookupPrefix through XPCOM works");
+
+
+
+// Document::lookupNamespaceURI called directly (quickstubs)
+var namespaceDirect = document.lookupNamespaceURI(null);
+is(namespaceDirect, "http://www.w3.org/1999/xhtml", 
+   "calling Document::lookupNamespaceURI through quickstubs works");
+
+// Document::lookupNamespaceURI called via XPCOM
+delete(proto.lookupNamespaceURI);
+var namespaceThroughXPCOM = document.lookupNamespaceURI(null);
+is(namespaceThroughXPCOM, "http://www.w3.org/1999/xhtml", 
+   "calling Document::lookupNamespaceURI through XPCOM works");
+   
+// Document::isDefaultNamespace called directly (quickstubs)
+var isDefaultNamespaceDirect = document.isDefaultNamespace("http://www.w3.org/1999/xhtml");
+is(isDefaultNamespaceDirect, true,
+   "Default namespace correctly detected through quickstubs");
+   
+// Document::isDefaultNamespace called via XPCOM
+delete(proto.isDefaultNamespace);
+var isDefaultNamespaceXPCOM = document.isDefaultNamespace("http://www.w3.org/1999/xhtml");
+is(isDefaultNamespaceXPCOM, true,
+   "Default namespace correctly detected through XPCOM");
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/base/test/test_xhr_progressevents.html
+++ b/content/base/test/test_xhr_progressevents.html
@@ -34,52 +34,60 @@ function updateProgress(e, data, testNam
   if (data.nodata) {
     is(e.target.response, null, "response should be null" + test);
     response = null;
   }
   else if (data.text) {
     is(typeof e.target.response, "string", "response should be a string" + test);
     response = e.target.response;
   }
+  else if (data.blob) {
+    ok(e.target.response instanceof Blob, "response should be a Blob" + test);
+    response = e.target.response;
+  }
   else {
-    ok(e.target.response instanceof ArrayBuffer, "response should be a ArrayBuffer" + test);
+    ok(e.target.response instanceof ArrayBuffer, "response should be an ArrayBuffer" + test);
     response = bufferToString(e.target.response);
   }
+  is(e.target.response, e.target.response, "reflexivity should hold" + test);
 
   if (!data.nodata && !data.encoded) {
-    if (!data.chunked) {
+    if (data.blob) {
+      is(e.loaded, response.size, "event.loaded matches response size" + test);
+    }
+    else if (!data.chunked) {
       is(e.loaded, response.length, "event.loaded matches response size" + test);
     }
     else {
       is(e.loaded - data.receivedBytes, response.length,
          "event.loaded grew by response size" + test);
     }
   }
   ok(e.loaded > data.receivedBytes, "event.loaded increased" + test);
   ok(e.loaded - data.receivedBytes <= data.pendingBytes,
      "event.loaded didn't increase too much" + test);
 
-  if (!data.nodata) {
+  if (!data.nodata && !data.blob) {
     var newData;
     ok(startsWith(response, data.receivedResult),
        "response strictly grew" + test);
     newData = response.substr(data.receivedResult.length);
   
     if (!data.encoded) {
       ok(newData.length > 0, "sanity check for progress" + test);
     }
     ok(startsWith(data.pendingResult, newData), "new data matches expected" + test);
   }
 
   is(e.lengthComputable, "total" in data, "lengthComputable" + test);
   if ("total" in data) {
     is(e.total, data.total, "total" + test);
   }
 
-  if (!data.nodata) {
+  if (!data.nodata && !data.blob) {
     data.pendingResult = data.pendingResult.substr(newData.length);
   }
   data.pendingBytes -= e.loaded - data.receivedBytes;
   data.receivedResult = response;
   data.receivedBytes = e.loaded;
 }
 
 function sendData(s) {
@@ -108,17 +116,18 @@ function bufferToString(buffer) {
 }
 
 function runTests() {
   var xhr = new XMLHttpRequest();
   xhr.onprogress = xhr.onload = xhr.onerror = xhr.onreadystatechange = xhr.onloadend = getEvent;
 
   var responseTypes = [{ type: "text", text: true },
                        { type: "arraybuffer", text: false, nodata: true },
-                       { type: "blob", text: false, nodata: true },
+                       { type: "blob", text: false, nodata: true, blob: true },
+                       { type: "moz-blob", text: false, nodata: false, blob: true },
                        { type: "document", text: true, nodata: true },
                        { type: "json", text: true, nodata: true },
                        { type: "", text: true },
                        { type: "moz-chunked-text", text: true, chunked: true },
                        { type: "moz-chunked-arraybuffer", text: false, chunked: true },
                       ];
   var responseType;
   var fileExpectedResult = "";
@@ -148,17 +157,17 @@ function runTests() {
                  { data: utf8encode("Å").substr(1), utf16: "Å" },
                  { data: utf8encode("aöb").substr(0,2), utf16: "a" },
                  { data: utf8encode("aöb").substr(2), utf16: "öb" },
                  { data: utf8encode("a\u867Eb").substr(0,3), utf16: "a" },
                  { data: utf8encode("a\u867Eb").substr(3,1), utf16: "\u867E" },
                  { data: utf8encode("a\u867Eb").substr(4), utf16: "b" },
                  { close: true },
                  ];
-    if (responseType.type === "blob") {
+    if (responseType.blob) {
       tests.push({ file: "file_XHR_binary2.bin", name: "cacheable data", total: 65536 },
                  { close: true },
                  { file: "file_XHR_binary2.bin", name: "cached data", total: 65536 },
                  { close: true });
     }
     let testState = { index: 0 };
 
     for (let i = 0; i < tests.length; ++i) {
@@ -172,16 +181,17 @@ function runTests() {
                       pendingBytes: 5,
                       receivedResult: "",
                       receivedBytes: 0,
                       total: test.total,
                       encoded: test.encoded,
                       nodata: responseType.nodata,
                       chunked: responseType.chunked,
                       text: responseType.text,
+                      blob: responseType.blob,
                       file: test.file };
   
         xhr.onreadystatechange = null;
         if (testState.file)
           xhr.open("GET", test.file);
         else
           xhr.open("POST", "progressserver.sjs?open&" + test.open);
         xhr.responseType = responseType.type;
@@ -230,25 +240,25 @@ function runTests() {
         is(e.type, "loadend", "should fire loadend closing " + testState.name);
         is(e.lengthComputable, true, "length should be computable during loadend closing " + testState.name);
         log("got loadend");
 
         if (responseType.chunked) {
           is(xhr.response, null, "chunked data has null response for " + testState.name);
         }
 
-        if (!testState.nodata || responseType.chunked) {
+        if (!testState.nodata && !responseType.blob || responseType.chunked) {
           // This branch intentionally left blank
           // Under these conditions we check the response during updateProgress
         }
         else if (responseType.type === "arraybuffer") {
           is(bufferToString(xhr.response), testState.pendingResult,
              "full response for " + testState.name);
         }
-        else if (responseType.type === "blob") {
+        else if (responseType.blob) {
           let reader = new FileReader;
           reader.readAsBinaryString(xhr.response);
           reader.onloadend = getEvent;
           yield;
 
           is(reader.result, testState.pendingResult,
              "full response in blob for " + testState.name);
         }
@@ -275,17 +285,17 @@ function runTests() {
           continue;
   
         updateProgress(e, testState, "data for " + testState.name + "[" + testState.index + "]");
         if (responseType.chunked) {
           testState.receivedResult = "";
         }
       }
 
-      if (!testState.nodata) {
+      if (!testState.nodata && !testState.blob) {
         is(testState.pendingResult, "",
            "should have consumed the expected result");
       }
 
       log("done with this test");
     }
   
     is(testState.name, "", "forgot to close last test");
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -990,17 +990,17 @@ NS_NewCanvasRenderingContext2DAzure(nsID
 {
 #ifdef XP_WIN
   if ((gfxWindowsPlatform::GetPlatform()->GetRenderMode() !=
       gfxWindowsPlatform::RENDER_DIRECT2D ||
       !gfxWindowsPlatform::GetPlatform()->DWriteEnabled()) &&
       !Preferences::GetBool("gfx.canvas.azure.prefer-skia", false)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-#elif !defined(XP_MACOSX) && !defined(ANDROID) && !defined(XP_LINUX)
+#elif !defined(XP_MACOSX) && !defined(ANDROID) && !defined(LINUX)
   return NS_ERROR_NOT_AVAILABLE;
 #endif
 
   nsRefPtr<nsIDOMCanvasRenderingContext2D> ctx = new nsCanvasRenderingContext2DAzure();
   if (!ctx)
     return NS_ERROR_OUT_OF_MEMORY;
 
   *aResult = ctx.forget().get();
--- a/content/canvas/test/webgl/README.mozilla
+++ b/content/canvas/test/webgl/README.mozilla
@@ -1,9 +1,9 @@
-This is a local copy of the WebGL conformance suite, SVN revision 16456
+This is a local copy of the WebGL conformance suite, SVN revision 16776
 
 The canonical location for this testsuite is:
 
   https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/tests
 
 All files and directories in this directory, with the exceptions listed below, come from
 upstream and should not be modified without corresponding upstream fixes and/or a
 patch file in this directory. The exceptions (the Mozilla-specific files) are:
--- a/content/canvas/test/webgl/conformance/buffers/buffer-data-array-buffer.html
+++ b/content/canvas/test/webgl/conformance/buffers/buffer-data-array-buffer.html
@@ -22,17 +22,17 @@ debug('Regression test for <a href="http
 
 var gl = create3DContext();
 shouldBeNonNull("gl");
 
 var array = new ArrayBuffer(128);
 shouldBeNonNull("array");
 
 var buf = gl.createBuffer();
-shouldBeNonNull(buf);
+shouldBeNonNull("buf");
 
 gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
 glErrorShouldBe(gl, gl.INVALID_OPERATION);
 
 gl.bindBuffer(gl.ARRAY_BUFFER, buf);
 glErrorShouldBe(gl, gl.NO_ERROR);
 
 gl.bufferData(gl.ARRAY_BUFFER, -10, gl.STATIC_DRAW);
--- a/content/canvas/test/webgl/conformance/canvas/drawingbuffer-static-canvas-test.html
+++ b/content/canvas/test/webgl/conformance/canvas/drawingbuffer-static-canvas-test.html
@@ -97,17 +97,17 @@ var maxSize;
 var canvas = document.getElementById("canvas");
 var gl =  wtu.create3DContext(canvas);
 if (!gl) {
   testFailed("context does not exist");
 } else {
   testPassed("context exists");
 
   gl.program = createProgram(gl, "vshader", "fshader", ["vPosition"]);
-  shouldBeNonNull(gl.program);
+  shouldBeNonNull("gl.program");
   gl.useProgram(gl.program);
   gl.enable(gl.DEPTH_TEST);
   gl.disable(gl.BLEND);
   gl.clearColor(0, 0, 0, 1);
   gl.clearDepth(1);
   shouldBe('gl.getError()', 'gl.NO_ERROR');
 
   debug("");
--- a/content/canvas/test/webgl/conformance/canvas/drawingbuffer-test.html
+++ b/content/canvas/test/webgl/conformance/canvas/drawingbuffer-test.html
@@ -96,17 +96,17 @@ var maxSize;
 var canvas = document.createElement("canvas");
 var gl = create3DContext(canvas);
 if (!gl) {
   testFailed("context does not exist");
 } else {
   testPassed("context exists");
 
   gl.program = createProgram(gl, "vshader", "fshader", ["vPosition"]);
-  shouldBeNonNull(gl.program);
+  shouldBeNonNull("gl.program");
   gl.useProgram(gl.program);
   gl.enable(gl.DEPTH_TEST);
   gl.disable(gl.BLEND);
   gl.clearColor(0, 0, 0, 1);
   gl.clearDepth(1);
   shouldBe('gl.getError()', 'gl.NO_ERROR');
 
   debug("");
--- a/content/canvas/test/webgl/conformance/context/context-lost-restored.html
+++ b/content/canvas/test/webgl/conformance/context/context-lost-restored.html
@@ -1,12 +1,11 @@
 <!DOCTYPE html>
 <html>
 <head>
-<meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../resources/js-test-pre.js"></script>
 <script src="../resources/webgl-test.js"></script>
 <script src="../resources/webgl-test-utils.js"></script>
 <script>
 var wtu = WebGLTestUtils;
 var canvas;
 var gl;
@@ -63,17 +62,17 @@ function testLosingContext()
 
     canvas.addEventListener("webglcontextlost", function(e) {
        testLostContext(e);
        // restore the context after this event has exited.
        setTimeout(function() {
          // we didn't call prevent default so we should not be able to restore the context
          shouldGenerateGLError(gl, gl.INVALID_OPERATION, "extension.restoreContext()");
          testLosingAndRestoringContext();
-       }, 1);
+       }, 0);
     });
     canvas.addEventListener("webglcontextrestored", testShouldNotRestoreContext);
     allowRestore = false;
     contextLostEventFired = false;
     contextRestoredEventFired = false;
 
     testOriginalContext();
     extension.loseContext();
@@ -101,17 +100,17 @@ function testLosingAndRestoringContext()
       setTimeout(function() {
         shouldGenerateGLError(gl, gl.NO_ERROR, "extension.restoreContext()");
         // The context should still be lost. It will not get restored until the 
         // webglrestorecontext event is fired.
         shouldBeTrue("gl.isContextLost()");
         shouldBe("gl.getError()", "gl.NO_ERROR");
         // gl methods should still be no-ops
         shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)");
-      }, 1);
+      }, 0);
     });
     canvas.addEventListener("webglcontextrestored", function() {
       testRestoredContext();
       finishTest();
     });
     allowRestore = true;
     contextLostEventFired = false;
     contextRestoredEventFired = false;
@@ -159,17 +158,17 @@ function testOriginalContext()
 function testLostContext(e)
 {
     debug("Test lost context");
     shouldBeFalse("contextLostEventFired");
     contextLostEventFired = true;
     shouldBeTrue("gl.isContextLost()");
     shouldBe("gl.getError()", "gl.NO_ERROR");
     debug("");
-    if (allowRestore) 
+    if (allowRestore)
       e.preventDefault();
 }
 
 function testShouldNotRestoreContext(e)
 {
     testFailed("Should not restore the context unless preventDefault is called on the context lost event");
     debug("");
 }
--- a/content/canvas/test/webgl/conformance/context/context-lost.html
+++ b/content/canvas/test/webgl/conformance/context/context-lost.html
@@ -1,12 +1,11 @@
 <!DOCTYPE html>
 <html>
 <head>
-<meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../resources/js-test-pre.js"></script>
 <script src="../resources/webgl-test.js"></script>
 <script src="../resources/webgl-test-utils.js"></script>
 <script>
 var wtu;
 var canvas;
 var gl;
--- a/content/canvas/test/webgl/conformance/extensions/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/extensions/00_test_list.txt
@@ -1,8 +1,7 @@
 oes-standard-derivatives.html
 oes-texture-float.html
 oes-vertex-array-object.html
 webgl-debug-renderer-info.html
 webgl-debug-shaders.html
-# commented out until 1.0.1 cut
-# webgl-experimental-compressed-textures.html
+--min-version 1.0.2 webgl-experimental-compressed-textures.html
 
--- a/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html
+++ b/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html
@@ -52,17 +52,25 @@ precision mediump float;
 varying vec2 texCoord;
 void main() {
     float dx = dFdx(texCoord.x);
     float dy = dFdy(texCoord.y);
     float w = fwidth(texCoord.x);
     gl_FragColor = vec4(dx, dy, w, 1.0);
 }
 </script>
-
+<!-- Shaders to link with test fragment shaders -->
+<script id="goodVertexShader" type="x-shader/x-vertex">
+attribute vec4 vPosition;
+varying vec2 texCoord;
+void main() {
+    texCoord = vPosition.xy;
+    gl_Position = vPosition;
+}
+</script>
 <!-- Shaders to test output -->
 <script id="outputVertexShader" type="x-shader/x-vertex">
 attribute vec4 vPosition;
 varying vec4 position;
 void main() {
     position = vPosition;
     gl_Position = vPosition;
 }
@@ -178,53 +186,54 @@ function runHintTestEnabled() {
         }
     }
     if (!anyFailed) {
         testPassed("Round-trip of hint()/getParameter() with all supported modes");
     }
 }
 
 function runShaderTests(extensionEnabled) {
+    debug("");
     debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
 
     // Expect the macro shader to succeed ONLY if enabled
-    var macroFragmentShader = wtu.loadShaderFromScript(gl, "macroFragmentShader");
+    var macroFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "macroFragmentShader");
     if (extensionEnabled) {
-        if (macroFragmentShader) {
+        if (macroFragmentProgram) {
             // Expected result
             testPassed("GL_OES_standard_derivatives defined in shaders when extension is enabled");
         } else {
             testFailed("GL_OES_standard_derivatives not defined in shaders when extension is enabled");
         }
     } else {
-        if (macroFragmentShader) {
+        if (macroFragmentProgram) {
             testFailed("GL_OES_standard_derivatives defined in shaders when extension is disabled");
         } else {
             testPassed("GL_OES_standard_derivatives not defined in shaders when extension disabled");
         }
     }
 
     // Always expect the shader missing the #pragma to fail (whether enabled or not)
-    var missingPragmaFragmentShader = wtu.loadShaderFromScript(gl, "missingPragmaFragmentShader");
-    if (missingPragmaFragmentShader) {
+    var missingPragmaFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "missingPragmaFragmentShader");
+    if (missingPragmaFragmentProgram) {
         testFailed("Shader built-ins allowed without #extension pragma");
     } else {
         testPassed("Shader built-ins disallowed without #extension pragma");
     }
 
     // Try to compile a shader using the built-ins that should only succeed if enabled
-    var testFragmentShader = wtu.loadShaderFromScript(gl, "testFragmentShader");
+    var testFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShader");
     if (extensionEnabled) {
-        if (testFragmentShader) {
+        if (testFragmentProgram) {
             testPassed("Shader built-ins compiled successfully when extension enabled");
         } else {
             testFailed("Shader built-ins failed to compile when extension enabled");
         }
     } else {
-        if (testFragmentShader) {
+        if (testFragmentProgram) {
             testFailed("Shader built-ins compiled successfully when extension disabled");
         } else {
             testPassed("Shader built-ins failed to compile when extension disabled");
         }
     }
 }
 
 function runOutputTests() {
--- a/content/canvas/test/webgl/conformance/glsl/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/glsl/00_test_list.txt
@@ -1,10 +1,8 @@
 functions/00_test_list.txt
 implicit/00_test_list.txt
-# commented out for version 1.0.1 of the conforamnce tests.
-#matrices/00_test_list.txt
+--min-version 1.0.2 matrices/00_test_list.txt
 misc/00_test_list.txt
 reserved/00_test_list.txt
-# commented out for version 1.0.1 of the conforamnce tests.
-#samplers/00_test_list.txt
+--min-version 1.0.2 samplers/00_test_list.txt
 variables/00_test_list.txt
 
--- a/content/canvas/test/webgl/conformance/glsl/misc/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/glsl/misc/00_test_list.txt
@@ -1,55 +1,67 @@
 attrib-location-length-limits.html
 embedded-struct-definitions-forbidden.html
-#glsl-2types-of-textures-on-same-unit.html
+# this test is intentionally disabled as it is too strict and to hard to simulate
+# glsl-2types-of-textures-on-same-unit.html
 glsl-function-nodes.html
 glsl-long-variable-names.html
 non-ascii-comments.vert.html
 non-ascii.vert.html
+
 shader-with-256-character-identifier.frag.html
 shader-with-257-character-identifier.frag.html
 shader-with-_webgl-identifier.vert.html
 shader-with-arbitrary-indexing.frag.html
 shader-with-arbitrary-indexing.vert.html
 shader-with-attrib-array.vert.html
 shader-with-attrib-struct.vert.html
 shader-with-clipvertex.vert.html
+--min-version 1.0.2 shader-with-conditional-scoping.html
 shader-with-default-precision.frag.html
 shader-with-default-precision.vert.html
 shader-with-define-line-continuation.frag.html
 shader-with-dfdx-no-ext.frag.html
 shader-with-dfdx.frag.html
+--min-version 1.0.2 shader-with-do-scoping.html
 shader-with-error-directive.html
 shader-with-explicit-int-cast.vert.html
 shader-with-float-return-value.frag.html
+--min-version 1.0.2 shader-with-for-scoping.html
+--min-version 1.0.2 shader-with-for-loop.html
 shader-with-frag-depth.frag.html
 shader-with-function-recursion.frag.html
+--min-version 1.0.2 shader-with-function-scoped-struct.html
+--min-version 1.0.2 shader-with-functional-scoping.html
 shader-with-glcolor.vert.html
 shader-with-gles-1.frag.html
 shader-with-gles-symbol.frag.html
 shader-with-glprojectionmatrix.vert.html
 shader-with-implicit-vec3-to-vec4-cast.vert.html
 shader-with-include.vert.html
 shader-with-int-return-value.frag.html
 shader-with-invalid-identifier.frag.html
 shader-with-ivec2-return-value.frag.html
 shader-with-ivec3-return-value.frag.html
 shader-with-ivec4-return-value.frag.html
 shader-with-limited-indexing.frag.html
-shader-with-line-directive.html
+# we can not check line directives because GLSL 1.0.17 says error messages
+# are implementation defined.
+#shader-with-line-directive.html
+--min-version 1.0.2 shader-with-hex-int-constant-macro.html
 shader-with-long-line.html
 shader-with-non-ascii-error.frag.html
 shader-with-precision.frag.html
 shader-with-quoted-error.frag.html
 shader-with-undefined-preprocessor-symbol.frag.html
 shader-with-uniform-in-loop-condition.vert.html
 shader-with-vec2-return-value.frag.html
 shader-with-vec3-return-value.frag.html
 shader-with-vec4-return-value.frag.html
+--min-version 1.0.2 shader-with-vec4-vec3-vec4-conditional.html
 shader-with-version-100.frag.html
 shader-with-version-100.vert.html
 shader-with-version-120.vert.html
 shader-with-version-130.vert.html
 shader-with-webgl-identifier.vert.html
 shader-without-precision.frag.html
 shared.html
 struct-nesting-exceeds-maximum.html
--- a/content/canvas/test/webgl/conformance/glsl/misc/attrib-location-length-limits.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/attrib-location-length-limits.html
@@ -55,18 +55,18 @@ var attribLoc = gl.getAttribLocation(pro
 if (attribLoc == -1) {
     testFailed("attrib location was -1, should not be");
 } else {
     testPassed("attrib location should not be -1");
 }
 wtu.glErrorShouldBe(gl, gl.NONE);
 
 debug("Test attrib location over the length limit");
-debug("Shader compilation should fail");
-shouldBe('wtu.loadShaderFromScript(gl, "badVertexShader", gl.VERTEX_SHADER, function (err) {})', 'null');
+debug("Shader compilation or link should fail");
+shouldBe('wtu.loadProgramFromScriptExpectError(gl, "badVertexShader", "fragmentShader")', 'null');
 wtu.glErrorShouldBe(gl, gl.NONE);
 
 debug("Attempt to bind too-long attrib location should produce error");
 program = gl.createProgram();
 gl.bindAttribLocation(program, 0, "vPosition01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567");
 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
 
 debug("Attempt to fetch too-long attrib location should produce error");
--- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-long-variable-names.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-long-variable-names.html
@@ -8,125 +8,242 @@
     <script src="../../resources/webgl-test.js"> </script>
 </head>
 <body>
     <canvas id="example" width="50" height="50">
     There is supposed to be an example drawing here, but it's not important.
     </canvas>
     <div id="description"></div>
     <div id="console"></div>
-    <script id="vshader" type="x-shader/x-vertex">
-        attribute vec4 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
-        varying float alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
+    <script id="vshader_shared_uniform" type="x-shader/x-vertex">
+        attribute vec3 vPosition;
+        uniform float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
         void main()
         {
-            alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1.0;
-            gl_Position = vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
+            gl_Position = vec4(vPosition, value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890);
+        }
+    </script>
+
+    <script id="fshader_shared_uniform" type="x-shader/x-fragment">
+        precision mediump float;
+        uniform float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
+        void main()
+        {
+            gl_FragColor = vec4(1.0, 0.0, value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, 1.0);
+        }
+    </script>
+
+    <script id="vshader_uniform_array" type="x-shader/x-vertex">
+        attribute vec3 vPosition;
+        void main()
+        {
+            gl_Position = vec4(vPosition, 1.0);
         }
     </script>
 
-    <script id="fshader" type="x-shader/x-fragment">
+    <script id="fshader_uniform_array" type="x-shader/x-fragment">
+        precision mediump float;
+        uniform float color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[2];
+        void main()
+        {
+            gl_FragColor = vec4(color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0], color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1], 1.0, 1.0);
+        }
+    </script>
+
+    <script id="vshader_varying" type="x-shader/x-vertex">
+        attribute vec3 vPosition;
+        varying float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
+        void main()
+        {
+            value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1.0;
+            gl_Position = vec4(vPosition, 1.0);
+        }
+    </script>
+
+    <script id="fshader_varying" type="x-shader/x-fragment">
         precision mediump float;
-        varying float alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
-        uniform float color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[3];
+        varying float value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890;
+        void main()
+        {
+            gl_FragColor = vec4(value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, 0.0, 1.0, 1.0);
+        }
+    </script>
+
+    <script id="vshader_local" type="x-shader/x-vertex">
+        attribute vec3 vPosition;
+        void main()
+        {
+            for (int i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 = 0; i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 < 1; ++i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234)
+            {
+                gl_Position = vec4(vPosition, 1.0);
+            }
+        }
+    </script>
+
+    <script id="fshader_local" type="x-shader/x-fragment">
+        precision mediump float;
         void main()
         {
             for (int i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 = 0; i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 < 1; ++i012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234)
             {
-                gl_FragColor = vec4(color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0], color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1], color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[2], alpha01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890);
+                gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
             }
         }
     </script>
 
+    <script id="vshader_attrib" type="x-shader/x-vertex">
+        attribute vec3 vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456;
+        void main()
+        {
+            gl_Position = vec4(vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456, 1.0);
+        }
+    </script>
+
+    <script id="fshader_attrib" type="x-shader/x-fragment">
+        precision mediump float;
+        void main()
+        {
+            gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
+        }
+    </script>
+
     <script>
-        function fail(x,y, buf, shouldBe)
+        if (window.initNonKhronosFramework) {
+            window.initNonKhronosFramework(false);
+        }
+
+        description("Verify that shader long variable names works fine if they are within 256 characters.");
+
+        debug("Test same long uniform name in both vertex shader and fragment shader");
+        var gl = initWebGL("example", "vshader_shared_uniform", "fshader_shared_uniform", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
+        shouldBeNonNull("gl");
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        var prog = gl.getParameter(gl.CURRENT_PROGRAM);
+        shouldBeNonNull("prog");
+        var valueLoc = gl.getUniformLocation(prog, "value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
+        shouldBeNonNull("valueLoc");
+        shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_UNIFORMS)", "1");
+        var activeUniform = gl.getActiveUniform(prog, 0);
+        shouldBeNonNull("activeUniform");
+        shouldBe("activeUniform.type", "gl.FLOAT");
+        shouldBe("activeUniform.size", "1");
+        shouldBe("activeUniform.name", "'value01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'");
+        gl.uniform1f(valueLoc, 1.0);
+        drawAndCheckPixels(gl);
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        debug("");
+
+        debug("Test long uniform array name");
+        var gl = initWebGL("example", "vshader_uniform_array", "fshader_uniform_array", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
+        shouldBeNonNull("gl");
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        var prog = gl.getParameter(gl.CURRENT_PROGRAM);
+        shouldBeNonNull("prog");
+        var redLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]");
+        shouldBeNonNull("redLoc");
+        var greenLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1]");
+        shouldBeNonNull("greenLoc");
+        shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_UNIFORMS)", "1");
+        var activeUniform = gl.getActiveUniform(prog, 0);
+        shouldBeNonNull("activeUniform");
+        shouldBe("activeUniform.type", "gl.FLOAT");
+        shouldBe("activeUniform.size", "2");
+        shouldBe("activeUniform.name", "'color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]'");
+        gl.uniform1f(redLoc, 1.0);
+        gl.uniform1f(greenLoc, 0.0);
+        drawAndCheckPixels(gl);
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        debug("");
+
+        debug("Test long varying name");
+        var gl = initWebGL("example", "vshader_varying", "fshader_varying", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
+        shouldBeNonNull("gl");
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        var prog = gl.getParameter(gl.CURRENT_PROGRAM);
+        shouldBeNonNull("prog");
+        drawAndCheckPixels(gl);
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        debug("");
+
+        debug("Test long local variable name");
+        var gl = initWebGL("example", "vshader_varying", "fshader_varying", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
+        shouldBeNonNull("gl");
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        var prog = gl.getParameter(gl.CURRENT_PROGRAM);
+        shouldBeNonNull("prog");
+        drawAndCheckPixels(gl);
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        debug("");
+
+        debug("Test long attribute name");
+        var gl = initWebGL("example", "vshader_attrib", "fshader_attrib", [ "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"], [ 0, 0, 0, 1 ], 1);
+        shouldBeNonNull("gl");
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        var prog = gl.getParameter(gl.CURRENT_PROGRAM);
+        shouldBeNonNull("prog");
+        shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_ATTRIBUTES)", "1");
+        var activeAttrib = gl.getActiveAttrib(prog, 0);
+        shouldBeNonNull("activeAttrib");
+        shouldBe("activeAttrib.size", "1");
+        shouldBe("activeAttrib.type", "gl.FLOAT_VEC3");
+        shouldBe("activeAttrib.name", "'vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456'");
+        drawAndCheckPixels(gl);
+        shouldBe("gl.getError()", "gl.NO_ERROR");
+        debug("");
+
+
+        function fail(x, y, buf, shouldBe)
         {
             var i = (y*50+x) * 4;
             var reason = "pixel at ("+x+","+y+") is ("+buf[i]+","+buf[i+1]+","+buf[i+2]+","+buf[i+3]+"), should be "+shouldBe;
             testFailed(reason);
         }
 
         function pass()
         {
             testPassed("drawing is correct");
         }
 
-        if (window.initNonKhronosFramework) {
-            window.initNonKhronosFramework(false);
-        }
-
-        description("Verify that shader long variable names works fine if they are within 256 characters.");
-
-        gl = initWebGL("example", "vshader", "fshader", [ "vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"], [ 0, 0, 0, 1 ], 1);
-
-        var prog = gl.getParameter(gl.CURRENT_PROGRAM);
-        shouldBeNonNull(prog);
-        var redLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]");
-        shouldBeNonNull(redLoc);
-        var greenLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[1]");
-        shouldBeNonNull(greenLoc);
-        var blueLoc = gl.getUniformLocation(prog, "color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[2]");
-        shouldBeNonNull(blueLoc);
-
-        shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_ATTRIBUTES)", "1");
-        var activeAttrib = gl.getActiveAttrib(prog, 0);
-        shouldBeNonNull(activeAttrib);
-        shouldBe("activeAttrib.size", "1");
-        shouldBe("activeAttrib.type", "gl.FLOAT_VEC4");
-        shouldBe("activeAttrib.name", "'vPosition0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456'");
+        function drawAndCheckPixels(gl)
+        {
+            var vertexObject = gl.createBuffer();
+            gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
+            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW);
+            gl.enableVertexAttribArray(0);
+            gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
 
-        shouldBe("gl.getProgramParameter(prog, gl.ACTIVE_UNIFORMS)", "1");
-        var activeUniform = gl.getActiveUniform(prog, 0);
-        shouldBeNonNull(activeUniform);
-        shouldBe("activeUniform.size", "3");
-        shouldBe("activeUniform.type", "gl.FLOAT");
-        shouldBe("activeUniform.name", "'color01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567[0]'");
-
-        gl.uniform1f(redLoc, 1.0); 
-        gl.uniform1f(greenLoc, 0.0);
-        gl.uniform1f(blueLoc, 1.0);
+            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+            gl.drawArrays(gl.TRIANGLES, 0, 3);
 
-        var vertexObject = gl.createBuffer();
-        gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
-        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW);
-        gl.enableVertexAttribArray(0);
-        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
+            var buf = new Uint8Array(50 * 50 * 4);
+            gl.readPixels(0, 0, 50, 50, gl.RGBA, gl.UNSIGNED_BYTE, buf);
 
-        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-        gl.drawArrays(gl.TRIANGLES, 0, 3);
-
-        var buf = new Uint8Array(50 * 50 * 4);
-        gl.readPixels(0, 0, 50, 50, gl.RGBA, gl.UNSIGNED_BYTE, buf);
-        shouldBe("gl.getError()", "gl.NO_ERROR");
-
-        function checkPixels()
-        {
             // Test several locations
             // First line should be all black
             for (var i = 0; i < 50; ++i)
                 if (buf[i*4] != 0 || buf[i*4+1] != 0 || buf[i*4+2] != 0 || buf[i*4+3] != 255) {
                     fail(i, 0, buf, "(0,0,0,255)");
                     return;
                 }
 
-            // Line 15 should be red for at least 10 red pixels starting 20 pixels in
+            // Line 15 should be magenta for at least 10 pixels starting 20 pixels in
             var offset = (15*50+20) * 4;
             for (var i = 0; i < 10; ++i)
                 if (buf[offset+i*4] != 255 || buf[offset+i*4+1] != 0 || buf[offset+i*4+2] != 255 || buf[offset+i*4+3] != 255) {
                     fail(20 + i, 15, buf, "(255,0,255,255)");
                     return;
                 }
             // Last line should be all black
             offset = (49*50) * 4;
             for (var i = 0; i < 50; ++i)
                 if (buf[offset+i*4] != 0 || buf[offset+i*4+1] != 0 || buf[offset+i*4+2] != 0 || buf[offset+i*4+3] != 255) {
                     fail(i, 49, buf, "(0,0,0,255)");
                     return;
                 }
 
             pass();
         }
-        checkPixels();
+
         successfullyParsed = true;
     </script>
     <script src="../../../resources/js-test-post.js"></script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-conditional-scoping.html
@@ -0,0 +1,47 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader with conditional scoping should succeed
+precision mediump float;
+void main() {
+  int k = 3;
+
+  if (true) int g = k = 4;
+  else int q = k = 5;
+
+  g = 3;
+  q = 4;
+
+  if (true) int g = 4;
+  else int k = 10;
+
+  if (true) { int g = 10; }
+  else { int k = 20; }
+
+  gl_FragColor = vec4(1.);
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-do-scoping.html
@@ -0,0 +1,35 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader with do scoping should succeed
+precision mediump float;
+void main() {
+  int k = 0;
+  do int k = 1; while (k != 0); // ok, do always introduces scope
+  gl_FragColor = vec4(float(k));
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
--- a/content/canvas/test/webgl/conformance/glsl/misc/shader-with-error-directive.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-error-directive.html
@@ -28,20 +28,22 @@ void main()
 description("Checks shader with error directive");
 
 var wtu = WebGLTestUtils;
 GLSLConformanceTester.runTests([
 { vShaderId: undefined,
   vShaderSuccess: true,
   fShaderId: 'fshaderWithErrorDirective',
   fShaderSuccess: false,
-  fShaderTest: (function() {
-    return wtu.getLastError().indexOf("testing123 testing123") >= 0; }),
+  // We can't test for the actual error message as
+  // GLSL 1.0.17 11 says the messages are implementation dependant.
+  //fShaderTest: (function() {
+  //  return wtu.getLastError().indexOf("testing123 testing123") >= 0; }),
   linkSuccess: false,
-  passMsg: "error directive returns error user's error message",
+  passMsg: "error directive causes error",
 },
 ]);
 
 debug("");
 successfullyParsed = true;
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-for-loop.html
@@ -0,0 +1,81 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader with for loop should succeed
+
+// TODO(gman): trim to min size to test bug.
+precision highp float;
+uniform float time;
+uniform vec2 resolution;
+
+// Saw-tooth function that is synced with the demo music (128bpm)
+float gBeat=fract(time*3.2/3.);
+
+// Calculate the surface color
+vec3 surfColor(vec2 p)
+{
+    vec2 q=vec2(sin(.08*p.x),4.*p.y);
+    vec3 c=vec3(0);
+    for(float i=0.;i<15.;i++)
+      c+=(1.+sin(i*sin(time)+vec3(0.,1.3,2.2)))*.2/length(q-vec2(sin(i),12.*sin(.3*time+i)));
+    return c+vec3(mix(mod(floor(p.x*.2)+floor(p.y*2.2),2.),.2,gBeat));
+}
+
+// Ray trace (cylinder)
+vec3 trace(vec3 o,vec3 d)
+{
+    d.y*=.65+.1*sin(.5*time);
+    float D=1./(d.y*d.y+d.z*d.z),
+          a=(o.y*d.y+o.z*d.z)*D,
+          b=(o.y*o.y+o.z*o.z-36.)*D,
+          t=-a-sqrt(a*a-b);
+    o+=t*d;
+    return surfColor(vec2(o.x,atan(o.y,o.z)))*(1.+.01*t);
+}
+
+void main()
+{
+    // Screen setup
+    vec2 p=(2.*gl_FragCoord.xy-resolution)/resolution.y,
+         q=2.*gl_FragCoord.xy/resolution-1.;
+
+    // Camera setup
+    vec3 cp=vec3(-time*20.+1.,1.6*sin(time*1.2),2.+2.*cos(time*.3)),
+         ct=cp+vec3(1.,.3*cos(time),-.2),
+         cd=normalize(ct-cp),
+         cr=normalize(cross(cd,vec3(.5*cos(.3*time),0.,1.))),
+         cu=cross(cr,cd),
+         rd=normalize(2.*cd+cr*p.x+cu*p.y);
+
+    // Trace! (+some funky lens/raster effects)
+    vec3 c=trace(cp,rd)*
+           min(1.,1.8-dot(q,q))*
+           (.9+.1*sin(3.*sin(gBeat)*gl_FragCoord.y));
+
+    gl_FragColor=vec4(c,1);
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-for-scoping.html
@@ -0,0 +1,35 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader with for scoping should succeed
+precision mediump float;
+void main() {
+  int k = 0;
+  for (int i = 0; i < 10; i++) { int i = k+i; } // ok, compound nests
+  gl_FragColor = vec4(float(k));
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-function-scoped-struct.html
@@ -0,0 +1,39 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader with private function scoped struct should fail.
+precision mediump float;
+int fun2(struct s { int m; } g) { return g.m; }
+
+s a;
+
+void main() {
+  int e = fun2(s(3));
+
+  gl_FragColor = vec4(1.0);
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-functional-scoping.html
@@ -0,0 +1,38 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader with functional scoping should succeed
+precision mediump float;
+int f(int k) {
+  int k = k + 3;
+  return k;
+}
+
+void main() {
+  gl_FragColor = vec4(f(100));
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-hex-int-constant-macro.html
@@ -0,0 +1,37 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="vertexShader" type="text/something-not-javascript">
+// vertex shader uses the long integer constant should succeed
+attribute vec4 vPosition;
+void main()
+{
+    #define TEST 0x1F
+    int a = TEST;
+
+    gl_Position = vPosition;
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/glsl/misc/shader-with-vec4-vec3-vec4-conditional.html
@@ -0,0 +1,35 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL GLSL Conformance Tests</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css"/>
+<script src="../../../resources/js-test-pre.js"></script>
+<script src="../../resources/webgl-test-utils.js"></script>
+<script src="../../resources/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script id="fragmentShader" type="text/something-not-javascript">
+// fragment shader that vec4->vec3->vec4 conditional should succeed
+precision mediump float;
+uniform float x;
+void main()
+{
+  gl_FragColor = vec4((x > 0.0 ? vec4(1.0, 1.0, 1.0, 0.0) : vec4(0.1, 0.1, 0.1, 0.0)).xyz, 1.0);
+}
+</script>
+<script>
+GLSLConformanceTester.runTest();
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
+
--- a/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-exceeds-maximum.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-exceeds-maximum.html
@@ -13,18 +13,22 @@ found in the LICENSE file.
 <script src="../../resources/webgl-test-utils.js"></script>
 <script src="../../resources/glsl-conformance-test.js"></script>
 </head>
 <body>
 <div id="description"></div>
 <div id="console"></div>
 <script id="vertexShader" type="text/something-not-javascript">
 // shader with too-deep struct nesting should fail per WebGL spec
+struct nesting5 {
+  vec4 vector;
+};
+
 struct nesting4 {
-  vec4 vector;
+  nesting5 field5;
 };
 
 struct nesting3 {
   nesting4 field4;
 };
 
 struct nesting2 {
   nesting3 field3;
@@ -32,17 +36,17 @@ struct nesting2 {
 
 struct nesting1 {
   nesting2 field2;
 };
 
 uniform nesting1 uniform1;
 void main()
 {
-    gl_Position = uniform1.field2.field3.field4.vector;
+    gl_Position = uniform1.field2.field3.field4.field5.vector;
 }
 </script>
 <script>
 GLSLConformanceTester.runTest();
 successfullyParsed = true;
 </script>
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-under-maximum.html
+++ b/content/canvas/test/webgl/conformance/glsl/misc/struct-nesting-under-maximum.html
@@ -13,32 +13,36 @@ found in the LICENSE file.
 <script src="../../resources/webgl-test-utils.js"></script>
 <script src="../../resources/glsl-conformance-test.js"></script>
 </head>
 <body>
 <div id="description"></div>
 <div id="console"></div>
 <script id="vertexShader" type="text/something-not-javascript">
 // shader with struct nesting less than maximum in WebGL spec should succeed
+struct nesting4 {
+  vec4 vector;
+};
+
 struct nesting3 {
-  vec4 vector;
+  nesting4 field4;
 };
 
 struct nesting2 {
   nesting3 field3;
 };
 
 struct nesting1 {
   nesting2 field2;
 };
 
 uniform nesting1 uniform1;
 void main()
 {
-    gl_Position = uniform1.field2.field3.vector;
+    gl_Position = uniform1.field2.field3.field4.vector;
 }
 </script>
 <script>
 GLSLConformanceTester.runTest();
 successfullyParsed = true;
 </script>
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/glsl/variables/gl-pointcoord.html
+++ b/content/canvas/test/webgl/conformance/glsl/variables/gl-pointcoord.html
@@ -36,106 +36,106 @@ found in the LICENSE file.
         gl_PointCoord.x,
         gl_PointCoord.y,
         0,
         1);
     }
   </script>
 
   <script>
-    function init()
-    {
-      if (window.initNonKhronosFramework) {
-        window.initNonKhronosFramework(false);
-      }
+    if (window.initNonKhronosFramework) {
+      window.initNonKhronosFramework(false);
+    }
+
+    description("Checks gl_PointCoord and gl_PointSize");
+    debug("");
 
-      description("Checks gl_PointCoord and gl_PointSize");
-      debug("");
+    // NOTE: I'm not 100% confident in this test. I think it is correct.
 
-      // NOTE: I'm not 100% confident in this test. I think it is correct.
-
-      wtu = WebGLTestUtils;
-      gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
+    wtu = WebGLTestUtils;
+    var gl = initWebGL("example", "vshader", "fshader", [ "vPosition"], [ 0, 0, 0, 1 ], 1);
+    shouldBeNonNull("gl");
+    shouldBe("gl.getError()", "gl.NO_ERROR");
 
-      canvas = gl.canvas;
-      width = canvas.width;
-      height = canvas.height;
-      shouldBeTrue("width == height");
+    var canvas = gl.canvas;
+    var width = canvas.width;
+    var height = canvas.height;
+    shouldBe("width", "height");
 
-      maxPointSize = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1];
-      shouldBeTrue("maxPointSize >= 1");
-      shouldBeTrue("maxPointSize % 1 == 0");
+    var maxPointSize = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1];
+    shouldBeTrue("maxPointSize >= 1");
+    shouldBeTrue("maxPointSize % 1 == 0");
 
-      maxPointSize = Math.min(maxPointSize, 64);
-      pointWidth = maxPointSize / width;
-      pointStep = Math.floor(maxPointSize / 4);
-      pointStep = Math.max(1, pointStep);
+    maxPointSize = Math.min(maxPointSize, 64);
+    var pointWidth = maxPointSize / width;
+    var pointStep = Math.floor(maxPointSize / 4);
+    var pointStep = Math.max(1, pointStep);
 
-      program = gl.program;
-      pointSizeLoc = gl.getUniformLocation(program, "uPointSize");
-      gl.uniform1f(pointSizeLoc, maxPointSize);
+    var program = gl.program;
+    var pointSizeLoc = gl.getUniformLocation(program, "uPointSize");
+    gl.uniform1f(pointSizeLoc, maxPointSize);
 
-      var pixelOffset = (maxPointSize % 2) ? (1 / width) : 0;
-      var vertexObject = gl.createBuffer();
-      gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
-      gl.bufferData(
-          gl.ARRAY_BUFFER,
-          new Float32Array(
-            [-0.5 + pixelOffset, -0.5 + pixelOffset,
-              0.5 + pixelOffset, -0.5 + pixelOffset,
-             -0.5 + pixelOffset,  0.5 + pixelOffset,
-              0.5 + pixelOffset,  0.5 + pixelOffset]),
-          gl.STATIC_DRAW);
-      gl.enableVertexAttribArray(0);
-      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+    var pixelOffset = (maxPointSize % 2) ? (1 / width) : 0;
+    var vertexObject = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
+    gl.bufferData(
+        gl.ARRAY_BUFFER,
+        new Float32Array(
+          [-0.5 + pixelOffset, -0.5 + pixelOffset,
+            0.5 + pixelOffset, -0.5 + pixelOffset,
+           -0.5 + pixelOffset,  0.5 + pixelOffset,
+            0.5 + pixelOffset,  0.5 + pixelOffset]),
+        gl.STATIC_DRAW);
+    gl.enableVertexAttribArray(0);
+    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
 
-      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
-      gl.drawArrays(gl.POINTS, 0, 4);
-      function s2p(s) {
-        return (s + 1.0) * 0.5 * width;
-      }
+    gl.drawArrays(gl.POINTS, 0, 4);
+    shouldBe("gl.getError()", "gl.NO_ERROR");
+
+    function s2p(s) {
+      return (s + 1.0) * 0.5 * width;
+    }
 
-      //function print(x, y) {
-      //  var b = new Uint8Array(4);
-      //  gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, b);
-      //  debug("" + x + "," + y + ": " + b[0] + "," + b[1] + "," + b[2]);
-      //}
-      //
-      //for (var ii = 0; ii < 100; ++ii) {
-      //  print(ii, ii);
-      //}
+    //function print(x, y) {
+    //  var b = new Uint8Array(4);
+    //  gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, b);
+    //  debug("" + x + "," + y + ": " + b[0] + "," + b[1] + "," + b[2]);
+    //}
+    //
+    //for (var ii = 0; ii < 100; ++ii) {
+    //  print(ii, ii);
+    //}
 
-      for (var py = 0; py < 2; ++py) {
-        for (var px = 0; px < 2; ++px) {
-          debug("");
-          var pointX = -0.5 + px + pixelOffset;
-          var pointY = -0.5 + py + pixelOffset;
-          for (var yy = 0; yy < maxPointSize; yy += pointStep) {
-            for (var xx = 0; xx < maxPointSize; xx += pointStep) {
-              // formula for s and t from OpenGL ES 2.0 spec section 3.3
-              var xw = s2p(pointX);
-              var yw = s2p(pointY);
-      //debug("xw: " + xw + " yw: " + yw);
-              var u = xx / maxPointSize * 2 - 1;
-              var v = yy / maxPointSize * 2 - 1;
-              var xf = Math.floor(s2p(pointX + u * pointWidth));
-              var yf = Math.floor(s2p(pointY + v * pointWidth));
-      //debug("xf: " + xf + " yf: " + yf);
-              var s = 0.5 + (xf + 0.5 - xw) / maxPointSize;
-              var t = 0.5 + (yf + 0.5 - yw) / maxPointSize;
-      //debug("s: " + s + " t: " + t);
-              var color = [Math.floor(s * 255), Math.floor((1 - t) * 255), 0];
-              var msg = "pixel " + xf + "," + yf + " should be " + color;
-              wtu.checkCanvasRect(gl, xf, yf, 1, 1, color, msg, 4);
-            }
+    for (var py = 0; py < 2; ++py) {
+      for (var px = 0; px < 2; ++px) {
+        debug("");
+        var pointX = -0.5 + px + pixelOffset;
+        var pointY = -0.5 + py + pixelOffset;
+        for (var yy = 0; yy < maxPointSize; yy += pointStep) {
+          for (var xx = 0; xx < maxPointSize; xx += pointStep) {
+            // formula for s and t from OpenGL ES 2.0 spec section 3.3
+            var xw = s2p(pointX);
+            var yw = s2p(pointY);
+            //debug("xw: " + xw + " yw: " + yw);
+            var u = xx / maxPointSize * 2 - 1;
+            var v = yy / maxPointSize * 2 - 1;
+            var xf = Math.floor(s2p(pointX + u * pointWidth));
+            var yf = Math.floor(s2p(pointY + v * pointWidth));
+            //debug("xf: " + xf + " yf: " + yf);
+            var s = 0.5 + (xf + 0.5 - xw) / maxPointSize;
+            var t = 0.5 + (yf + 0.5 - yw) / maxPointSize;
+            //debug("s: " + s + " t: " + t);
+            var color = [Math.floor(s * 255), Math.floor((1 - t) * 255), 0];
+            var msg = "pixel " + xf + "," + yf + " should be " + color;
+            wtu.checkCanvasRect(gl, xf, yf, 1, 1, color, msg, 4);
           }
         }
       }
     }
 
-    init();
     successfullyParsed = true;
   </script>
-<script src="../../../resources/js-test-post.js"></script>
+  <script src="../../../resources/js-test-post.js"></script>
 
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/misc/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/misc/00_test_list.txt
@@ -1,11 +1,13 @@
 bad-arguments-test.html
+--min-version 1.0.2 delayed-drawing.html
 error-reporting.html
 instanceof-test.html
 invalid-passed-params.html
 is-object.html
 null-object-behaviour.html
+functions-returning-strings.html
 object-deletion-behaviour.html
 shader-precision-format.html
 type-conversion-test.html
 uninitialized-test.html
 webgl-specific.html
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/misc/delayed-drawing.html
@@ -0,0 +1,63 @@
+<!--
+Copyright (c) 2011 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Delayed Drawing test.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../resources/js-test-pre.js"></script>
+<script src="../resources/webgl-test.js"> </script>
+<script src="../resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="example" width="4" height="4" style="width: 40px; height: 30px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+description(document.title);
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("example");
+var gl = wtu.create3DContext(canvas);
+var program = wtu.setupTexturedQuad(gl);
+
+glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
+
+var tex = gl.createTexture();
+wtu.fillTexture(gl, tex, 5, 3, [0, 192, 128, 255]);
+
+var loc = gl.getUniformLocation(program, "tex");
+gl.uniform1i(loc, 0);
+
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+
+drawAndCheck();
+
+setTimeout(step2, 1000);
+
+function step2() {
+  drawAndCheck();
+  finishTest();
+}
+
+function drawAndCheck() {
+  glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors before drawing.");
+  wtu.drawQuad(gl);
+  glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from drawing.");
+  wtu.checkCanvas(
+      gl, [0, 192, 128, 255],
+      "draw should be 0, 192, 128, 255");
+}
+
+successfullyParsed = true;
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/misc/functions-returning-strings.html
@@ -0,0 +1,92 @@
+<!--
+Copyright (c) 2012 Mozilla Foundation. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../resources/desktop-gl-constants.js" type="text/javascript"></script>
+<script src="../../resources/js-test-pre.js"></script>
+<script src="../resources/webgl-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<canvas id="canvas" width="2" height="2"> </canvas>
+<script>
+description("Test that functions returning strings really do return strings (and not e.g. null)");
+debug("");
+
+var validVertexShaderString =
+  "attribute vec4 aVertex; attribute vec4 aColor; varying vec4 vColor; void main() { vColor = aColor; gl_Position = aVertex; }";
+var validFragmentShaderString =
+  "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }";
+
+function shouldReturnString(_a)
+{
+  var exception;
+  var _av;
+  try {
+     _av = eval(_a);
+  } catch (e) {
+     exception = e;
+  }
+
+  if (exception)
+    testFailed(_a + ' should return a string. Threw exception ' + exception);
+  else if (typeof _av == "string")
+    testPassed(_a + ' returns a string: "' + _av + '"');
+  else
+    testFailed(_a + ' should return a string.  Returns: "' + _av + '"');
+}
+
+var gl = create3DContext(document.getElementById("canvas"));
+if (!gl) {
+  testFailed("context does not exist");
+} else {
+  var vs = gl.createShader(gl.VERTEX_SHADER);
+  shouldReturnString("gl.getShaderSource(vs)");
+  shouldReturnString("gl.getShaderInfoLog(vs)");
+  gl.shaderSource(vs, validVertexShaderString);
+  gl.compileShader(vs);
+  shouldReturnString("gl.getShaderSource(vs)");
+  shouldReturnString("gl.getShaderInfoLog(vs)");
+
+  var fs = gl.createShader(gl.FRAGMENT_SHADER);
+  shouldReturnString("gl.getShaderSource(fs)");
+  shouldReturnString("gl.getShaderInfoLog(fs)");
+  gl.shaderSource(fs, validFragmentShaderString);
+  gl.compileShader(fs);
+  shouldReturnString("gl.getShaderSource(fs)");
+  shouldReturnString("gl.getShaderInfoLog(fs)");
+
+  var prog = gl.createProgram();
+  shouldReturnString("gl.getProgramInfoLog(prog)");
+  gl.attachShader(prog, vs);
+  gl.attachShader(prog, fs);
+  gl.linkProgram(prog);
+  shouldReturnString("gl.getProgramInfoLog(prog)");
+
+  var exts = gl.getSupportedExtensions();
+  for (i in exts) {
+    shouldReturnString("gl.getSupportedExtensions()[" + i + "]");
+  }
+
+  shouldReturnString("gl.getParameter(gl.VENDOR)");
+  shouldReturnString("gl.getParameter(gl.RENDERER)");
+  shouldReturnString("gl.getParameter(gl.VERSION)");
+  shouldReturnString("gl.getParameter(gl.SHADING_LANGUAGE_VERSION)");
+}
+
+debug("");
+successfullyParsed = true;
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+
+</body>
+</html>
--- a/content/canvas/test/webgl/conformance/misc/invalid-passed-params.html
+++ b/content/canvas/test/webgl/conformance/misc/invalid-passed-params.html
@@ -132,17 +132,20 @@ context.shaderSource(fShader, "precision
                               + "void main() {\n"
                               + "gl_FragColor = vec4(" + validAttribName + ", 0.0, 0.0, 1.0); }");
 context.compileShader(fShader);
 shouldBe("context.getError()", "context.NO_ERROR");
 var program = context.createProgram();
 context.attachShader(program, vShader);
 context.attachShader(program, fShader);
 context.linkProgram(program);
-shouldBeTrue("context.getProgramParameter(program, context.LINK_STATUS)");
+var linkStatus = context.getProgramParameter(program, context.LINK_STATUS);
+shouldBeTrue("linkStatus");
+if (!linkStatus)
+  debug(context.getProgramInfoLog(program));
 shouldBe("context.getError()", "context.NO_ERROR");
 context.bindAttribLocation(program, 1, validAttribName);
 shouldBe("context.getError()", "context.NO_ERROR");
 context.getAttribLocation(program, validAttribName);
 shouldBe("context.getError()", "context.NO_ERROR");
 context.getUniformLocation(program, validUniformName);
 shouldBe("context.getError()", "context.NO_ERROR");
 
--- a/content/canvas/test/webgl/conformance/programs/gl-bind-attrib-location-test.html
+++ b/content/canvas/test/webgl/conformance/programs/gl-bind-attrib-location-test.html
@@ -37,17 +37,17 @@ void main()
 </script>
 <script>
 description("This test ensures WebGL implementations don't allow names that start with 'gl_' when calling bindAttribLocation.");
 
 debug("");
 debug("Canvas.getContext");
 
 var gl = create3DContext(document.getElementById("canvas"));
-shouldBeNonNull(gl);
+shouldBeNonNull("gl");
 
 function fail(x,y, buf, shouldBe)
 {
   var i = (y*50+x) * 4;
   var reason = "pixel at ("+x+","+y+") is ("+buf[i]+","+buf[i+1]+","+buf[i+2]+","+buf[i+3]+"), should be "+shouldBe;
   testFailed(reason);
 }
 
--- a/content/canvas/test/webgl/conformance/programs/program-test.html
+++ b/content/canvas/test/webgl/conformance/programs/program-test.html
@@ -76,18 +76,19 @@ function go() {
 
     assertMsg(gl.getShaderParameter(vs2, gl.COMPILE_STATUS) == true,
               "good vertex shader #2 should compile");
 
     var vsBad = gl.createShader(gl.VERTEX_SHADER);
     gl.shaderSource(vsBad, "WILL NOT COMPILE;");
     gl.compileShader(vsBad);
 
-    assertMsg(gl.getShaderParameter(vsBad, gl.COMPILE_STATUS) == false,
-              "bad vertex shader should fail to compile");
+    // GLSL 1.0.17 section 10.27. compile shader does not have to return failure.
+    //assertMsg(gl.getShaderParameter(vsBad, gl.COMPILE_STATUS) == false,
+    //          "bad vertex shader should fail to compile");
 
     var fs = gl.createShader(gl.FRAGMENT_SHADER);
     gl.shaderSource(fs, "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }");
     gl.compileShader(fs);
 
     assertMsg(gl.getShaderParameter(fs, gl.COMPILE_STATUS) == true,
               "good fragment shader should compile");
 
@@ -97,18 +98,19 @@ function go() {
 
     assertMsg(gl.getShaderParameter(fs2, gl.COMPILE_STATUS) == true,
               "good fragment shader #2 should compile");
 
     var fsBad = gl.createShader(gl.FRAGMENT_SHADER);
     gl.shaderSource(fsBad, "WILL NOT COMPILE;");
     gl.compileShader(fsBad);
 
-    assertMsg(gl.getShaderParameter(fsBad, gl.COMPILE_STATUS) == false,
-              "bad fragment shader should fail to compile");
+    // GLSL 1.0.17 section 10.27. compile shader does not have to return failure.
+    //assertMsg(gl.getShaderParameter(fsBad, gl.COMPILE_STATUS) == false,
+    //          "bad fragment shader should fail to compile");
 
     glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors at this point");
 
 /////// Check attachShader() /////////////////////////////
 
     function checkAttachShader(already_attached_shaders, shader, expected_error_code, errmsg) {
         var prog = gl.createProgram();
         for (var i = 0; i < already_attached_shaders.length; ++i)
@@ -191,18 +193,23 @@ function go() {
                 gl.deleteShader(shaders[i]);
         }
         gl.bindAttribLocation(prog, 0, "aVertex");
         gl.bindAttribLocation(prog, 1, "aColor");
         gl.linkProgram(prog);
         if (gl.getError() != gl.NO_ERROR)
             assertMsg(false, "unexpected error in linkProgram()");
         assertMsg(gl.getProgramParameter(prog, gl.LINK_STATUS) == expected_status, errmsg);
+        var infolog = gl.getProgramInfoLog(prog);
+        if (gl.getError() != gl.NO_ERROR)
+            assertMsg(false, "unexpected error in getProgramInfoLog()");
+        if (typeof(infolog) != "string")
+            assertMsg(false, "getProgramInfoLog() did not return a string");
         if (expected_status == true && gl.getProgramParameter(prog, gl.LINK_STATUS) == false)
-            debug(gl.getProgramInfoLog(prog));
+            debug(infolog);
         if (gl.getError() != gl.NO_ERROR)
             assertMsg(false, "unexpected error in getProgramParameter()");
         gl.useProgram(prog);
         if (expected_status == true)
             glErrorShouldBe(gl, gl.NO_ERROR, "using a valid program should succeed");
         if (expected_status == false)
             glErrorShouldBe(gl, gl.INVALID_OPERATION, "using an invalid program should generate INVALID_OPERATION");
         return prog;
@@ -247,16 +254,20 @@ function go() {
     gl.drawArrays(gl.TRIANGLES, 0, 3);
     glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid program shouldn't generate a GL error");
     gl.detachShader(progGood2, fs2);
     gl.attachShader(progGood2, fsBad);
     gl.linkProgram(progGood2);
     assertMsg(gl.getProgramParameter(progGood2, gl.LINK_STATUS) == false,
               "linking should fail with in-use formerly good program, with new bad shader attached");
 
+    // Invalid link leaves previous valid program intact.
+    gl.drawArrays(gl.TRIANGLES, 0, 3);
+    glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid program shouldn't generate a GL error");
+
     gl.useProgram(progGood1);
     gl.drawArrays(gl.TRIANGLES, 0, 4);
     glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid when last used program shouldn't generate a GL error");
 
     var progGood1 = checkLinkAndUse([vs, fs], true, true, "delete shaders after attaching them and before linking program should not affect linkProgram");
     gl.useProgram(progGood1);
     gl.drawArrays(gl.TRIANGLES, 0, 4);
     glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid when last used program shouldn't generate a GL error");
--- a/content/canvas/test/webgl/conformance/reading/read-pixels-pack-alignment.html
+++ b/content/canvas/test/webgl/conformance/reading/read-pixels-pack-alignment.html
@@ -186,17 +186,17 @@ function runTestIteration(format, type, 
     var bytesPerRow = width * bytesPerPixel + padding;
     var pos = bytesPerRow * (height - 1) + (width - 1) * bytesPerPixel;
     var numComponents = bytesPerPixel;
     for (var i = 0; i < numComponents; ++i)
         pixel[i] = array[pos + i];
     for (var i = numComponents; i < 4; ++i)
         pixel[i] = 0;
     expectedColor = packColor(format, type, 255, 102, 0, 255);
-    shouldBeNonNull(expectedColor);
+    shouldBeNonNull("expectedColor");
     shouldBe("pixel", "expectedColor");
 }
 
 description('Verify readPixels() works fine with various PACK_ALIGNMENT values.');
 
 shouldBeNonNull("gl = initWebGL('example', 'vshader', 'fshader', [ 'pos', 'colorIn' ], [ 0, 0, 0, 1 ], 1)");
 gl.disable(gl.BLEND);
 
--- a/content/canvas/test/webgl/conformance/rendering/line-loop-tri-fan.html
+++ b/content/canvas/test/webgl/conformance/rendering/line-loop-tri-fan.html
@@ -66,16 +66,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                 var green = [0,255,0,255];
                 var black = [0,0,0,255];
                 var isCorrect = true;
                 for (var j = 0; j < w * w * 4; j += 4) {
                     var correct = black;
                     if (j < w * 4 || j > w * (w - 1) * 4 || j % (w * 4) == 0 || j % (w * 4) == (w - 1) * 4) {
                         correct = green;
                     }
+                    // ignore corner pixels
+                    if ((j == 0) || (j == 4*(w-1)) || (j == 4*w*(w-1)) || (j== 4*(w*w - 1))) {
+                        continue;
+                    }
                     if (!checkPixel(buf, j, correct)) {
                         isCorrect = false;
                         break;
                     }
                 }
                 if (isCorrect) {
                     testPassed("Line loop was drawn correctly.");
                 } else {
--- a/content/canvas/test/webgl/conformance/rendering/point-size.html
+++ b/content/canvas/test/webgl/conformance/rendering/point-size.html
@@ -6,47 +6,50 @@ found in the LICENSE file.
 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="utf-8">
 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
 <script src="../../resources/js-test-pre.js"></script>
 <script src="../resources/webgl-test.js"></script>
 <script id="vshader" type="x-shader/x-vertex">
-attribute vec3 pos;
-attribute vec4 colorIn;
-uniform float pointSize;
-varying vec4 color;
+    attribute vec3 pos;
+    attribute vec4 colorIn;
+    uniform float pointSize;
+    varying vec4 color;
 
-void main()
-{
-    gl_PointSize = pointSize;
-    color = colorIn;
-    gl_Position = vec4(pos.xyz, 3.0);
-}
+    void main()
+    {
+        gl_PointSize = pointSize;
+        color = colorIn;
+        gl_Position = vec4(pos, 1.0);
+    }
 </script>
 
 <script id="fshader" type="x-shader/x-fragment">
-precision mediump float;
-varying vec4 color;
-
-void main()
-{
-    gl_FragColor = color;
-}
-</script>
+    precision mediump float;
+    varying vec4 color;
 
+    void main()
+    {
+        gl_FragColor = color;
+    }
+</script>
+</head>
+<body>
+<canvas id="testbed" width="2px" height="2px"></canvas>
+<div id="description"></div>
+<div id="console"></div>
 <script>
-function runTest()
-{
+    description('Verify GL_VERTEX_PROGRAM_POINT_SIZE is enabled in WebGL');
+
     var gl = initWebGL('testbed', 'vshader', 'fshader', ['pos', 'colorIn'], [0, 0, 0, 1], 1, { antialias: false });
-    if (!gl) {
-        testFailed('initWebGL(..) failed');
-        return false;
-    }
+    shouldBeNonNull("gl");
+    shouldBe('gl.getError()', 'gl.NO_ERROR');
+
     gl.disable(gl.BLEND);
     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
     // The choice of (0.4, 0.4) ensures that the centers of the surrounding
     // pixels are not contained within the point when it is of size 1, but
     // that they definitely are when it is of size 2.
     var vertices = new Float32Array([
         0.4, 0.4, 0.0]);
@@ -63,71 +66,60 @@ function runTest()
 
     gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
     gl.enableVertexAttribArray(0);
     gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, colorOffset);
     gl.enableVertexAttribArray(1);
 
     var locPointSize = gl.getUniformLocation(gl.program, 'pointSize');
 
+    shouldBe('gl.getError()', 'gl.NO_ERROR');
+
     debug('Draw a point of size 1 and verify it does not touch any other pixels.');
 
     gl.uniform1f(locPointSize, 1.0);
     gl.drawArrays(gl.POINTS, 0, vertices.length / 3);
     var buf = new Uint8Array(2 * 2 * 4);
     gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+    shouldBe('gl.getError()', 'gl.NO_ERROR');
+
     var index = 0;
+    var correctColor;
     for (var y = 0; y < 2; ++y) {
         for (var x = 0; x < 2; ++x) {
-            var correctColor = [0, 0, 0];
+            correctColor = [0, 0, 0];
             if (x == 1 && y == 1)
-                correctColor[0] = 255;
-            if (buf[index] != correctColor[0] || buf[index + 1] != correctColor[1] || buf[index + 2] != correctColor[2]) {
-                testFailed('Drawing a point of size 1 touched pixels that should not be touched');
-                return false;
-            }
+                shouldBe('buf[index]', '255');
+            else
+                shouldBe('buf[index]', '0');
+            shouldBe('buf[index + 1]', '0');
+            shouldBe('buf[index + 2]', '0');
             index += 4;
         }
     }
     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
     debug('Draw a point of size 2 and verify it fills the appropriate region.');
 
     var pointSizeRange = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE);
-    if (pointSizeRange < 2.0)
-        return true;
-
-    gl.uniform1f(locPointSize, 2.0);
-    gl.drawArrays(gl.POINTS, 0, vertices.length / 3);
-    gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, buf);
-    index = 0;
-    for (var y = 0; y < 2; ++y) {
-        for (var x = 0; x < 2; ++x) {
-            var correctColor = [255, 0, 0];
-            if (buf[index] != correctColor[0] || buf[index + 1] != correctColor[1] || buf[index + 2] != correctColor[2]) {
-                testFailed('Drawing a point of size 2 failed to fill the appropriate region');
-                return false;
+    if (pointSizeRange[1] >= 2.0) {
+        gl.uniform1f(locPointSize, 2.0);
+        gl.drawArrays(gl.POINTS, 0, vertices.length / 3);
+        gl.readPixels(0, 0, 2, 2, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+        shouldBe('gl.getError()', 'gl.NO_ERROR');
+        index = 0;
+        for (var y = 0; y < 2; ++y) {
+            for (var x = 0; x < 2; ++x) {
+                shouldBe('buf[index]', '255');
+                shouldBe('buf[index + 1]', '0');
+                shouldBe('buf[index + 2]', '0');
+                index += 4;
             }
-            index += 4;
         }
     }
 
-    return true;
-}
-</script>
-</head>
-<body>
-<canvas id="testbed" width="2px" height="2px"></canvas>
-<div id="description"></div>
-<div id="console"></div>
-<script>
-description('Verify GL_VERTEX_PROGRAM_POINT_SIZE is enabled in WebGL');
-
-if (runTest())
-    testPassed("");
-
-successfullyParsed = true;
+    successfullyParsed = true;
 </script>
 
 <script src="../../resources/js-test-post.js"></script>
 
 </body>
 </html>
--- a/content/canvas/test/webgl/conformance/resources/glsl-conformance-test.js
+++ b/content/canvas/test/webgl/conformance/resources/glsl-conformance-test.js
@@ -66,20 +66,21 @@ function runOneTest(gl, info) {
   if (!vShader) {
     vShader = wtu.loadShader(gl, vSource, gl.VERTEX_SHADER);
     if (info.vShaderTest) {
       if (!info.vShaderTest(vShader)) {
         testFailed("[vertex shader test] " + passMsg);
         return;
       }
     }
-    if ((vShader != null) != info.vShaderSuccess) {
+    // As per GLSL 1.0.17 10.27 we can only check for success on
+    // compileShader, not failure.
+    if (info.vShaderSuccess && !vShader) {
       testFailed("[unexpected vertex shader compile status] (expected: " +
-                info.vShaderSuccess + ") " + passMsg);
-      return;
+                 info.vShaderSuccess + ") " + passMsg);
     }
     // Save the shaders so we test shared shader.
     if (vShader) {
       vShaderDB[vSource] = vShader;
     }
   }
 
   var fSource = info.fShaderPrep ? info.fShaderPrep(info.fShaderSource) :
@@ -91,17 +92,19 @@ function runOneTest(gl, info) {
     fShader = wtu.loadShader(gl, fSource, gl.FRAGMENT_SHADER);
     if (info.fShaderTest) {
       if (!info.fShaderTest(fShader)) {
         testFailed("[fragment shdaer test] " + passMsg);
         return;
       }
     }
     //debug(fShader == null ? "fail" : "succeed");
-    if ((fShader != null) != info.fShaderSuccess) {
+    // As per GLSL 1.0.17 10.27 we can only check for success on
+    // compileShader, not failure.
+    if (info.fShaderSuccess && !fShader) {
       testFailed("[unexpected fragment shader compile status] (expected: " +
                 info.fShaderSuccess + ") " + passMsg);
       return;
     }
     // Safe the shaders so we test shared shader.
     if (fShader) {
       fShaderDB[fSource] = fShader;
     }
--- a/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
+++ b/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
@@ -582,26 +582,27 @@ var glErrorShouldBe = function(gl, glErr
 
 /**
  * Links a WebGL program, throws if there are errors.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {!WebGLProgram} program The WebGLProgram to link.
  * @param {function(string): void) opt_errorCallback callback for errors. 
  */
 var linkProgram = function(gl, program, opt_errorCallback) {
+  errFn = opt_errorCallback || testFailed;
   // Link the program
   gl.linkProgram(program);
 
   // Check the link status
   var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
   if (!linked) {
     // something went wrong with the link
     var error = gl.getProgramInfoLog (program);
 
-    testFailed("Error in program linking:" + error);
+    errFn("Error in program linking:" + error);
 
     gl.deleteProgram(program);
   }
 };
 
 /**
  * Sets up WebGL with shaders.
  * @param {string} canvasName The id of the canvas.
@@ -926,17 +927,17 @@ var getScript = function(scriptId) {
  * @return {!WebGLShader} The created shader.
  */
 var loadShaderFromScript = function(
     gl, scriptId, opt_shaderType, opt_errorCallback) {
   var shaderSource = "";
   var shaderType;
   var shaderScript = document.getElementById(scriptId);
   if (!shaderScript) {
-    throw("*** Error: unknown script element" + scriptId);
+    throw("*** Error: unknown script element " + scriptId);
   }
   shaderSource = shaderScript.text;
 
   if (!opt_shaderType) {
     if (shaderScript.type == "x-shader/x-vertex") {
       shaderType = gl.VERTEX_SHADER;
     } else if (shaderScript.type == "x-shader/x-fragment") {
       shaderType = gl.FRAGMENT_SHADER;
@@ -1027,16 +1028,51 @@ var loadProgram = function(
   gl.attachShader(
       program,
       loadShader(
           gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback));
   linkProgram(gl, program, opt_errorCallback);
   return program;
 };
 
+/**
+ * Loads shaders from source, creates a program, attaches the shaders and
+ * links but expects error.
+ *
+ * GLSL 1.0.17 10.27 effectively says that compileShader can
+ * always succeed as long as linkProgram fails so we can't
+ * rely on compileShader failing. This function expects
+ * one of the shader to fail OR linking to fail.
+ *
+ * @param {!WebGLContext} gl The WebGLContext to use.
+ * @param {string} vertexShaderScriptId The vertex shader.
+ * @param {string} fragmentShaderScriptId The fragment shader.
+ * @return {WebGLProgram} The created program.
+ */
+var loadProgramFromScriptExpectError = function(
+    gl, vertexShaderScriptId, fragmentShaderScriptId) {
+  var vertexShader = loadShaderFromScript(gl, vertexShaderScriptId);
+  if (!vertexShader) {
+    return null;
+  }
+  var fragmentShader = loadShaderFromScript(gl, fragmentShaderScriptId);
+  if (!fragmentShader) {
+    return null;
+  }
+  var linkSuccess = true;
+  var program = gl.createProgram();
+  gl.attachShader(program, vertexShader);
+  gl.attachShader(program, fragmentShader);
+  linkSuccess = true;
+  linkProgram(gl, program, function() {
+      linkSuccess = false;
+    });
+  return linkSuccess ? program : null;
+};
+
 var basePath;
 var getBasePath = function() {
   if (!basePath) {
     var expectedBase = "webgl-test-utils.js";
     var scripts = document.getElementsByTagName('script');
     for (var script, i = 0; script = scripts[i]; i++) {
       var src = script.src;
       var l = src.length;
@@ -1139,16 +1175,17 @@ return {
   glEnumToString: glEnumToString,
   glErrorShouldBe: glErrorShouldBe,
   fillTexture: fillTexture,
   loadImageAsync: loadImageAsync,
   loadImagesAsync: loadImagesAsync,
   loadProgram: loadProgram,
   loadProgramFromFile: loadProgramFromFile,
   loadProgramFromScript: loadProgramFromScript,
+  loadProgramFromScriptExpectError: loadProgramFromScriptExpectError,
   loadShader: loadShader,
   loadShaderFromFile: loadShaderFromFile,
   loadShaderFromScript: loadShaderFromScript,
   loadStandardProgram: loadStandardProgram,
   loadStandardVertexShader: loadStandardVertexShader,
   loadStandardFragmentShader: loadStandardFragmentShader,
   loadTextFileAsync: loadTextFileAsync,
   loadTexture: loadTexture,
--- a/content/canvas/test/webgl/conformance/textures/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/textures/00_test_list.txt
@@ -1,8 +1,9 @@
+compressed-tex-image.html
 copy-tex-image-and-sub-image-2d.html
 gl-pixelstorei.html
 gl-teximage.html
 origin-clean-conformance.html
 tex-image-and-sub-image-2d-with-array-buffer-view.html
 tex-image-and-sub-image-2d-with-canvas.html
 tex-image-and-sub-image-2d-with-image-data.html
 tex-image-and-sub-image-2d-with-image.html
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/textures/compressed-tex-image.html
@@ -0,0 +1,61 @@
+<!--
+Copyright (c) 2012 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+ -->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL CompressedTexImage and CompressedTexSubImage Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../resources/js-test-pre.js"></script>
+<script src="../resources/webgl-test.js"></script>
+<script src="../resources/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+description("This test ensures WebGL implementations correctly implement compressedTexImage2D and compressedTexSubImage2D.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var canvas = document.createElement("canvas");
+var gl = wtu.create3DContext(canvas);
+
+const  COMPRESSED_RGB_S3TC_DXT1_EXT        = 0x83F0;
+const  COMPRESSED_RGBA_S3TC_DXT1_EXT       = 0x83F1;
+const  COMPRESSED_RGBA_S3TC_DXT5_EXT       = 0x83F3;
+const  ETC1_RGB8_OES                       = 0x8D64;
+const  COMPRESSED_RGB_PVRTC_4BPPV1_IMG     = 0x8C00;
+const  COMPRESSED_RGBA_PVRTC_4BPPV1_IMG    = 0x8C02;
+
+var formats = null;
+
+if (!gl) {
+  testFailed("context does not exist");
+} else {
+  testPassed("context exists");
+
+  var tex = gl.createTexture();
+  gl.bindTexture(gl.TEXTURE_2D, tex);
+
+  shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))");
+  shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))");
+  shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT5_EXT, 4, 4, 0, new Uint8Array(16))");
+  shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ETC1_RGB8_OES, 4, 4, 0, new Uint8Array(8))");
+  shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))");
+  shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))");
+
+  shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)");
+  shouldBeNonNull("formats");
+  shouldBe("formats.length", "0");
+}
+
+successfullyParsed = true;
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
--- a/content/canvas/test/webgl/conformance/textures/texture-size-cube-maps.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-size-cube-maps.html
@@ -144,16 +144,17 @@ function testSize(size) {
       gl.texParameteri(
           gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER,
           gl.NEAREST_MIPMAP_NEAREST);
       gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
     }
 
     var err = gl.getError();
     if (err == gl.OUT_OF_MEMORY) {
+      debug("out of memory");
       return false;
     }
     if (err != gl.NO_ERROR) {
       testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err));
     }
 
 
     for (var rr = 0; rr < rotations.length; ++rr) {
--- a/content/canvas/test/webgl/conformance/textures/texture-size.html
+++ b/content/canvas/test/webgl/conformance/textures/texture-size.html
@@ -91,17 +91,17 @@ function runTest() {
       }
     }
     if (size <= maxCubeMapSize) {
       gl.useProgram(programCubeMap);
       if (!checkTexture(size, size, true)) {
         return false;
       }
     }
-	return true;
+    return true;
   }
 
   if (doTest()) {
     ++power;
     setTimeout(runTest, 100);
   } else {
     finishTest();
   }
@@ -115,30 +115,32 @@ function checkTexture(width, height, cub
   var type = cubeMap ? "cube map" : "2D texture";
   gl.bindTexture(target, tex);
   gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
   gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
   gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
   fillLevel(0, width, height, color.rgba, cubeMap);
   var err = gl.getError();
   if (err == gl.OUT_OF_MEMORY) {
-	return false;
+    debug("out of memory");
+    return false;
   }
   if (err != gl.NO_ERROR) {
-	testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err));
+    testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err));
   }
   wtu.drawQuad(gl);
   wtu.checkCanvas(gl, color.rgba,
       type + " of size " + width + "x" + height + " with no mips should draw with " + color.name);
   count = (count + 1) % colors.length;
   color = colors[count];
   fillLevel(0, width, height, color.rgba, cubeMap);
   gl.generateMipmap(target);
   var err = gl.getError();
   if (err == gl.OUT_OF_MEMORY) {
+    debug("out of memory");
     return false;
   }
   if (err != gl.NO_ERROR) {
     testFailed("unexpected gl error: " + wtu.glEnumToString(gl, err));
   }
   gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
   wtu.drawQuad(gl);
   wtu.checkCanvas(gl, color.rgba,
@@ -177,17 +179,17 @@ function fillLevel(level, width, height,
     gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
     gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
     gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
     gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
     gl.TEXTURE_CUBE_MAP_NEGATIVE_Z] :
     [gl.TEXTURE_2D];
 
   for (var ii = 0; ii < targets.length; ++ii) {
-	// debug(wtu.glEnumToString(gl, targets[ii]));
+    // debug(wtu.glEnumToString(gl, targets[ii]));
     gl.texImage2D(
         targets[ii], level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE,
         pixels);
   }
 }
 
 glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors.");
 
--- a/content/canvas/test/webgl/conformance/typedarrays/array-unit-tests.html
+++ b/content/canvas/test/webgl/conformance/typedarrays/array-unit-tests.html
@@ -85,16 +85,68 @@ function assert(prefix, expected) {
 function printSummary() {
   if (allPassed) {
     debug("Test passed.");
   } else {
     debug("TEST FAILED");
   }
 }
 
+var byteLength;
+var subBuffer;
+function testSlice() {
+  function test(subBuf, starts, size) {
+    byteLength = size;
+    subBuffer = eval(subBuf);
+    var subArray = new Int8Array(subBuffer);
+    assertEq(subBuf, subBuffer.byteLength, byteLength);
+    for (var i = 0; i < size; ++i)
+      assertEq('Element ' + i, starts + i, subArray[i]);
+  }
+
+  try {
+    running('testSlice');
+    var buffer = new ArrayBuffer(32);
+    var array = new Int8Array(buffer);
+    for (var i = 0; i < 32; ++i)
+      array[i] = i;
+
+    test("buffer.slice(0)", 0, 32);
+    test("buffer.slice(16)", 16, 16);
+    test("buffer.slice(24)", 24, 8);
+    test("buffer.slice(32)", 32, 0);
+    test("buffer.slice(40)", 32, 0);
+    test("buffer.slice(80)", 32, 0);
+
+    test("buffer.slice(-8)", 24, 8);
+    test("buffer.slice(-16)", 16, 16);
+    test("buffer.slice(-24)", 8, 24);
+    test("buffer.slice(-32)", 0, 32);
+    test("buffer.slice(-40)", 0, 32);
+    test("buffer.slice(-80)", 0, 32);
+
+    test("buffer.slice(0, 32)", 0, 32);
+    test("buffer.slice(0, 16)", 0, 16);
+    test("buffer.slice(8, 24)", 8, 16);
+    test("buffer.slice(16, 32)", 16, 16);
+    test("buffer.slice(24, 16)", 24, 0);
+
+    test("buffer.slice(16, -8)", 16, 8);
+    test("buffer.slice(-20, 30)", 12, 18);
+
+    test("buffer.slice(-8, -20)", 24, 0);
+    test("buffer.slice(-20, -8)", 12, 12);
+    test("buffer.slice(-40, 16)", 0, 16);
+    test("buffer.slice(-40, 40)", 0, 32);
+    pass();
+  } catch (e) {
+    fail(e);
+  }
+}
+
 //
 // Tests for unsigned array variants
 //
 
 function testSetAndGet10To1(type, name) {
   running('test ' + name + ' SetAndGet10To1');
   try {
     var array = new type(10);
@@ -197,17 +249,21 @@ function testConstructWithTypedArrayOfSi
 function testIntegralArrayTruncationBehavior(type, name, unsigned) {
   running('test integral array truncation behavior for ' + name);
 
   var sourceData;
   var expectedResults;
 
   if (unsigned) {
     sourceData = [0.6, 10.6];
-    expectedResults = [0, 10];
+    if (type === Uint8ClampedArray) {
+      expectedResults = [1, 11];
+    } else {
+      expectedResults = [0, 10];
+    }
   } else {
     sourceData = [0.6, 10.6, -0.6, -10.6];
     expectedResults = [0, 10, 0, -10];
   }
 
   var numIterations = 10;
   var array = new type(numIterations);
 
@@ -244,16 +300,24 @@ function testIntegralArrayTruncationBeha
     case Uint8Array:
       for (var ii = 0; ii < sourceData.length; ++ii) {
         for (var jj = 0; jj < numIterations; ++jj) {
           array[jj] = sourceData[ii];
           assertEq('Storing ' + sourceData[ii], expectedResults[ii], array[jj]);
         }
       }
       break;
+    case Uint8ClampedArray:
+      for (var ii = 0; ii < sourceData.length; ++ii) {
+        for (var jj = 0; jj < numIterations; ++jj) {
+          array[jj] = sourceData[ii];
+          assertEq('Storing ' + sourceData[ii], expectedResults[ii], array[jj]);
+        }
+      }
+      break;
     case Uint16Array:
       for (var ii = 0; ii < sourceData.length; ++ii) {
         for (var jj = 0; jj < numIterations; ++jj) {
           array[jj] = sourceData[ii];
           assertEq('Storing ' + sourceData[ii], expectedResults[ii], array[jj]);
         }
       }
       break;
@@ -514,24 +578,28 @@ function testConstructionWithOutOfRangeV
         var array = new type(buffer, 4, 0x3FFFFFFF);
     }, "Construction of " + name + " with out-of-range number of elements");
     shouldThrowIndexSizeErr(function() {
         var buffer = new ArrayBuffer(4);
         var array = new type(buffer, 8);
     }, "Construction of " + name + " with out-of-range offset");
 }
 
-function testConstructionWithNegativeOutOfRangeValues(type, name, elementSizeInBytes) {
-    if (elementSizeInBytes > 1) {
-        try {
-            var array = new type(-1);
-            testFailed("Construction of " + name + " with negative size should throw exception");
-        } catch (e) {
-            testPassed("Construction of " + name + " with negative size threw exception");
-        }
+function testConstructionWithNegativeOutOfRangeValues(type, name) {
+    try {
+        var buffer = new ArrayBuffer(-1);
+        testFailed("Construction of ArrayBuffer with negative size should throw exception");
+    } catch (e) {
+        testPassed("Construction of ArrayBuffer with negative size threw exception");
+    }
+    try {
+        var array = new type(-1);
+        testFailed("Construction of " + name + " with negative size should throw exception");
+    } catch (e) {
+        testPassed("Construction of " + name + " with negative size threw exception");
     }
     shouldThrowIndexSizeErr(function() {
         var buffer = new ArrayBuffer(4);
         var array = new type(buffer, 4, -2147483648);
     }, "Construction of " + name + " with negative out-of-range values");
 }
 
 function testConstructionWithUnalignedOffset(type, name, elementSizeInBytes) {
@@ -639,55 +707,117 @@ function testSubarrayWithDefaultValues(t
         } catch (e) {
             testFailed("Subarray of " + name + " threw exception");
         }
     } catch (e) {
         testFailed("Exception: " + e);
     }
 }
 
-function testSettingFromArrayWithOutOfRangeOffset(type, name) {
-    var webglArray = new type(32);
-    var array = [];
-    for (var i = 0; i < 16; i++) {
-        array.push(i);
+function setWithInvalidOffset(type, name, length,
+                              sourceType, sourceName, sourceLength,
+                              offset, offsetDescription) {
+    var webglArray = new type(length);
+    var sourceArray = new sourceType(sourceLength);
+    for (var i = 0; i < sourceLength; i++)
+        sourceArray[i] = 42 + i;
+    try {
+        webglArray.set(sourceArray, offset);
+        testFailed("Setting " + name + " from " + sourceName + " with " +
+                   offsetDescription + " offset was not caught");
+    } catch (e) {
+        testPassed("Setting " + name + " from " + sourceName + " with " +
+                   offsetDescription + " offset was caught");
+    }
+}
+
+function setWithValidOffset(type, name, length,
+                            sourceType, sourceName, sourceLength,
+                            offset, offsetDescription) {
+    running("Setting " + name + " from " + sourceName + " with " +
+            offsetDescription + " offset");
+    var webglArray = new type(length);
+    var sourceArray = new sourceType(sourceLength);
+    for (var i = 0; i < sourceLength; i++)
+        sourceArray[i] = 42 + i;
+    try {
+        webglArray.set(sourceArray, offset);
+        offset = Math.floor(offset);
+        for (var i = 0; i < sourceLength; i++) {
+            assertEq("Element " + i + offset, sourceArray[i], webglArray[i + offset]);
+        }
+        pass();
+    } catch (e) {
+        fail(e);
     }
-    try {
-        webglArray.set(array, 0x7FFFFFF8);
-        testFailed("Setting " + name + " from array with out-of-range offset was not caught");
-    } catch (e) {
-        testPassed("Setting " + name + " from array with out-of-range offset was caught");
-    }
+}
+
+
+function testSettingFromArrayWithOutOfRangeOffset(type, name) {
+    setWithInvalidOffset(type, name, 32, Array, "array", 16,
+                         0x7FFFFFF8, "out-of-range");
+}
+
+function testSettingFromTypedArrayWithOutOfRangeOffset(type, name) {
+    setWithInvalidOffset(type, name, 32, type, name, 16,
+                         0x7FFFFFF8, "out-of-range");
+}
+
+function testSettingFromArrayWithNegativeOffset(type, name) {
+    setWithInvalidOffset(type, name, 32, Array, "array", 16,
+                         -1, "negative");
+}
+
+function testSettingFromTypedArrayWithNegativeOffset(type, name) {
+    setWithInvalidOffset(type, name, 32, type, name, 16,
+                         -1, "negative");
+}
+
+function testSettingFromArrayWithMinusZeroOffset(type, name) {
+    setWithValidOffset(type, name, 32, Array, "array", 16,
+                       -0, "-0");
+}
+
+function testSettingFromTypedArrayWithMinusZeroOffset(type, name) {
+    setWithValidOffset(type, name, 32, type, name, 16,
+                       -0, "-0");
+}
+
+function testSettingFromArrayWithBoundaryOffset(type, name) {
+    setWithValidOffset(type, name, 32, Array, "array", 16,
+                       16, "boundary");
+}
+
+function testSettingFromTypedArrayWithBoundaryOffset(type, name) {
+    setWithValidOffset(type, name, 32, type, name, 16,
+                       16, "boundary");
+}
+
+function testSettingFromArrayWithNonIntegerOffset(type, name) {
+    setWithValidOffset(type, name, 32, Array, "array", 16,
+                       16.999, "non-integer");
+}
+
+function testSettingFromTypedArrayWithNonIntegerOffset(type, name) {
+    setWithValidOffset(type, name, 32, type, name, 16,
+                       16.999, "non-integer");
 }
 
 function testSettingFromFakeArrayWithOutOfRangeLength(type, name) {
     var webglArray = new type(32);
     var array = {};
     array.length = 0x80000000;
     try {
         webglArray.set(array, 8);
         testFailed("Setting " + name + " from fake array with invalid length was not caught");
     } catch (e) {
         testPassed("Setting " + name + " from fake array with invalid length was caught");
     }
 }
 
-function testSettingFromTypedArrayWithOutOfRangeOffset(type, name) {
-    var webglArray = new type(32);
-    var srcArray = new type(16);
-    for (var i = 0; i < 16; i++) {
-        srcArray[i] = i;
-    }
-    try {
-        webglArray.set(srcArray, 0x7FFFFFF8);
-        testFailed("Setting " + name + " from " + name + " with out-of-range offset was not caught");
-    } catch (e) {
-        testPassed("Setting " + name + " from " + name + " with out-of-range offset was caught");
-    }
-}
 
 function negativeTestGetAndSetMethods(type, name) {
     array = new type([2, 3]);
     shouldBeUndefined("array.get");
     var exceptionThrown = false;
     // We deliberately check for an exception here rather than using
     // shouldThrow here because the precise contents of the syntax
     // error are not specified.
@@ -746,16 +876,22 @@ function testNaNConversion(type, name) {
       }
       break;
     case Uint8Array:
       for (var i = 0; i < array.length; ++i) {
         array[i] = NaN;
         results[i] = array[i];
       }
       break;
+    case Uint8ClampedArray:
+      for (var i = 0; i < array.length; ++i) {
+        array[i] = NaN;
+        results[i] = array[i];
+      }
+      break;
     case Uint16Array:
       for (var i = 0; i < array.length; ++i) {
         array[i] = NaN;
         results[i] = array[i];
       }
       break;
     case Uint32Array:
       for (var i = 0; i < array.length; ++i) {
@@ -787,16 +923,18 @@ function testNaNConversion(type, name) {
 
 //
 // Test driver
 //
 
 function runTests() {
   allPassed = true;
 
+  testSlice();
+
   // The "name" attribute is a concession to browsers which don't
   // implement the "name" property on function objects
   var testCases =
     [ {name: "Float32Array",
        unsigned: false,
        integral: false,
        elementSizeInBytes: 4,
        testValues:     [ -500.5, 500.5 ],
@@ -832,16 +970,23 @@ function runTests() {
       },
       {name: "Uint8Array",
        unsigned: true,
        integral: true,
        elementSizeInBytes: 1,
        testValues:     [ 0, 255,  -1, 256 ],
        expectedValues: [ 0, 255, 255,   0 ]
       },
+      {name: "Uint8ClampedArray",
+       unsigned: true,
+       integral: true,
+       elementSizeInBytes: 1,
+       testValues:     [ 0, 255,  -1, 256 ],
+       expectedValues: [ 0, 255,   0, 255 ]
+      },
       {name: "Uint16Array",
        unsigned: true,
        integral: true,
        elementSizeInBytes: 2,
        testValues:     [ 0, 65535,    -1, 65536 ],
        expectedValues: [ 0, 65535, 65535,     0 ]
       },
       {name: "Uint32Array",
@@ -886,27 +1031,35 @@ function runTests() {
                               testCase.testValues,
                               testCase.expectedValues);
     testConstructionBoundaryConditions(type,
                                        name,
                                        testCase.testValues,
                                        testCase.expectedValues);
     testConstructionWithNullBuffer(type, name);
     testConstructionWithOutOfRangeValues(type, name);
-    testConstructionWithNegativeOutOfRangeValues(type, name, testCase.elementSizeInBytes);
+    testConstructionWithNegativeOutOfRangeValues(type, name);
     testConstructionWithUnalignedOffset(type, name, testCase.elementSizeInBytes);
     testConstructionWithUnalignedLength(type, name, testCase.elementSizeInBytes);
     testConstructionOfHugeArray(type, name, testCase.elementSizeInBytes);
     testConstructionWithBothArrayBufferAndLength(type, name, testCase.elementSizeInBytes);
     testConstructionWithSubPortionOfArrayBuffer(type, name, testCase.elementSizeInBytes);
     testSubarrayWithOutOfRangeValues(type, name, testCase.elementSizeInBytes);
     testSubarrayWithDefaultValues(type, name, testCase.elementSizeInBytes);
     testSettingFromArrayWithOutOfRangeOffset(type, name);
+    testSettingFromTypedArrayWithOutOfRangeOffset(type, name);
+    testSettingFromArrayWithNegativeOffset(type, name);
+    testSettingFromTypedArrayWithNegativeOffset(type, name);
+    testSettingFromArrayWithMinusZeroOffset(type, name);
+    testSettingFromTypedArrayWithMinusZeroOffset(type, name);
+    testSettingFromArrayWithBoundaryOffset(type, name);
+    testSettingFromTypedArrayWithBoundaryOffset(type, name);
+    testSettingFromArrayWithNonIntegerOffset(type, name);
+    testSettingFromTypedArrayWithNonIntegerOffset(type, name);
     testSettingFromFakeArrayWithOutOfRangeLength(type, name);
-    testSettingFromTypedArrayWithOutOfRangeOffset(type, name);
     negativeTestGetAndSetMethods(type, name);
     testNaNConversion(type, name);
   }
 
   printSummary();
 }
 
 runTests();
--- a/content/canvas/test/webgl/dont-load-image-from-internet.patch
+++ b/content/canvas/test/webgl/dont-load-image-from-internet.patch
@@ -1,10 +1,10 @@
 # HG changeset patch
-# Parent 07fbbfde4173da7d8e513bb2d52ae07e333dbf43
+# Parent 30d84739fa6136571e995045960c3fd90b44382c
 diff --git a/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html b/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html
 --- a/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html
 +++ b/content/canvas/test/webgl/conformance/more/functions/readPixelsBadArgs.html
 @@ -110,10 +110,10 @@ Tests.testReadPixelsSOPCanvas = function
  
  Tests.endUnit = function(gl) {
  }
  
--- a/content/canvas/test/webgl/failing_tests_linux.txt
+++ b/content/canvas/test/webgl/failing_tests_linux.txt
@@ -5,10 +5,9 @@ conformance/glsl/misc/shader-with-long-l
 conformance/misc/uninitialized-test.html
 conformance/programs/gl-get-active-attribute.html
 conformance/textures/texture-mips.html
 conformance/uniforms/gl-uniform-bool.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/more/functions/uniformfArrayLen1.html
 conformance/glsl/misc/attrib-location-length-limits.html
 conformance/glsl/misc/uniform-location-length-limits.html
-conformance/renderbuffers/framebuffer-object-attachment.html
-conformance/glsl/misc/struct-nesting-exceeds-maximum.html
+conformance/renderbuffers/framebuffer-object-attachment.html
\ No newline at end of file
--- a/content/canvas/test/webgl/failing_tests_mac.txt
+++ b/content/canvas/test/webgl/failing_tests_mac.txt
@@ -3,9 +3,9 @@ conformance/glsl/misc/glsl-function-node
 conformance/glsl/misc/glsl-long-variable-names.html
 conformance/glsl/misc/shader-with-256-character-identifier.frag.html
 conformance/glsl/misc/shader-with-long-line.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/more/functions/uniformfBadArgs.html
 conformance/more/functions/uniformiBadArgs.html
 conformance/glsl/misc/attrib-location-length-limits.html
 conformance/glsl/misc/uniform-location-length-limits.html
-conformance/glsl/misc/struct-nesting-exceeds-maximum.html
+conformance/programs/program-test.html
\ No newline at end of file
--- a/content/canvas/test/webgl/failing_tests_windows.txt
+++ b/content/canvas/test/webgl/failing_tests_windows.txt
@@ -3,10 +3,9 @@ conformance/glsl/functions/glsl-function
 conformance/glsl/functions/glsl-function-atan-xy.html
 conformance/glsl/misc/glsl-long-variable-names.html
 conformance/glsl/misc/shader-with-256-character-identifier.frag.html
 conformance/glsl/misc/shader-with-long-line.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/more/functions/uniformfArrayLen1.html
 conformance/glsl/misc/attrib-location-length-limits.html
 conformance/glsl/misc/struct-nesting-under-maximum.html
-conformance/glsl/misc/uniform-location-length-limits.html
-conformance/glsl/misc/struct-nesting-exceeds-maximum.html
+conformance/glsl/misc/uniform-location-length-limits.html
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/fix-webgl-harness-async.patch
@@ -0,0 +1,22 @@
+diff --git a/content/canvas/test/webgl/resources/webgl-test-harness.js b/content/canvas/test/webgl/resources/webgl-test-harness.js
+--- a/content/canvas/test/webgl/resources/webgl-test-harness.js
++++ b/content/canvas/test/webgl/resources/webgl-test-harness.js
+@@ -362,18 +362,16 @@ TestHarness.prototype.addFiles_ = functi
+   }
+   log("total files: " + files.length);
+   for (var ii = 0; ii < files.length; ++ii) {
+     log("" + ii + ": " + files[ii]);
+     this.files.push(new TestFile(files[ii]));
+     this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined);
+   }
+   this.reportFunc(TestHarness.reportType.READY, undefined, undefined);
+-  this.nextFileIndex = files.length;
+-  this.lastFileIndex = files.length;
+ }
+ 
+ TestHarness.prototype.runTests = function(opt_start, opt_count) {
+   var count = opt_count || this.files.length;
+   this.nextFileIndex = opt_start || 0;
+   this.lastFileIndex = this.nextFileIndex + count;
+   this.startNextFile();
+ };
--- a/content/canvas/test/webgl/log-more-info-about-test-failures.patch
+++ b/content/canvas/test/webgl/log-more-info-about-test-failures.patch
@@ -1,10 +1,10 @@
 # HG changeset patch
-# Parent 1910ae60536dce7272cb0478089bf40806666de8
+# Parent 0122002fbffad16de0690a21aa87d0999d0cdcb9
 diff --git a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
 --- a/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
 +++ b/content/canvas/test/webgl/conformance/glsl/misc/glsl-function-nodes.html
 @@ -117,17 +117,18 @@ function init()
      var bufFunction = new Uint8Array(width * height * 4);
      var bufMacro = new Uint8Array(width * height * 4);
  
      if (drawAndRead("canvasFunction", "vshaderFunction", bufFunction) == false ||
--- a/content/canvas/test/webgl/remove-uniqueObjectTest.patch
+++ b/content/canvas/test/webgl/remove-uniqueObjectTest.patch
@@ -1,14 +1,14 @@
 # HG changeset patch
-# Parent a41d853e5110aca4f2c77c63db502f670d0e50a1
+# Parent efec656cc42addae5aa96c6691031d54d46436fe
 diff --git a/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html b/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html
 --- a/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html
 +++ b/content/canvas/test/webgl/conformance/extensions/oes-standard-derivatives.html
-@@ -107,17 +107,18 @@ if (!gl) {
+@@ -115,17 +115,18 @@ if (!gl) {
      } else {
          testPassed("Successfully enabled OES_standard_derivatives extension");
  
          runSupportedTest(true);
  
          runHintTestEnabled();
          runShaderTests(true);
          runOutputTests();
--- a/content/canvas/test/webgl/resources/webgl-test-harness.js
+++ b/content/canvas/test/webgl/resources/webgl-test-harness.js
@@ -90,105 +90,290 @@ var log = function(msg) {
   if (window.console && window.console.log) {
     window.console.log(msg);
   }
 };
 
 /**
  * Loads text from an external file. This function is synchronous.
  * @param {string} url The url of the external file.
- * @return {string} the loaded text if the request is synchronous.
+ * @param {!function(bool, string): void} callback that is sent a bool for
+ *     success and the string.
  */
-var loadTextFileSynchronous = function(url) {
+var loadTextFileAsynchronous = function(url, callback) {
+  log ("loading: " + url);
   var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
   var request;
   if (window.XMLHttpRequest) {
     request = new XMLHttpRequest();
     if (request.overrideMimeType) {
       request.overrideMimeType('text/plain');
     }
   } else {
     throw 'XMLHttpRequest is disabled';
   }
-  request.open('GET', url, false);
-  request.send(null);
-  if (request.readyState != 4) {
-    throw error;
+  try {
+    request.open('GET', url, true);
+    request.onreadystatechange = function() {
+      if (request.readyState == 4) {
+        var text = '';
+        // HTTP reports success with a 200 status. The file protocol reports
+        // success with zero. HTTP does not use zero as a status code (they
+        // start at 100).
+        // https://developer.mozilla.org/En/Using_XMLHttpRequest
+        var success = request.status == 200 || request.status == 0;
+        if (success) {
+          text = request.responseText;
+        }
+        log("loaded: " + url);
+        callback(success, text);
+      }
+    };
+    request.send(null);
+  } catch (e) {
+    log("failed to load: " + url);
+    callback(false, '');
   }
-  return request.responseText;
+};
+
+/**
+ * Compare version strings.
+ */
+var greaterThanOrEqualToVersion = function(have, want) {
+  have = have.split(" ")[0].split(".");
+  want = want.split(" ")[0].split(".");
+
+  //have 1.2.3   want  1.1
+  //have 1.1.1   want  1.1
+  //have 1.0.9   want  1.1
+  //have 1.1     want  1.1.1
+
+  for (var ii = 0; ii < want.length; ++ii) {
+    var wantNum = parseInt(want[ii]);
+    var haveNum = have[ii] ? parseInt(have[ii]) : 0
+    if (haveNum < wantNum) {
+      return false;
+    }
+  }
+  return true;
 };
 
-var getFileList = function(url) {
+/**
+ * Reads a file, recursively adding files referenced inside.
+ *
+ * Each line of URL is parsed, comments starting with '#' or ';'
+ * or '//' are stripped.
+ *
+ * arguments beginning with -- are extracted
+ *
+ * lines that end in .txt are recursively scanned for more files
+ * other lines are added to the list of files.
+ *
+ * @param {string} url The url of the file to read.
+ * @param {void function(boolean, !Array.<string>)} callback.
+ *      Callback that is called with true for success and an
+ *      array of filenames.
+ * @param {Object} options. Optional options
+ *
+ * Options:
+ *    version: {string} The version of the conformance test.
+ *    Tests with the argument --min-version <version> will
+ *    be ignored version is less then <version>
+ *
+ */
+var getFileList = function(url, callback, options) {
   var files = [];
-  if (url.substr(url.length - 4) == '.txt') {
-    var lines = loadTextFileSynchronous(url).split('\n');
-    var prefix = '';
-    var lastSlash = url.lastIndexOf('/');
-    if (lastSlash >= 0) {
-      prefix = url.substr(0, lastSlash + 1);
+
+  var copyObject = function(obj) {
+    return JSON.parse(JSON.stringify(obj));
+  };
+
+  var toCamelCase = function(str) {
+    return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() });
+  };
+
+  var globalOptions = copyObject(options);
+  globalOptions.defaultVersion = "1.0";
+
+  var getFileListImpl = function(prefix, line, hierarchicalOptions, callback) {
+    var files = [];
+
+    var args = line.split(/\s+/);
+    var nonOptions = [];
+    var useTest = true;
+    var testOptions = {};
+    for (var jj = 0; jj < args.length; ++jj) {
+      var arg = args[jj];
+      if (arg[0] == '-') {
+        if (arg[1] != '-') {
+          throw ("bad option at in " + url + ":" + (ii + 1) + ": " + str);
+        }
+        var option = arg.substring(2);
+        switch (option) {
+          case 'min-version':
+            ++jj;
+            testOptions[toCamelCase(option)] = args[jj];
+            break;
+          default:
+            throw ("bad unknown option '" + option + "' at in " + url + ":" + (ii + 1) + ": " + str);
+        }
+      } else {
+        nonOptions.push(arg);
+      }
     }
-    for (var ii = 0; ii < lines.length; ++ii) {
-      var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
-      if (str.length > 4 &&
-          str[0] != '#' &&
-          str[0] != ";" &&
-          str.substr(0, 2) != "//") {
-        new_url = prefix + str;
-        files = files.concat(getFileList(new_url));
+    var url = prefix + nonOptions.join(" ");
+
+    if (url.substr(url.length - 4) != '.txt') {
+      var minVersion = testOptions.minVersion;
+      if (!minVersion) {
+        minVersion = hierarchicalOptions.defaultVersion;
+      }
+
+      if (globalOptions.minVersion) {
+        useTest = greaterThanOrEqualToVersion(minVersion, globalOptions.minVersion);
+      } else {
+        useTest = greaterThanOrEqualToVersion(globalOptions.version, minVersion);
       }
     }
-  } else {
-    files.push(url);
-  }
-  return files;
-}
+
+    if (!useTest) {
+      callback(true, []);
+      return;
+    }
+
+    if (url.substr(url.length - 4) == '.txt') {
+      // If a version was explicity specified pass it down.
+      if (testOptions.minVersion) {
+        hierarchicalOptions.defaultVersion = testOptions.minVersion;
+      }
+      loadTextFileAsynchronous(url, function() {
+        return function(success, text) {
+          if (!success) {
+            callback(false, '');
+            return;
+          }
+          var lines = text.split('\n');
+          var prefix = '';
+          var lastSlash = url.lastIndexOf('/');
+          if (lastSlash >= 0) {
+            prefix = url.substr(0, lastSlash + 1);
+          }
+          var fail = false;
+          var count = 1;
+          var index = 0;
+          for (var ii = 0; ii < lines.length; ++ii) {
+            var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+            if (str.length > 4 &&
+                str[0] != '#' &&
+                str[0] != ";" &&
+                str.substr(0, 2) != "//") {
+              ++count;
+              getFileListImpl(prefix, str, copyObject(hierarchicalOptions), function(index) {
+                return function(success, new_files) {
+                  log("got files: " + new_files.length);
+                  if (success) {
+                    files[index] = new_files;
+                  }
+                  finish(success);
+                };
+              }(index++));
+            }
+          }
+          finish(true);
+
+          function finish(success) {
+            if (!success) {
+              fail = true;
+            }
+            --count;
+            log("count: " + count);
+            if (!count) {
+              callback(!fail, files);
+            }
+          }
+        }
+      }());
+    } else {
+      files.push(url);
+      callback(true, files);
+    }
+  };
+
+  getFileListImpl('', url, globalOptions, function(success, files) {
+    // flatten
+    var flat = [];
+    flatten(files);
+    function flatten(files) {
+      for (var ii = 0; ii < files.length; ++ii) {
+        var value = files[ii];
+        if (typeof(value) == "string") {
+          flat.push(value);
+        } else {
+          flatten(value);
+        }
+      }
+    }
+    callback(success, flat);
+  });
+};
 
 var TestFile = function(url) {
   this.url = url;
 };
 
-var TestHarness = function(iframe, filelistUrl, reportFunc) {
+var TestHarness = function(iframe, filelistUrl, reportFunc, options) {
   this.window = window;
   this.iframe = iframe;
   this.reportFunc = reportFunc;
-  try {
-    var files = getFileList(filelistUrl);
-  } catch (e) {
+  this.timeoutDelay = 20000;
+  this.files = [];
+
+  var that = this;
+  getFileList(filelistUrl, function() {
+    return function(success, files) {
+      that.addFiles_(success, files);
+    };
+  }(), options);
+
+};
+
+TestHarness.reportType = {
+  ADD_PAGE: 1,
+  READY: 2,
+  START_PAGE: 3,
+  TEST_RESULT: 4,
+  FINISH_PAGE: 5,
+  FINISHED_ALL_TESTS: 6
+};
+
+TestHarness.prototype.addFiles_ = function(success, files) {
+  if (!success) {
     this.reportFunc(
         TestHarness.reportType.FINISHED_ALL_TESTS,
         'Unable to load tests. Are you running locally?\n' +
         'You need to run from a server or configure your\n' +
         'browser to allow access to local files (not recommended).\n\n' +
         'Note: An easy way to run from a server:\n\n' +
         '\tcd path_to_tests\n' +
         '\tpython -m SimpleHTTPServer\n\n' +
         'then point your browser to ' +
           '<a href="http://localhost:8000/webgl-conformance-tests.html">' +
           'http://localhost:8000/webgl-conformance-tests.html</a>',
         false)
     return;
   }
-  this.files = [];
+  log("total files: " + files.length);
   for (var ii = 0; ii < files.length; ++ii) {
+    log("" + ii + ": " + files[ii]);
     this.files.push(new TestFile(files[ii]));
     this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined);
   }
-  this.nextFileIndex = files.length;
-  this.lastFileIndex = files.length;
-  this.timeoutDelay = 20000;
+  this.reportFunc(TestHarness.reportType.READY, undefined, undefined);
 }
 
-TestHarness.reportType = {
-  ADD_PAGE: 1,
-  START_PAGE: 2,
-  TEST_RESULT: 3,
-  FINISH_PAGE: 4,
-  FINISHED_ALL_TESTS: 5
-};
-
 TestHarness.prototype.runTests = function(opt_start, opt_count) {
   var count = opt_count || this.files.length;
   this.nextFileIndex = opt_start || 0;
   this.lastFileIndex = this.nextFileIndex + count;
   this.startNextFile();
 };
 
 TestHarness.prototype.setTimeout = function() {
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
@@ -5,16 +5,22 @@
 <title>
 Mochitest version of the WebGL Conformance Test Suite
 </title>
 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 <script type="text/javascript" src="resources/webgl-test-harness.js"></script>
 <script>
 
+var CONFORMANCE_TEST_VERSION = "1.0.1 (beta)";
+
+var OPTIONS = {
+  version: CONFORMANCE_TEST_VERSION
+};
+
 /**
  * This is copied from webgl-test-harness.js where it is defined as a private function, not accessible to us (argh!)
  *
  * Loads text from an external file. This function is synchronous.
  * @param {string} url The url of the external file.
  * @return {string} the loaded text if the request is synchronous.
  */
   var loadTextFileSynchronous = function (url) {
@@ -261,57 +267,84 @@ 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();
   }
 
+  Reporter.prototype.ready = function() {
+    statusTextNode.textContent = 'Loaded test lists. Starting tests...';
+    window.webglTestHarness.runTests();
+  }
+
   Reporter.prototype.reportFunc = function(type, msg, success) {
     switch (type) {
       case reportType.ADD_PAGE:
         return this.addPage(msg);
+      case reportType.READY:
+        return this.ready();
       case reportType.START_PAGE:
         return this.startPage(msg);
       case reportType.TEST_RESULT:
         return this.addResult(msg, success);
       case reportType.FINISH_PAGE:
         return this.finishPage(success);
       case reportType.FINISHED_ALL_TESTS:
         this.finishedTestSuite();
         return true;
       default:
         throw 'unhandled';
         break;
     };
   };
 
+  var getURLOptions = function(obj) {
+    var s = window.location.href;
+    var q = s.indexOf("?");
+    var e = s.indexOf("#");
+    if (e < 0) {
+      e = s.length;
+    }
+    var query = s.substring(q + 1, e);
+    var pairs = query.split("&");
+    for (var ii = 0; ii < pairs.length; ++ii) {
+      var keyValue = pairs[ii].split("=");
+      var key = keyValue[0];
+      var value = decodeURIComponent(keyValue[1]);
+          obj[key] = value;
+    }
+  };
+
+  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;
     try {
         ctx = canvas.getContext("experimental-webgl");
     } catch(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) {
                 return reporter.reportFunc(type, msg, success);
-            });
+            },
+            OPTIONS);
         testHarness.setTimeoutDelay(20000); // and make it much higher when running under valgrind.
         window.webglTestHarness = testHarness;
-        testHarness.runTests();
     } else {
         var errmsg = "Can't create a WebGL context";
         reporter.fullResultsNode.textContent = errmsg;
         ok(false, errmsg);
         dump("WebGL mochitest failed: " + errmsg + "\n");
         reporter.finishedTestSuite();
     }
   };
deleted file mode 100644
--- a/content/canvas/test/webgl/undo-r15330-async-test-list-loading.patch
+++ /dev/null
@@ -1,236 +0,0 @@
-# HG changeset patch
-# Parent fb36d18f04ef9b01ca87d3fde539d50c204f9bba
-diff --git a/content/canvas/test/webgl/resources/webgl-test-harness.js b/content/canvas/test/webgl/resources/webgl-test-harness.js
---- a/content/canvas/test/webgl/resources/webgl-test-harness.js
-+++ b/content/canvas/test/webgl/resources/webgl-test-harness.js
-@@ -90,190 +90,105 @@ var log = function(msg) {
-   if (window.console && window.console.log) {
-     window.console.log(msg);
-   }
- };
- 
- /**
-  * Loads text from an external file. This function is synchronous.
-  * @param {string} url The url of the external file.
-- * @param {!function(bool, string): void} callback that is sent a bool for
-- *     success and the string.
-+ * @return {string} the loaded text if the request is synchronous.
-  */
--var loadTextFileAsynchronous = function(url, callback) {
--  log ("loading: " + url);
-+var loadTextFileSynchronous = function(url) {
-   var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
-   var request;
-   if (window.XMLHttpRequest) {
-     request = new XMLHttpRequest();
-     if (request.overrideMimeType) {
-       request.overrideMimeType('text/plain');
-     }
-   } else {
-     throw 'XMLHttpRequest is disabled';
-   }
--  try {
--    request.open('GET', url, true);
--    request.onreadystatechange = function() {
--      if (request.readyState == 4) {
--        var text = '';
--        // HTTP reports success with a 200 status. The file protocol reports
--        // success with zero. HTTP does not use zero as a status code (they
--        // start at 100).
--        // https://developer.mozilla.org/En/Using_XMLHttpRequest
--        var success = request.status == 200 || request.status == 0;
--        if (success) {
--          text = request.responseText;
--        }
--        log("loaded: " + url);
--        callback(success, text);
--      }
--    };
--    request.send(null);
--  } catch (e) {
--    log("failed to load: " + url);
--    callback(false, '');
-+  request.open('GET', url, false);
-+  request.send(null);
-+  if (request.readyState != 4) {
-+    throw error;
-   }
-+  return request.responseText;
- };
- 
--var getFileList = function(url, callback) {
-+var getFileList = function(url) {
-   var files = [];
--
--  var getFileListImpl = function(url, callback) {
--    var files = [];
--    if (url.substr(url.length - 4) == '.txt') {
--      loadTextFileAsynchronous(url, function() {
--        return function(success, text) {
--          if (!success) {
--            callback(false, '');
--            return;
--          }
--          var lines = text.split('\n');
--          var prefix = '';
--          var lastSlash = url.lastIndexOf('/');
--          if (lastSlash >= 0) {
--            prefix = url.substr(0, lastSlash + 1);
--          }
--          var fail = false;
--          var count = 1;
--          var index = 0;
--          for (var ii = 0; ii < lines.length; ++ii) {
--            var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
--            if (str.length > 4 &&
--                str[0] != '#' &&
--                str[0] != ";" &&
--                str.substr(0, 2) != "//") {
--              new_url = prefix + str;
--              ++count;
--              getFileListImpl(new_url, function(index) {
--                return function(success, new_files) {
--                  log("got files: " + new_files.length);
--                  if (success) {
--                    files[index] = new_files;
--                  }
--                  finish(success);
--                };
--              }(index++));
--            }
--          }
--          finish(true);
--
--          function finish(success) {
--            if (!success) {
--              fail = true;
--            }
--            --count;
--            log("count: " + count);
--            if (!count) {
--              callback(!fail, files);
--            }
--          }
--        }
--      }());
--
--    } else {
--      files.push(url);
--      callback(true, files);
-+  if (url.substr(url.length - 4) == '.txt') {
-+    var lines = loadTextFileSynchronous(url).split('\n');
-+    var prefix = '';
-+    var lastSlash = url.lastIndexOf('/');
-+    if (lastSlash >= 0) {
-+      prefix = url.substr(0, lastSlash + 1);
-     }
--  };
--
--  getFileListImpl(url, function(success, files) {
--    // flatten
--    var flat = [];
--    flatten(files);
--    function flatten(files) {
--      for (var ii = 0; ii < files.length; ++ii) {
--        var value = files[ii];
--        if (typeof(value) == "string") {
--          flat.push(value);
--        } else {
--          flatten(value);
--        }
-+    for (var ii = 0; ii < lines.length; ++ii) {
-+      var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
-+      if (str.length > 4 &&
-+          str[0] != '#' &&
-+          str[0] != ";" &&
-+          str.substr(0, 2) != "//") {
-+        new_url = prefix + str;
-+        files = files.concat(getFileList(new_url));
-       }
-     }
--    callback(success, flat);
--  });
--};
-+  } else {
-+    files.push(url);
-+  }
-+  return files;
-+}
- 
- var TestFile = function(url) {
-   this.url = url;
- };
- 
- var TestHarness = function(iframe, filelistUrl, reportFunc) {
-   this.window = window;
-   this.iframe = iframe;
-   this.reportFunc = reportFunc;
--  this.timeoutDelay = 20000;
--  this.files = [];
--
--  var that = this;
--  getFileList(filelistUrl, function() {
--    return function(success, files) {
--      that.addFiles_(success, files);
--    };
--  }());
--
--};
--
--TestHarness.reportType = {
--  ADD_PAGE: 1,
--  READY: 2,
--  START_PAGE: 3,
--  TEST_RESULT: 4,
--  FINISH_PAGE: 5,
--  FINISHED_ALL_TESTS: 6
--};
--
--TestHarness.prototype.addFiles_ = function(success, files) {
--  if (!success) {
-+  try {
-+    var files = getFileList(filelistUrl);
-+  } catch (e) {
-     this.reportFunc(
-         TestHarness.reportType.FINISHED_ALL_TESTS,
-         'Unable to load tests. Are you running locally?\n' +
-         'You need to run from a server or configure your\n' +
-         'browser to allow access to local files (not recommended).\n\n' +
-         'Note: An easy way to run from a server:\n\n' +
-         '\tcd path_to_tests\n' +
-         '\tpython -m SimpleHTTPServer\n\n' +
-         'then point your browser to ' +
-           '<a href="http://localhost:8000/webgl-conformance-tests.html">' +
-           'http://localhost:8000/webgl-conformance-tests.html</a>',
-         false)
-     return;
-   }
--  log("total files: " + files.length);
-+  this.files = [];
-   for (var ii = 0; ii < files.length; ++ii) {
--    log("" + ii + ": " + files[ii]);
-     this.files.push(new TestFile(files[ii]));
-     this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined);
-   }
--  this.reportFunc(TestHarness.reportType.READY, undefined, undefined);
-   this.nextFileIndex = files.length;
-   this.lastFileIndex = files.length;
-+  this.timeoutDelay = 20000;
- }
- 
-+TestHarness.reportType = {
-+  ADD_PAGE: 1,
-+  START_PAGE: 2,
-+  TEST_RESULT: 3,
-+  FINISH_PAGE: 4,
-+  FINISHED_ALL_TESTS: 5
-+};
-+
- TestHarness.prototype.runTests = function(opt_start, opt_count) {
-   var count = opt_count || this.files.length;
-   this.nextFileIndex = opt_start || 0;
-   this.lastFileIndex = this.nextFileIndex + count;
-   this.startNextFile();
- };
- 
- TestHarness.prototype.setTimeout = function() {
--- a/content/canvas/test/webgl/webgl-conformance-tests.html
+++ b/content/canvas/test/webgl/webgl-conformance-tests.html
@@ -45,16 +45,20 @@ found in the LICENSE file.
     list-style: none;
     padding-left: 1em;
   }
 </style>
 <script type="text/javascript" src="resources/webgl-test-harness.js"></script>
 <script>
 var CONFORMANCE_TEST_VERSION = "1.0.1 (beta)";
 
+var OPTIONS = {
+  version: CONFORMANCE_TEST_VERSION
+};
+
 function start() {
 
   function log(msg) {
     if (window.console && window.console.log) {
       window.console.log(msg);
     }
   }
 
@@ -212,17 +216,17 @@ function start() {
     li.appendChild(div);
     li.appendChild(ul);
     this.childUL = ul;
     this.elem = li;
     this.check = check;
   };
 
   Folder.prototype.checked = function() {
-    return this.check.checked && 
+    return this.check.checked &&
         (this.folder ? this.folder.checked() : true);
   };
 
   Folder.prototype.firstTestIndex = function() {
     return this.items[0].firstTestIndex();
   };
 
   Folder.prototype.numChildren = function() {
@@ -242,17 +246,17 @@ function start() {
 
   Folder.prototype.getSubFolder = function(name) {
     var subFolder = this.subFolders[name];
     if (subFolder === undefined) {
       subFolder = new Folder(this.reporter, this, this.depth + 1, name);
       this.subFolders[name] = subFolder;
       this.items.push(subFolder);
       this.childUL.appendChild(subFolder.elem);
-    } 
+    }
     return subFolder;
   };
 
   Folder.prototype.getOrCreateFolder = function(url) {
     var parts = url.split('/');
     var folder = this;
     for (var pp = 0; pp < parts.length - 1; ++pp) {
       folder = folder.getSubFolder(parts[pp]);
@@ -358,17 +362,17 @@ function start() {
       }
       var msg = ' (' + totalSuccessful + ' of ' +
                 totalTests + ' passed' + timeout + ')';
       this.fullResultsNode.textContent = msg;
 
       // generate a text summary
       var tx = "";
       tx += "WebGL Conformance Test Results\n";
-      tx += "Version " + CONFORMANCE_TEST_VERSION + "\n";
+      tx += "Version " + OPTIONS.version + "\n";
       tx += "\n";
       tx += "-------------------\n\n";
       tx += "User Agent: " + (navigator.userAgent ? navigator.userAgent : "(navigator.userAgent is null)") + "\n";
       tx += "WebGL VENDOR: " + this.contextInfo["VENDOR"] + "\n";
       tx += "WebGL VERSION: " + this.contextInfo["VERSION"] + "\n";
       tx += "WebGL RENDERER: " + this.contextInfo["RENDERER"] + "\n";
       tx += "Unmasked VENDOR: " + this.contextInfo["UNMASKED_VENDOR"] + "\n";
       tx += "Unmasked RENDERER: " + this.contextInfo["UNMASKED_RENDERER"] + "\n";
@@ -427,26 +431,46 @@ function start() {
       case reportType.FINISHED_ALL_TESTS:
         return this.displayFinalResults(msg, success);
       default:
         throw 'unhandled';
         break;
     };
   };
 
-  document.getElementById("testVersion").innerHTML = CONFORMANCE_TEST_VERSION;
+  var getURLOptions = function(obj) {
+    var s = window.location.href;
+    var q = s.indexOf("?");
+    var e = s.indexOf("#");
+    if (e < 0) {
+      e = s.length;
+    }
+    var query = s.substring(q + 1, e);
+    var pairs = query.split("&");
+    for (var ii = 0; ii < pairs.length; ++ii) {
+      var keyValue = pairs[ii].split("=");
+      var key = keyValue[0];
+      var value = decodeURIComponent(keyValue[1]);
+	  obj[key] = value;
+    }
+  };
+
+  getURLOptions(OPTIONS);
+
+  document.getElementById("testVersion").innerHTML = OPTIONS.version;
 
   var reporter = new Reporter();
   var iframe = document.getElementById("testframe");
   var testHarness = new WebGLTestHarnessModule.TestHarness(
       iframe,
       '00_test_list.txt',
       function(type, msg, success) {
         return reporter.reportFunc(type, msg, success);
-      });
+      },
+      OPTIONS);
   window.webglTestHarness = testHarness;
   var button = document.getElementById("runTestsButton");
   button.disabled = true;
   button.onclick = function() {
     testHarness.runTests();
   };
   var textbutton = document.getElementById("showTextSummary");
   textbutton.onclick = function() {
--- a/content/events/src/nsContentEventHandler.cpp
+++ b/content/events/src/nsContentEventHandler.cpp
@@ -202,50 +202,72 @@ static void AppendSubString(nsAString& a
                "aContent is not a text node!");
   const nsTextFragment* text = aContent->GetText();
   if (!text)
     return;
   text->AppendTo(aString, PRInt32(aXPOffset), PRInt32(aXPLength));
 }
 
 #if defined(XP_WIN)
-static PRUint32 CountNewlinesIn(nsIContent* aContent, PRUint32 aMaxOffset)
+static PRUint32 CountNewlinesInXPLength(nsIContent* aContent,
+                                        PRUint32 aXPLength)
 {
   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                "aContent is not a text node!");
   const nsTextFragment* text = aContent->GetText();
   if (!text)
     return 0;
-  if (aMaxOffset == PR_UINT32_MAX) {
-    // search the entire string
-    aMaxOffset = text->GetLength();
-  }
+  NS_ASSERTION(aXPLength == PR_UINT32_MAX || aXPLength <= text->GetLength(),
+               "text offset is out-of-bounds");
+  const PRUint32 length = NS_MIN(aXPLength, text->GetLength());
   PRUint32 newlines = 0;
-  for (PRUint32 i = 0; i < aMaxOffset; ++i) {
+  for (PRUint32 i = 0; i < length; ++i) {
     if (text->CharAt(i) == '\n') {
       ++newlines;
     }
   }
   return newlines;
 }
+
+static PRUint32 CountNewlinesInNativeLength(nsIContent* aContent,
+                                            PRUint32 aNativeLength)
+{
+  NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
+               "aContent is not a text node!");
+  const nsTextFragment* text = aContent->GetText();
+  if (!text) {
+    return 0;
+  }
+  const PRUint32 xpLength = text->GetLength();
+  PRUint32 newlines = 0;
+  for (PRUint32 i = 0, nativeOffset = 0;
+       i < xpLength && nativeOffset < aNativeLength;
+       ++i, ++nativeOffset) {
+    if (text->CharAt(i) == '\n') {
+      ++newlines;
+      ++nativeOffset;
+    }
+  }
+  return newlines;
+}
 #endif
 
 static PRUint32 GetNativeTextLength(nsIContent* aContent, PRUint32 aMaxLength = PR_UINT32_MAX)
 {
   if (aContent->IsNodeOfType(nsINode::eTEXT)) {
     PRUint32 textLengthDifference =
 #if defined(XP_MACOSX)
       // On Mac, the length of a native newline ("\r") is equal to the length of
       // the XP newline ("\n"), so the native length is the same as the XP length.
       0;
 #elif defined(XP_WIN)
       // On Windows, the length of a native newline ("\r\n") is twice the length of
       // the XP newline ("\n"), so XP length is equal to the length of the native
       // offset plus the number of newlines encountered in the string.
-      CountNewlinesIn(aContent, aMaxLength);
+      CountNewlinesInXPLength(aContent, aMaxLength);
 #else
       // On other platforms, the native and XP newlines are the same.
       0;
 #endif
 
     const nsTextFragment* text = aContent->GetText();
     if (!text)
       return 0;
@@ -267,17 +289,17 @@ static PRUint32 ConvertToXPOffset(nsICon
 #if defined(XP_MACOSX)
   // On Mac, the length of a native newline ("\r") is equal to the length of
   // the XP newline ("\n"), so the native offset is the same as the XP offset.
   return aNativeOffset;
 #elif defined(XP_WIN)
   // On Windows, the length of a native newline ("\r\n") is twice the length of
   // the XP newline ("\n"), so XP offset is equal to the length of the native
   // offset minus the number of newlines encountered in the string.
-  return aNativeOffset - CountNewlinesIn(aContent, aNativeOffset);
+  return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
 #else
   // On other platforms, the native and XP newlines are the same.
   return aNativeOffset;
 #endif
 }
 
 static nsresult GenerateFlatTextContent(nsRange* aRange,
                                         nsAFlatString& aString)
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -295,10 +295,19 @@ include $(topsrcdir)/config/rules.mk
 		file_fullscreen-esc-exit-inner.html \
 		file_fullscreen-rollback.html \
 		test_li_attributes_reflection.html \
 		test_ol_attributes_reflection.html \
 		test_bug651956.html \
 		test_bug694503.html \
 		$(NULL)
 
+_BROWSER_TEST_FILES = \
+		browser_bug649778.js \
+		file_bug649778.html \
+		file_bug649778.html^headers^ \
+		$(NULL)
+
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+
+libs:: $(_BROWSER_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/browser_bug649778.js
@@ -0,0 +1,67 @@
+// Test for bug 649778 - document.write may cause a document to be written to disk cache even when the page has Cache-Control: no-store
+
+// Globals
+var testPath = "http://mochi.test:8888/browser/content/html/content/test/";
+var popup;
+
+function checkCache(url, policy, shouldExist)
+{
+  var cache = Components.classes["@mozilla.org/network/cache-service;1"].
+              getService(Components.interfaces.nsICacheService);
+  var session = cache.createSession(
+                  "wyciwyg", policy,
+                  Components.interfaces.nsICache.STREAM_BASED);
+  try {
+    var cacheEntry = session.openCacheEntry(
+                       url, Components.interfaces.nsICache.ACCESS_READ, true);
+    is(shouldExist, true, "Entry found");
+  }
+  catch (e) {
+    is(shouldExist, false, "Entry not found");
+    is(e.result, Components.results.NS_ERROR_CACHE_KEY_NOT_FOUND,
+       "Invalid error");
+  }
+}
+
+function getPopupURL() {
+  var sh = popup.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                .getInterface(Components.interfaces.nsIWebNavigation)
+                .sessionHistory;
+
+  return sh.getEntryAtIndex(sh.index, false).URI.spec;
+}
+
+function testContinue() {
+  var wyciwygURL = getPopupURL();
+  is(wyciwygURL.substring(0, 10), "wyciwyg://", "Unexpected URL.");
+  popup.close()
+
+  checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_ON_DISK, false);
+  checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_IN_MEMORY, true);
+
+  finish();
+}
+
+function waitForWyciwygDocument() {
+  try {
+    var url = getPopupURL();
+    if (url.substring(0, 10) == "wyciwyg://") {
+      setTimeout(testContinue, 0);
+      return;
+    }
+  }
+  catch (e) {
+  }
+  setTimeout(waitForWyciwygDocument, 100);
+}
+
+// Entry point from Mochikit
+function test() {
+  waitForExplicitFinish();
+
+  popup = window.open(testPath + "file_bug649778.html", "popup 0",
+                      "height=200,width=200,location=yes," +
+                      "menubar=yes,status=yes,toolbar=yes,dependent=yes");
+
+  waitForWyciwygDocument();
+}
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_bug649778.html
@@ -0,0 +1,11 @@
+<html>
+<script>
+function test() {
+  document.open();
+  document.write('<html><body>WYCIWYG DOCUMENT</body></html>');
+  document.close();
+}
+</script>
+<body onload="setTimeout(test, 0);">
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_bug649778.html^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -131,16 +131,17 @@
 #include "nsRange.h"
 #include "mozAutoDocUpdate.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsHtml5Module.h"
 #include "prprf.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 #include "nsMimeTypes.h"
+#include "nsIRequest.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
 
 #include "prmem.h"
 #include "prtime.h"
@@ -1414,16 +1415,17 @@ nsHTMLDocument::Open(const nsAString& aC
   }
 
   // Grab a reference to the calling documents security info (if any)
   // and URIs as they may be lost in the call to Reset().
   nsCOMPtr<nsISupports> securityInfo = callerDoc->GetSecurityInfo();
   nsCOMPtr<nsIURI> uri = callerDoc->GetDocumentURI();
   nsCOMPtr<nsIURI> baseURI = callerDoc->GetBaseURI();
   nsCOMPtr<nsIPrincipal> callerPrincipal = callerDoc->NodePrincipal();
+  nsCOMPtr<nsIChannel> callerChannel = callerDoc->GetChannel();
 
   // We're called from script. Make sure the script is from the same
   // origin, not just that the caller can access the document. This is
   // needed to keep document principals from ever changing, which is
   // needed because of the way we use our XOW code, and is a sane
   // thing to do anyways.
 
   bool equals = false;
@@ -1485,16 +1487,31 @@ nsHTMLDocument::Open(const nsAString& aC
   // We can't depend on channels implementing property bags, so do our
   // base URI manually after reset.
 
   // Set the caller principal, if any, on the channel so that we'll
   // make sure to use it when we reset.
   rv = channel->SetOwner(callerPrincipal);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (callerChannel) {
+    nsLoadFlags callerLoadFlags;
+    rv = callerChannel->GetLoadFlags(&callerLoadFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsLoadFlags loadFlags;
+    rv = channel->GetLoadFlags(&loadFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    loadFlags |= callerLoadFlags & nsIRequest::INHIBIT_PERSISTENT_CACHING;
+
+    rv = channel->SetLoadFlags(loadFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   // Before we reset the doc notify the globalwindow of the change,
   // but only if we still have a window (i.e. our window object the
   // current inner window in our outer window).
 
   // Hold onto ourselves on the offchance that we're down to one ref
   nsCOMPtr<nsIDOMDocument> kungFuDeathGrip =
     do_QueryInterface((nsIHTMLDocument*)this);
 
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -122,16 +122,17 @@ include $(topsrcdir)/config/rules.mk
 		test_info_leak.html \
 		test_load.html \
 		test_load_candidates.html \
 		test_load_same_resource.html \
 		test_load_source.html \
 		test_loop.html \
 		test_media_selection.html \
 		test_mozLoadFrom.html \
+		test_no_load_event.html \
 		test_networkState.html \
 		test_new_audio.html \
 		test_paused.html \
 		test_paused_after_ended.html \
 		test_play_events.html \
 		test_play_events_2.html \
 		test_playback.html \
 		test_playback_errors.html \
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_no_load_event.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=715469
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 715469</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body onload="start();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=715469">Mozilla Bug 715469</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+
+<script type="application/javascript">
+
+/** Test for Bug 715469 **/
+
+var gotLoadEvent = false;
+
+function start() {
+  var resource = getPlayableVideo(gSmallTests);
+  if (resource == null) {
+    todo(false, "No types supported");
+  } else {
+    SimpleTest.waitForExplicitFinish();
+    var v = document.createElement("video");
+    v.src = resource.name;
+    v.addEventListener("loadeddata", function(){v.play();}, false);
+    v.controls = "true";
+    
+    v.addEventListener("load",
+      function(){
+        gotLoadEvent = true;
+      },
+      false);
+      
+    v.addEventListener("ended", finished, false);
+    
+    document.body.appendChild(v);
+  }
+}
+
+function finished() {
+  is(gotLoadEvent, false, "Should not receive a load on the video element");
+  SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -2194,17 +2194,17 @@ nsSMILTimedElement::NotifyNewInterval()
       "Attempting to notify dependents of a new interval but the interval "
       "is not set");
 
   nsSMILTimeContainer* container = GetTimeContainer();
   if (container) {
     container->SyncPauseTime();
   }
 
-  NotifyTimeDependentsParams params = { mCurrentInterval, container };
+  NotifyTimeDependentsParams params = { this, container };
   mTimeDependents.EnumerateEntries(NotifyNewIntervalCallback, &params);
 }
 
 void
 nsSMILTimedElement::NotifyChangedInterval(nsSMILInterval* aInterval,
                                           bool aBeginObjectChanged,
                                           bool aEndObjectChanged)
 {
@@ -2304,14 +2304,19 @@ nsSMILTimedElement::NotifyNewIntervalCal
 {
   NS_ABORT_IF_FALSE(aKey, "Null hash key for time container hash table");
   NS_ABORT_IF_FALSE(aKey->GetKey(),
                     "null nsSMILTimeValueSpec in set of time dependents");
 
   NotifyTimeDependentsParams* params =
     static_cast<NotifyTimeDependentsParams*>(aData);
   NS_ABORT_IF_FALSE(params, "null data ptr while enumerating hashtable");
-  NS_ABORT_IF_FALSE(params->mCurrentInterval, "null current-interval ptr");
+  nsSMILInterval* interval = params->mTimedElement->mCurrentInterval;
+  // It's possible that in notifying one new time dependent of a new interval
+  // that a chain reaction is triggered which results in the original interval
+  // disappearing. If that's the case we can skip sending further notifications.
+  if (!interval)
+    return PL_DHASH_STOP;
 
   nsSMILTimeValueSpec* spec = aKey->GetKey();
-  spec->HandleNewInterval(*params->mCurrentInterval, params->mTimeContainer);
+  spec->HandleNewInterval(*interval, params->mTimeContainer);
   return PL_DHASH_NEXT;
 }
--- a/content/smil/nsSMILTimedElement.h
+++ b/content/smil/nsSMILTimedElement.h
@@ -361,17 +361,17 @@ protected:
     public:
       bool Equals(const nsSMILInstanceTime* aElem1,
                     const nsSMILInstanceTime* aElem2) const;
       bool LessThan(const nsSMILInstanceTime* aElem1,
                       const nsSMILInstanceTime* aElem2) const;
   };
 
   struct NotifyTimeDependentsParams {
-    nsSMILInterval*      mCurrentInterval;
+    nsSMILTimedElement*  mTimedElement;
     nsSMILTimeContainer* mTimeContainer;
   };
 
   // Templated helper functions
   template <class TestFunctor>
   void RemoveInstanceTimes(InstanceTimeList& aArray, TestFunctor& aTest);
 
   //
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -801,43 +801,45 @@ nsDOMWindowUtils::Focus(nsIDOMElement* a
     else
       fm->ClearFocus(mWindow);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener)
+nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener,
+                                 PRInt32 aExtraForgetSkippableCalls)
 {
   SAMPLE_LABEL("GC", "GarbageCollect");
   // Always permit this in debug builds.
 #ifndef DEBUG
   if (!IsUniversalXPConnectCapable()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 #endif
 
   nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS);
-  nsJSContext::CycleCollectNow(aListener);
+  nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener)
+nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener,
+                               PRInt32 aExtraForgetSkippableCalls)
 {
   // Always permit this in debug builds.
 #ifndef DEBUG
   if (!IsUniversalXPConnectCapable()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 #endif
 
-  nsJSContext::CycleCollectNow(aListener);
+  nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType,
                                          float aX,
                                          float aY,
                                          PRUint32 aDirection,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -278,16 +278,19 @@ static PRInt32              gRefCnt     
 static PRInt32              gOpenPopupSpamCount        = 0;
 static PopupControlState    gPopupControlState         = openAbused;
 static PRInt32              gRunningTimeoutDepth       = 0;
 static bool                 gMouseDown                 = false;
 static bool                 gDragServiceDisabled       = false;
 static FILE                *gDumpFile                  = nsnull;
 static PRUint64             gNextWindowID              = 0;
 static PRUint32             gSerialCounter             = 0;
+static PRUint32             gTimeoutsRecentlySet       = 0;
+static TimeStamp            gLastRecordedRecentTimeouts;
+#define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
 
 #ifdef DEBUG_jst
 PRInt32 gTimeoutCnt                                    = 0;
 #endif
 
 #if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 static bool                 gDOMWindowDumpEnabled      = false;
 #endif
@@ -9049,16 +9052,17 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
   }
 
   if (subsumes) {
     timeout->mPrincipal = subjectPrincipal;
   } else {
     timeout->mPrincipal = ourPrincipal;
   }
 
+  ++gTimeoutsRecentlySet;
   TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
 
   if (!IsFrozen() && !mTimeoutsSuspendDepth) {
     // If we're not currently frozen, then we set timeout->mWhen to be the
     // actual firing time of the timer (i.e., now + delta). We also actually
     // create a timer and fire it off.
 
     timeout->mWhen = TimeStamp::Now() + delta;
@@ -9223,16 +9227,26 @@ nsGlobalWindow::RunTimeout(nsTimeout *aT
 
   // Maybe the timeout that the event was fired for has been deleted
   // and there are no others timeouts with deadlines that make them
   // eligible for execution yet. Go away.
   if (!last_expired_timeout) {
     return;
   }
 
+  // Record telemetry information about timers set recently.
+  TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL);
+  if (gLastRecordedRecentTimeouts.IsNull() ||
+      now - gLastRecordedRecentTimeouts > recordingInterval) {
+    PRUint32 count = gTimeoutsRecentlySet;
+    gTimeoutsRecentlySet = 0;
+    Telemetry::Accumulate(Telemetry::DOM_TIMERS_RECENTLY_SET, count);
+    gLastRecordedRecentTimeouts = now;
+  }
+
   // Insert a dummy timeout into the list of timeouts between the
   // portion of the list that we are about to process now and those
   // timeouts that will be processed in a future call to
   // win_run_timeout(). This dummy timeout serves as the head of the
   // list for any timeouts inserted as a result of running a timeout.
   dummy_timeout.mFiringDepth = firingDepth;
   dummy_timeout.mWhen = now;
   PR_INSERT_AFTER(&dummy_timeout, last_expired_timeout);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -134,16 +134,20 @@ static PRLogModuleInfo* gJSDiagnostics;
 // The amount of time we wait from the first request to GC to actually
 // doing the first GC.
 #define NS_FIRST_GC_DELAY           10000 // ms
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 5000 // ms
 
+#define NS_CC_SKIPPABLE_DELAY       250 // ms
+
+#define NS_CC_FORCED                (5 * 60 * PR_USEC_PER_SEC) // 5 min
+
 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
 
 // if you add statics here, add them to the list in nsJSRuntime::Startup
 
 static nsITimer *sGCTimer;
 static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sCCTimer;
 
@@ -157,16 +161,25 @@ static bool sGCHasRun;
 // all cases. This counter also gets reset if we end up GC'ing while
 // we're waiting for a slow page to load. IOW, this count may be 0
 // even when there are pending loads.
 static PRUint32 sPendingLoadCount;
 static bool sLoadingInProgress;
 
 static PRUint32 sCCollectedWaitingForGC;
 static bool sPostGCEventsToConsole;
+static PRUint32 sCCTimerFireCount = 0;
+static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX;
+static PRUint32 sMaxForgetSkippableTime = 0;
+static PRUint32 sTotalForgetSkippableTime = 0;
+static PRUint32 sRemovedPurples = 0;
+static PRUint32 sForgetSkippableBeforeCC = 0;
+static PRUint32 sPreviousSuspectedCount = 0;
+
+static bool sCleanupSinceLastGC = true;
 
 nsScriptNameSpaceManager *gNameSpaceManager;
 
 static nsIJSRuntimeService *sRuntimeService;
 JSRuntime *nsJSRuntime::sRuntime;
 
 static const char kJSRuntimeServiceContractID[] =
   "@mozilla.org/js/xpc/RuntimeService;1";
@@ -3251,30 +3264,40 @@ nsJSContext::ShrinkGCBuffersNow()
 
   KillShrinkGCBuffersTimer();
 
   JS_ShrinkGCBuffers(nsJSRuntime::sRuntime);
 }
 
 //Static
 void
-nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener)
+nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
+                             PRInt32 aExtraForgetSkippableCalls)
 {
   if (!NS_IsMainThread()) {
     return;
   }
 
   SAMPLE_LABEL("GC", "CycleCollectNow");
   NS_TIME_FUNCTION_MIN(1.0);
 
   KillCCTimer();
 
   PRTime start = PR_Now();
 
   PRUint32 suspected = nsCycleCollector_suspectedCount();
+
+  for (PRInt32 i = 0; i < aExtraForgetSkippableCalls; ++i) {
+    nsCycleCollector_forgetSkippable();
+  }
+
+  // nsCycleCollector_forgetSkippable may mark some gray js to black.
+  if (!sCleanupSinceLastGC && aExtraForgetSkippableCalls >= 0) {
+    nsCycleCollector_forgetSkippable();
+  }
   PRUint32 collected = nsCycleCollector_collect(aListener);
   sCCollectedWaitingForGC += collected;
 
   // If we collected a substantial amount of cycles, poke the GC since more objects
   // might be unreachable now.
   if (sCCollectedWaitingForGC > 250) {
     PokeGC(js::gcreason::CC_WAITING);
   }
@@ -3290,28 +3313,45 @@ nsJSContext::CycleCollectNow(nsICycleCol
   if (sPostGCEventsToConsole) {
     PRTime delta = 0;
     if (sFirstCollectionTime) {
       delta = now - sFirstCollectionTime;
     } else {
       sFirstCollectionTime = now;
     }
 
-    NS_NAMED_LITERAL_STRING(kFmt,
-                            "CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.");
+    NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
+      NS_LL("CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.\n")
+      NS_LL("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, removed: %lu"));
     nsString msg;
+    PRUint32 cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
+    sMinForgetSkippableTime = (sMinForgetSkippableTime == PR_UINT32_MAX)
+      ? 0 : sMinForgetSkippableTime;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
                                         collected, sCCollectedWaitingForGC, suspected,
-                                        (now - start) / PR_USEC_PER_MSEC));
+                                        (now - start) / PR_USEC_PER_MSEC,
+                                        sForgetSkippableBeforeCC,
+                                        sMinForgetSkippableTime / PR_USEC_PER_MSEC,
+                                        sMaxForgetSkippableTime / PR_USEC_PER_MSEC,
+                                        (sTotalForgetSkippableTime / cleanups) /
+                                          PR_USEC_PER_MSEC,
+                                        sTotalForgetSkippableTime / PR_USEC_PER_MSEC,
+                                        sRemovedPurples));
     nsCOMPtr<nsIConsoleService> cs =
       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
+  sMinForgetSkippableTime = PR_UINT32_MAX;
+  sMaxForgetSkippableTime = 0;
+  sTotalForgetSkippableTime = 0;
+  sRemovedPurples = 0;
+  sForgetSkippableBeforeCC = 0;
+  sCleanupSinceLastGC = true;
 }
 
 // static
 void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sGCTimer);
 
@@ -3326,19 +3366,60 @@ ShrinkGCBuffersTimerFired(nsITimer *aTim
 
   nsJSContext::ShrinkGCBuffersNow();
 }
 
 // static
 void
 CCTimerFired(nsITimer *aTimer, void *aClosure)
 {
-  NS_RELEASE(sCCTimer);
-
-  nsJSContext::CycleCollectNow();
+  if (sDidShutdown) {
+    return;
+  }
+  ++sCCTimerFireCount;
+  if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) {
+    PRUint32 suspected = nsCycleCollector_suspectedCount();
+    if ((sPreviousSuspectedCount + 100) > suspected) {
+      // Just few new suspected objects, return early.
+      return;
+    }
+    sPreviousSuspectedCount = suspected;
+    PRTime startTime;
+    if (sPostGCEventsToConsole) {
+      startTime = PR_Now();
+    }
+    nsCycleCollector_forgetSkippable();
+    sCleanupSinceLastGC = true;
+    if (sPostGCEventsToConsole) {
+      PRTime delta = PR_Now() - startTime;
+      if (sMinForgetSkippableTime > delta) {
+        sMinForgetSkippableTime = delta;
+      }
+      if (sMaxForgetSkippableTime < delta) {
+        sMaxForgetSkippableTime = delta;
+      }
+      sTotalForgetSkippableTime += delta;
+      sRemovedPurples += (suspected - nsCycleCollector_suspectedCount());
+      ++sForgetSkippableBeforeCC;
+    }
+  } else {
+    sPreviousSuspectedCount = 0;
+    nsJSContext::KillCCTimer();
+    if (nsCycleCollector_suspectedCount() > 500 ||
+        sLastCCEndTime + NS_CC_FORCED < PR_Now()) {
+      nsJSContext::CycleCollectNow();
+    }
+  }
+}
+
+// static
+bool
+nsJSContext::CleanupSinceLastGC()
+{
+  return sCleanupSinceLastGC;
 }
 
 // static
 void
 nsJSContext::LoadStart()
 {
   sLoadingInProgress = true;
   ++sPendingLoadCount;
@@ -3409,40 +3490,31 @@ nsJSContext::PokeShrinkGCBuffers()
                                               NS_SHRINK_GC_BUFFERS_DELAY,
                                               nsITimer::TYPE_ONE_SHOT);
 }
 
 // static
 void
 nsJSContext::MaybePokeCC()
 {
-  if (nsCycleCollector_suspectedCount() > 1000) {
-    PokeCC();
-  }
-}
-
-// static
-void
-nsJSContext::PokeCC()
-{
-  if (sCCTimer || !sGCHasRun) {
-    // There's already a timer for GC'ing, or GC hasn't run yet, just return.
+  if (sCCTimer) {
     return;
   }
 
-  CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
-
-  if (!sCCTimer) {
-    // Failed to create timer (probably because we're in XPCOM shutdown)
-    return;
+  if (nsCycleCollector_suspectedCount() > 100 ||
+      sLastCCEndTime + NS_CC_FORCED < PR_Now()) {
+    sCCTimerFireCount = 0;
+    CallCreateInstance("@mozilla.org/timer;1", &sCCTimer);
+    if (!sCCTimer) {
+      return;
+    }
+    sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull,
+                                   NS_CC_SKIPPABLE_DELAY,
+                                   nsITimer::TYPE_REPEATING_SLACK);
   }
-
-  sCCTimer->InitWithFuncCallback(CCTimerFired, nsnull,
-                                 NS_CC_DELAY,
-                                 nsITimer::TYPE_ONE_SHOT);
 }
 
 //static
 void
 nsJSContext::KillGCTimer()
 {
   if (sGCTimer) {
     sGCTimer->Cancel();
@@ -3499,16 +3571,18 @@ DOMGCFinishedCallback(JSRuntime *rt, JSC
                                         double(delta) / PR_USEC_PER_SEC, status));
     nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
 
   sCCollectedWaitingForGC = 0;
+  sCleanupSinceLastGC = false;
+
   if (sGCTimer) {
     // If we were waiting for a GC to happen, kill the timer.
     nsJSContext::KillGCTimer();
 
     // If this is a compartment GC, restart it. We still want
     // a full GC to happen. Compartment GCs usually happen as a
     // result of last-ditch or MaybeGC. In both cases its
     // probably a time of heavy activity and we want to delay
@@ -3518,17 +3592,17 @@ DOMGCFinishedCallback(JSRuntime *rt, JSC
 
       // We poked the GC, so we can kill any pending CC here.
       nsJSContext::KillCCTimer();
     }
   } else {
     // If this was a full GC, poke the CC to run soon.
     if (!comp) {
       sGCHasRun = true;
-      nsJSContext::PokeCC();
+      nsJSContext::MaybePokeCC();
     }
   }
 
   // If we didn't end up scheduling a GC, make sure that we release GC buffers
   // soon after canceling previous shrinking attempt 
   nsJSContext::KillShrinkGCBuffersTimer();
   if (!sGCTimer) {
     nsJSContext::PokeShrinkGCBuffers();
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -178,30 +178,34 @@ public:
 
   NS_DECL_NSIXPCSCRIPTNOTIFY
 
   static void LoadStart();
   static void LoadEnd();
 
   static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal);
   static void ShrinkGCBuffersNow();
-  static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull);
+  // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
+  // called even if the previous collection was GC.
+  static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull,
+                              PRInt32 aExtraForgetSkippableCalls = 0);
 
   static void PokeGC(js::gcreason::Reason aReason);
   static void KillGCTimer();
 
   static void PokeShrinkGCBuffers();
   static void KillShrinkGCBuffersTimer();
 
-  static void PokeCC();
   static void MaybePokeCC();
   static void KillCCTimer();
 
   virtual void GC(js::gcreason::Reason aReason);
 
+  static bool CleanupSinceLastGC();
+
   nsIScriptGlobalObject* GetCachedGlobalObject()
   {
     // Verify that we have a global so that this
     // does always return a null when GetGlobalObject() is null.
     JSObject* global = JS_GetGlobalObject(mContext);
     return global ? mGlobalObjectRef.get() : nsnull;
   }
 protected:
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -65,17 +65,17 @@ interface nsIDOMEvent;
 interface nsITransferable;
 interface nsIQueryContentEventResult;
 interface nsIDOMWindow;
 interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 
-[scriptable, uuid(e01171b0-712a-47ce-8552-b7b2ef0a2507)]
+[scriptable, uuid(ab6e9c71-8aa1-40bb-8bf9-65e16429055f)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -398,30 +398,44 @@ interface nsIDOMWindowUtils : nsISupport
    * Force a garbage collection followed by a cycle collection.
    *
    * Will throw a DOM security error if called without UniversalXPConnect
    * privileges in non-debug builds. Available to all callers in debug builds.
    *
    * @param aListener listener that receives information about the CC graph
    *                  (see @mozilla.org/cycle-collector-logger;1 for a logger
    *                   component)
+   * @param aExtraForgetSkippableCalls indicates how many times
+   *                                   nsCycleCollector_forgetSkippable will
+   *                                   be called before running cycle collection.
+   *                                   -1 prevents the default
+   *                                   nsCycleCollector_forgetSkippable call
+   *                                   which happens after garbage collection.
    */
-  void garbageCollect([optional] in nsICycleCollectorListener aListener);
+  void garbageCollect([optional] in nsICycleCollectorListener aListener,
+                      [optional] in long aExtraForgetSkippableCalls);
 
   /**
    * Force a cycle collection without garbage collection.
    *
    * Will throw a DOM security error if called without UniversalXPConnect
    * privileges in non-debug builds. Available to all callers in debug builds.
    *
    * @param aListener listener that receives information about the CC graph
    *                  (see @mozilla.org/cycle-collector-logger;1 for a logger
    *                   component)
+   * @param aExtraForgetSkippableCalls indicates how many times
+   *                                   nsCycleCollector_forgetSkippable will
+   *                                   be called before running cycle collection.
+   *                                   -1 prevents the default
+   *                                   nsCycleCollector_forgetSkippable call
+   *                                   which happens after garbage collection.
    */
-  void cycleCollect([optional] in nsICycleCollectorListener aListener);
+  void cycleCollect([optional] in nsICycleCollectorListener aListener,
+                    [optional] in long aExtraForgetSkippableCalls);
 
   /** Synthesize a simple gesture event for a window. The event types
    *  supported are: MozSwipeGesture, MozMagnifyGestureStart,
    *  MozMagnifyGestureUpdate, MozMagnifyGesture, MozRotateGestureStart,
    *  MozRotateGestureUpdate, MozRotateGesture, MozPressTapGesture, and
    *  MozTapGesture.
    *
    * Cannot be accessed from unprivileged context (not
--- a/dom/wifi/nsWifiWorker.js
+++ b/dom/wifi/nsWifiWorker.js
@@ -478,25 +478,44 @@ var WifiManager = (function() {
     var handler = manager["on" + eventName];
     if (handler) {
       if (!eventObject)
         eventObject = ({});
       handler.call(eventObject);
     }
   }
 
+  function parseStatus(status) {
+    if (status === null) {
+      debug("Unable to get wpa supplicant's status");
+      return;
+    }
+
+    var lines = status.split("\n");
+    for (let i = 0; i < lines.length; ++i) {
+      let [key, value] = lines[i].split("=");
+      if (key === "wpa_state") {
+        if (value === "COMPLETED")
+          onconnected();
+      }
+    }
+  }
+
   // try to connect to the supplicant
   var connectTries = 0;
   var retryTimer = null;
   function connectCallback(ok) {
     if (ok === 0) {
-      // tell the event worker to start waiting for events
+      // Tell the event worker to start waiting for events.
       retryTimer = null;
       waitForEvent();
       notify("supplicantconnection");
+
+      // Load up the supplicant state.
+      statusCommand(parseStatus);
       return;
     }
     if (connectTries++ < 3) {
       // try again in 5 seconds
       if (!retryTimer)
         retryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
       retryTimer.initWithCallback(function(timer) {
@@ -783,51 +802,27 @@ function nsWifiWorker() {
   }
   WifiManager.onsupplicantlost = function() {
     debug("Couldn't connect to supplicant");
   }
 
   var networks = Object.create(null);
   WifiManager.onscanresultsavailable = function() {
     debug("Scan results are available! Asking for them.");
-    if (networks["Mozilla Guest"])
-      return;
     WifiManager.getScanResults(function(r) {
       let lines = r.split("\n");
       // NB: Skip the header line.
-      let added = !("Mozilla Guest" in networks);
       for (let i = 1; i < lines.length; ++i) {
         // bssid / frequency / signal level / flags / ssid
         var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s+(.*)/.exec(lines[i])
         if (match)
           networks[match[5]] = match[1];
         else
           debug("Match didn't find anything for: " + lines[i]);
       }
-
-      if (("Mozilla Guest" in networks) && added) {
-        debug("Mozilla Guest exists in networks, trying to connect!");
-        var config = Object.create(null);
-        config["ssid"] = '"Mozilla Guest"';
-        //config["bssid"] = '"' + networks["Mozilla Guest"] + '"';
-        config["key_mgmt"] = "NONE";
-        config["scan_ssid"] = 1;
-        WifiManager.addNetwork(config, function (ok) {
-          if (ok) {
-            WifiManager.enableNetwork(config.netId, false, function (ok) {
-              if (ok)
-                debug("Enabled the network!");
-              else
-                debug("Failed to enable the network :(");
-            });
-          } else {
-            debug("Failed to add the network :(");
-          }
-        });
-      }
     });
   }
 
   WifiManager.setWifiEnabled(true, function (ok) {
       if (ok === 0)
         WifiManager.start();
       else
         debug("Couldn't start Wifi");
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -227,26 +227,26 @@ public:
   }
 
   NS_IMETHOD
   CollectReports(nsIMemoryMultiReporterCallback* aCallback,
                  nsISupports* aClosure)
   {
     AssertIsOnMainThread();
 
-    JS::IterateData data(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
-                         xpc::DestroyCompartmentName);
-    nsresult rv = CollectForRuntime(/* isQuick = */false, &data);
+    JS::RuntimeStats rtStats(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
+                             xpc::DestroyCompartmentName);
+    nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Always report, even if we're disabled, so that we at least get an entry
     // in about::memory.
-    ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure);
+    ReportJSRuntimeStats(rtStats, mPathPrefix, aCallback, aClosure);
 
     return NS_OK;
   }
 
   NS_IMETHOD
   GetExplicitNonHeap(PRInt64 *aAmount)
   {
     AssertIsOnMainThread();
@@ -1519,17 +1519,17 @@ public:
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JSAutoSuspendRequest asr(aCx);
 
     *mSucceeded = mIsQuick
       ? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), xpc::JsMallocSizeOf)
-      : JS::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), static_cast<JS::IterateData*>(mData));
+      : JS::CollectRuntimeStats(JS_GetRuntime(aCx), static_cast<JS::RuntimeStats*>(mData));
 
     {
       MutexAutoLock lock(mMutex);
       mDone = true;
       mCondVar.Notify();
     }
 
     return true;
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -194,13 +194,13 @@ RES_DRAWABLE = $(addprefix res/drawable/
 $(RESOURCES): $(RES_DIRS) $(subst res/,$(srcdir)/resources/,$(RESOURCES))
 	@echo "creating $@"
 	$(NSINSTALL) $(subst res/,$(srcdir)/resources/,$@) $(dir $@)
 
 
 R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable/icon.png res/drawable-hdpi/icon.png AndroidManifest.xml chrome
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko
 
-gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/values/strings.xml FORCE
+gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) chrome FORCE
 	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
 
 libs:: classes.dex
 	$(INSTALL) classes.dex $(FINAL_TARGET)
--- a/extensions/auth/nsAuthGSSAPI.cpp
+++ b/extensions/auth/nsAuthGSSAPI.cpp
@@ -160,17 +160,18 @@ gssInit()
             "gss",
             "gssapi_krb5",
             "gssapi"
         };
         
         const char *const verLibNames[] = {
             "libgssapi_krb5.so.2", /* MIT - FC, Suse10, Debian */
             "libgssapi.so.4",      /* Heimdal - Suse10, MDK */
-            "libgssapi.so.1"       /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/
+            "libgssapi.so.1",      /* Heimdal - Suse9, CITI - FC, MDK, Suse10*/
+            "libgssapi.so"         /* OpenBSD */
         };
 
         for (size_t i = 0; i < ArrayLength(verLibNames) && !lib; ++i) {
             lib = PR_LoadLibrary(verLibNames[i]);
  
             /* The CITI libgssapi library calls exit() during
              * initialization if it's not correctly configured. Try to
              * ensure that we never use this library for our GSSAPI
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/upstream-hunspell.diff
@@ -9138,428 +9138,430 @@ 15460a21264,21266
 > cancelled/U
 > canceller/M
 > cancelling
 15559a21366
 > capita
 15629,15630d21435
 < carburetter/SM
 < carburettor/SM
-15788d21592
+15701a21507
+> carnitas
+15788d21593
 < cashpoint/S
-15797d21600
+15797d21601
 < cassino/M
-15832a21636
+15832a21637
 > catalyses
-15940d21743
+15940d21744
 < caviare/M
-16372c22175
+16372c22176
 < chickenshit/S!
 ---
 > chickenshit/SM!
-16404c22207
+16404c22208
 < children
 ---
 > children/M
-16488d22290
+16488d22291
 < chlorophyl/M
-16629,16630c22431
+16629,16630c22432
 < cider's
 < cider/S
 ---
 > cider/MS
-17072d22872
+17072d22873
 < cocain/M
-17102,17103c22902
+17102,17103c22903
 < cocksucker's
 < cocksucker/S!
 ---
 > cocksucker/SM!
-17755c23554
+17755c23555
 < confer/S
 ---
 > confer/SB
-18151d23949
+18151d23950
 < convenor/S
-18206c24004
+18206c24005
 < cookie/M
 ---
 > cookie/SM
-18467a24266
+18467a24267
 > could've
-19246c25045
+19246c25046
 < cysteine
 ---
 > cysteine/M
-20196,20197c25995,25996
+20196,20197c25996,25997
 < dialog/SM
 < dialogue/SM
 ---
 > dialog/SMGD
 > dialogue/SMRGD
-20481a26281
+20481a26282
 > disclose/DSG
-20830c26630
+20830c26631
 < dogie/M
 ---
 > dogie/SM
-20895a26696
+20895a26697
 > donator/MS
-21820a27622
+21820a27623
 > elicitor/MS
-22071a27874
+22071a27875
 > encyclopaedia
-22556a28360
+22556a28361
 > estoppel
-22638c28442
+22638c28443
 < euthanize
 ---
 > euthanize/DSG
-22719a28524
+22719a28525
 > exabyte/MS
-22947a28753
+22947a28754
 > experimentalism
-23207,23208d29012
+23207,23208d29013
 < faecal
 < faeces/M
-23215c29019
+23215c29020
 < faggoting's
 ---
 > faggot/SMG
-23701a29506
+23701a29507
 > filesystem/MS
-24155c29960
+24155c29961
 < fluidized
 ---
 > fluidize/DSG
-24216a30022
+24216a30023
 > foci
-24736d30541
+24736d30542
 < frier/M
-24855,24856c30660,30661
+24855,24856c30661,30662
 < fucker/M!
 < fuckhead/S!
 ---
 > fucker/SM!
 > fuckhead/SM!
-24953d30757
+24953d30758
 < furore/MS
-25125c30929
+25125c30930
 < gaolbird/S
 ---
 > gaolbirds
-25180d30983
+25180d30984
 < gasolene/M
-25190a30994
+25190a30995
 > gastroenterologist/M
-25262c31066
+25262c31067
 < geezer/M
 ---
 > geezer/MS
-25327c31131
+25327c31132
 < genomic
 ---
 > genomic/S
-25462a31267
+25462a31268
 > gigabit/MS
-25464a31270,31272
+25464a31271,31273
 > gigajoule/MS
 > gigapixel/MS
 > gigawatt/MS
-25560d31367
+25560d31368
 < glamourize/DSG
-25674c31481
+25674c31482
 < glycerine's
 ---
 > glycerine/M
-25905c31712
+25905c31713
 < gram/MS
 ---
 > gram/KMS
-25909d31715
+25909d31716
 < gramme/SM
-26063c31869,31870
+26063c31870,31871
 < greybeard
 ---
 > grey/MDRTGSP
 > greybeard/SM
-26066c31873
+26066c31874
 < greyness
 ---
 > greyness/M
-26246,26247d32052
+26246,26247d32053
 < guerilla's
 < guerillas
-26432,26436d32236
+26432,26436d32237
 < haemoglobin's
 < haemophilia/M
 < haemorrhage/DSMG
 < haemorrhoid/S
 < haemorrhoids/M
-27167c32967
+27167c32968
 < hexane
 ---
 > hexane/SM
-27273a33074
+27273a33075
 > hippopotami
-27875d33675
+27875d33676
 < hyaena/SM
-28017c33817
+28017c33818
 < iPod/M
 ---
 > iPod/MS
-28105a33906
+28105a33907
 > idolator/SM
-28513c34314
+28513c34315
 < inbound
 ---
 > inbound/s
-28650a34452
+28650a34453
 > indices
-28812d34613
+28812d34614
 < inflexion/SM
-29216a35018
+29216a35019
 > intern/GDL
-29272a35075,35078
+29272a35076,35079
 > intersex
 > intersexual/MS
 > intersexualism
 > intersexuality
-29724c35530
+29724c35531
 < jewellery's
 ---
 > jewellery/M
-29870a35677
+29870a35678
 > judgement/MS
-30066c35873
+30066c35874
 < kiddie/M
 ---
 > kiddie/SM
-30262,30263c36069
+30262,30263c36070
 < kraut's
 < kraut/S!
 ---
 > kraut/MS!
-30665a36472
+30665a36473
 > lector/MS
-31031c36838
+31031c36839
 < linguini's
 ---
 > linguini/M
-31151,31152c36958
+31151,31152c36959
 < liver's
 < liver/S
 ---
 > liver/MS
-32230c38036
+32230c38037
 < meanie/M
 ---
 > meanie/MS
-32317,32318c38123
+32317,32318c38124
 < megadeath/M
 < megadeaths
 ---
 > megadeath/SM
-32320c38125
+32320c38126
 < megajoules
 ---
 > megajoule/SM
-32329c38134
+32329c38135
 < megapixel/S
 ---
 > megapixel/MS
-32708a38514
+32708a38515
 > might've
-32777d38582
+32777d38583
 < millionnaire/M
-32934a38740
+32934a38741
 > miscommunication/S
-32991a38798
+32991a38799
 > misjudgement/MS
-33784a39592
+33784a39593
 > must've
-33963c39771
+33963c39772
 < native/MS
 ---
 > native/MSY
-34169,34171c39977,39978
+34169,34171c39978,39979
 < neurone/S
 < neurophysiology
 < neuroscience
 ---
 > neurophysiology/M
 > neuroscience/MS
-34275c40082
+34275c40083
 < nightie/M
 ---
 > nightie/SM
-35104a40912
+35104a40913
 > octopi
-35219d41026
+35219d41027
 < oleomargarin/M
-35226a41034
+35226a41035
 > oligo
-35913c41721
+35913c41722
 < oversize/D
 ---
 > oversize
-36056,36059d41863
+36056,36059d41864
 < paederast/S
 < paediatrician's
 < paediatricians
 < paediatrics/M
-36291a42096
+36291a42097
 > paralyses
-36403d42207
+36403d42208
 < parrakeet/MS
-36449d42252
+36449d42253
 < partizan/SM
-37093a42897
+37093a42898
 > petabyte/MS
-37102c42906
+37102c42907
 < petitioner/M
 ---
 > petitioner/MS
-37264a43069
+37264a43070
 > phosphorylate/DSGN
-37316d43120
+37316d43121
 < phrenetic
-37796a43601
+37796a43602
 > plugin/MS
-37987c43792
+37987c43793
 < polypeptide/S
 ---
 > polypeptide/MS
-38291d44095
+38291d44096
 < practise's
-38451a44256
+38451a44257
 > prejudgement/MS
-38891a44697,44698
+38891a44698,44699
 > pronate/DSGN
 > pronator/MS
-38951c44758
+38951c44759
 < proprietorship/M
 ---
 > proprietorship/MS
-39039a44847
+39039a44848
 > provender/M
-40036a45845
+40036a45846
 > recency
-40141a45951
+40141a45952
 > recuse/DGS
-40208a46019
+40208a46020
 > refactor/SMDG
-40244d46054
+40244d46055
 < reflexion/SM
-40829c46639
+40829c46640
 < reverie/M
 ---
 > reverie/MS
-41415a47226
+41415a47227
 > sabre/MS
-41914c47725
+41914c47726
 < schnaps's
 ---
 > schnaps/M
-41949c47760
+41949c47761
 < schrod's
 ---
 > schrod/SM
-41998a42010
+41998a47811
 > scot-free
-42883,42885c48695
+42883,42885c48696
 < shit's
 < shit/S!
 < shite/S!
 ---
 > shit/MS!
-42887,42888c48697,48698
+42887,42888c48698,48699
 < shithead/S!
 < shitload/!
 ---
 > shithead/MS!
 > shitload/MS!
-42891c48701
+42891c48702
 < shitty/RT!
 ---
 > shitty/TR!
-42976a48787
+42976a48788
 > should've
-43008c48819
+43008c48820
 < showtime
 ---
 > showtime/MS
-43724,43726c49535
+43724,43726c49536
 < smoulder's
 < smouldered
 < smoulders
 ---
 > smoulder/GSMD
-44062c49871
+44062c49872
 < sonofabitch
 ---
 > sonofabitch/!
-44371a50181
+44371a50182
 > spick/S!
-44383c50193
+44383c50194
 < spik/S
 ---
 > spik/S!
-46106a51917
+46106a51918
 > syllabi
-46160c51971
+46160c51972
 < synch/GMD
 ---
 > synch/GMDS
-46167d51977
+46167d51978
 < synchs
-46203,46204c52013,52014
+46203,46204c52014,52015
 < sysadmin/S
 < sysop/S
 ---
 > sysadmin/MS
 > sysop/MS
-46752a52563
+46752a52564
 > terabit/MS
-46753a52565,52566
+46753a52566,52567
 > terahertz/M
 > terapixel/MS
-46817a52631
+46817a52632
 > testcase/MS
-46831a52646
+46831a52647
 > testsuite/MS
-46925a52741
+46925a52742
 > theremin/MS
-47755a53572
+47755a53573
 > transfect/DSMG
-47774a53592,53593
+47774a53593,53594
 > transgenderism
 > transgene/MS
-47951c53770
+47951c53771
 < triage/M
 ---
 > triage/MG
-48869a54689
+48869a54690
 > unlikeable
-49211c55031
+49211c55032
 < vagina/M
 ---
 > vagina/MS