Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 31 Jan 2012 12:17:35 -0800
changeset 86693 ce25bca1ad68dd9e4b87644c001804071a4bf600
parent 86548 990ee31c370a209355cb2d85d07be97b085108b8 (current diff)
parent 86692 29514d9b42165ca04c0d5a8d7dae9934c0dc1955 (diff)
child 86694 4e487dfde168fd6bd9ba69aaf747e6d5e6cd2536
push idunknown
push userunknown
push dateunknown
milestone13.0a1
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