Rollup of bug 615386, parts 1-6. Teach the reftest harness about <browser remote>. r=dbaron,roc,ted a=a
authorChris Jones <jones.chris.g@gmail.com>
Thu, 03 Feb 2011 13:54:10 -0600
changeset 61873 c1523d3f78410892b1750d98e414171ca4793fe3
parent 61865 9c815db836e360e9ca4fa4c6f54f31852e13f004
child 61875 41258e566f2e5ccb14cee2c645635d9f811cc522
push idunknown
push userunknown
push dateunknown
reviewersdbaron, roc, ted, a
bugs615386
milestone2.0b12pre
Rollup of bug 615386, parts 1-6. Teach the reftest harness about <browser remote>. r=dbaron,roc,ted a=a
content/html/content/reftests/autofocus/reftest.list
editor/libeditor/html/crashtests/crashtests.list
editor/txmgr/tests/crashtests/crashtests.list
layout/base/crashtests/crashtests.list
layout/generic/crashtests/crashtests.list
layout/reftests/bugs/reftest.list
layout/reftests/css-placeholder/input/reftest.list
layout/reftests/css-placeholder/textarea/reftest.list
layout/reftests/css-ui-invalid/default-style/reftest.list
layout/reftests/editor/reftest.list
layout/reftests/forms/placeholder/reftest.list
layout/reftests/reftest-sanity/needs-focus.html
layout/reftests/reftest-sanity/reftest.list
layout/tools/reftest/README.txt
layout/tools/reftest/jar.mn
layout/tools/reftest/print-manifest-dirs.py
layout/tools/reftest/reftest-content.js
layout/tools/reftest/reftest.js
layout/tools/reftest/reftest.xul
modules/plugin/test/crashtests/crashtests.list
security/manager/ssl/crashtests/crashtests.list
testing/testsuite-targets.mk
--- a/content/html/content/reftests/autofocus/reftest.list
+++ b/content/html/content/reftests/autofocus/reftest.list
@@ -1,11 +1,11 @@
-== input-load.html input-ref.html
-== input-create.html input-ref.html
-== button-load.html button-ref.html
-== button-create.html button-ref.html
-== textarea-load.html textarea-ref.html
-== textarea-create.html textarea-ref.html
-== select-load.html select-ref.html
-== select-create.html select-ref.html
-== autofocus-after-load.html autofocus-after-load-ref.html
-== autofocus-leaves-iframe.html autofocus-leaves-iframe-ref.html
-== autofocus-after-body-focus.html autofocus-after-body-focus-ref.html
+needs-focus == input-load.html input-ref.html
+needs-focus == input-create.html input-ref.html
+needs-focus == button-load.html button-ref.html
+needs-focus == button-create.html button-ref.html
+needs-focus == textarea-load.html textarea-ref.html
+needs-focus == textarea-create.html textarea-ref.html
+needs-focus == select-load.html select-ref.html
+needs-focus == select-create.html select-ref.html
+needs-focus == autofocus-after-load.html autofocus-after-load-ref.html
+needs-focus == autofocus-leaves-iframe.html autofocus-leaves-iframe-ref.html
+needs-focus == autofocus-after-body-focus.html autofocus-after-body-focus-ref.html
--- a/editor/libeditor/html/crashtests/crashtests.list
+++ b/editor/libeditor/html/crashtests/crashtests.list
@@ -6,17 +6,17 @@ load 418923-1.html
 load 420439.html
 load 428489-1.html
 load 431086-1.xhtml
 load 448329-1.html
 load 448329-2.html
 load 448329-3.html
 load 456727-1.html
 load 456727-2.html
-asserts(2) load 467647-1.html # bug 382210, bug 414178
+needs-focus asserts(2) load 467647-1.html # bug 382210, bug 414178
 load 499844-1.html
 load 503709-1.xhtml
 load 513375-1.xhtml
 load 535632-1.xhtml
 load 574558-1.xhtml
 load 582138-1.xhtml
 load 612565-1.html
 load 615015-1.html
--- a/editor/txmgr/tests/crashtests/crashtests.list
+++ b/editor/txmgr/tests/crashtests/crashtests.list
@@ -1,2 +1,2 @@
-asserts(3) load 407072-1.html # bug 382210?
+needs-focus asserts(3) load 407072-1.html # bug 382210?
 load 449006-1.html
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -88,27 +88,27 @@ load 336291-1.html
 load 336999-1.xul
 load 337066-1.xhtml
 load 337268-1.html
 load 337419-1.html
 load 337476-1.xul
 load 338703-1.html
 load 339651-1.html
 load 340093-1.xul
-load 341858-1.html
+asserts-if(browserIsRemote,1) load 341858-1.html # bug 622188
 load 342145-1.xhtml
 load 343293-1.xhtml
 load 343293-2.xhtml
 load 343540-1.html
 load 344057-1.xhtml
 load 344064-1.html
 load 344300-1.html
 load 344300-2.html
 load 344340-1.xul
-load 347898-1.html
+asserts-if(browserIsRemote,1) load 347898-1.html # bug 622188
 load 348126-1.html
 load 348688-1.html
 load 348708-1.xhtml
 asserts(2) load 348729-1.html # bug 548836
 load 349095-1.xhtml
 load 350128-1.xhtml
 load 350267-1.html
 load 354133-1.html
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -30,17 +30,17 @@ load 334602-1.html
 load 337412-1.html
 load 337883-1.html
 load 337883-2.html
 load 339769-1.html
 load 342322-1.html
 load 343206-1.xhtml
 load 345139-1.xhtml
 load 345617-1.html
-load 348887-1.html
+skip load 348887-1.html # bug 623091
 load 348991-1.xhtml
 load 354458-1.html
 load 354458-2.html
 load 355426-1.html
 load 359371-1.html
 load 359371-2.html
 load 363722-1.html
 load 363722-2.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -142,17 +142,17 @@ random == 99850-1b.html 99850-1-ref.html
 == 179596-1b.html 179596-1b-ref.html
 == 179596-2.html 179596-2-ref.html
 == 179596-2.html 179596-2-ref2.html
 == 179596-2.html 179596-2-ref3.html
 == 180085-1.html 180085-1-ref.html
 == 180085-2.html 180085-2-ref.html
 == 185388-1.html 185388-1-ref.html
 == 192902-1.html 192902-ref.html
-!= 200774-1.html about:blank # really a crashtest
+!= 200774-1.html about:blank
 == 201215-1.html 201215-1-ref.html
 == 201293-1a.html 201293-1-ref.html
 == 201293-1b.html 201293-1-ref.html
 == 201293-1c.html 201293-1-ref.html
 == 201293-1d.html 201293-1-ref.html
 == 203727.html 203727-ref.html
 == 206516-1.html 206516-1-ref.html
 == 206631-1.html 206631-1-ref.html
@@ -1350,17 +1350,17 @@ random-if(cocoaWidget&&layersGPUAccelera
 == 502942-1.html 502942-1-ref.html
 == 502447-1.html 502447-1-ref.html
 == 502795-1.html 502795-1-ref.html
 == 503364-1a.html 503364-1-ref.html
 == 503364-1b.html 503364-1-ref.html
 == 503399.html 503399-ref.html
 # Reftest for bug 503531 marked as failing; should be re-enabled when
 # bug 607548 gets resolved.
-fails == 503531-1.html 503531-1-ref.html
+needs-focus fails == 503531-1.html 503531-1-ref.html
 == 504032-1.html 504032-1-ref.html
 == 505743-1.html about:blank
 == 506481-1.html 506481-1-ref.html
 == 507187-1.html 507187-1-ref.html
 == 507487-1.html 507487-1-ref.html
 == 507487-2.xhtml 507487-2-ref.xhtml
 == 507762-1.html 507762-1-ref.html
 == 507762-2.html 507762-2-ref.html
@@ -1423,19 +1423,19 @@ asserts(5) == 528038-2.html 528038-2-ref
 == 539323-1.html 539323-1-ref.html
 == 539323-2.html 539323-2-ref.html
 == 539323-3.html 539323-3-ref.html
 == 539880-1.html 539880-1-ref.html
 == 539880-1-dynamic.html 539880-1-ref.html
 == 539949-1.html#test2 539949-1-ref.html#test2
 == 541382-1.html 541382-1-ref.html
 random-if(!haveTestPlugin) == 541406-1.html 541406-1-ref.html
-!= 542116-1.html 542116-1-ref.html
-asserts(1) != 542116-2.html 542116-2-ref.html # bug 596901
-!= 542116-3.html 542116-3-ref.html
+needs-focus != 542116-1.html 542116-1-ref.html
+needs-focus asserts(1) != 542116-2.html 542116-2-ref.html # bug 596901
+needs-focus != 542116-3.html 542116-3-ref.html
 == 542317-1.html 542317-1-ref.html
 == 542605-hidden-unscrollable.xul 542605-hidden-unscrollable-ref.xul
 == 542620-1.html 542620-1-ref.html
 == 545049-1.html 545049-1-ref.html
 == 546033-1.html 546033-1-ref.html
 random-if(!haveTestPlugin) == 546071-1.html 546071-1-ref.html
 == 549184-1.html 549184-1-ref.html
 == 550325-1.html 550325-1-ref.html
@@ -1483,17 +1483,17 @@ random-if(d2d) == 555388-1.html 555388-1
 == 563584-9d.html 563584-9cd-ref.html
 == 563584-10a.html 563584-10-ref.html
 == 563584-10b.html 563584-10-ref.html
 == 563584-11.html 563584-11-ref.html
 == 564054-1.html 564054-1-ref.html
 random-if(layersGPUAccelerated) == 564991-1.html 564991-1-ref.html
 == 565819-1.html 565819-ref.html
 == 565819-2.html 565819-ref.html
-== 568441.html 568441-ref.html
+needs-focus == 568441.html 568441-ref.html
 == 569006-1.html 569006-1-ref.html
 == 571281-1a.html 571281-1-ref.html
 == 571281-1b.html 571281-1-ref.html
 == 571281-1c.html 571281-1-ref.html
 == 571347-1a.html 571347-1-ref.html
 == 571347-1b.html 571347-1-ref.html
 == 571347-2a.html 571347-2-ref.html
 == 571347-2b.html 571347-2-ref.html
@@ -1548,25 +1548,25 @@ fails-if(!haveTestPlugin) == 599476.html
 == 602200-2.html 602200-2-ref.html
 == 602200-3.html 602200-3-ref.html
 == 602200-4.html 602200-4-ref.html
 == 604737.html 604737-ref.html
 == 605138-1.html 605138-1-ref.html
 == 605157-1.xhtml 605157-1-ref.xhtml
 == 609272-1.html 609272-1-ref.html
 == 608636-1.html 608636-1-ref.html
-== 613433-1.html 613433-1-ref.html
-== 613433-1.html 613433-2-ref.html
-== 613433-1.html 613433-3-ref.html
-== 613433-2.html 613433-1-ref.html
-== 613433-2.html 613433-2-ref.html
-== 613433-2.html 613433-3-ref.html
-== 613433-3.html 613433-1-ref.html
-== 613433-3.html 613433-2-ref.html
-== 613433-3.html 613433-3-ref.html
+needs-focus == 613433-1.html 613433-1-ref.html
+needs-focus == 613433-1.html 613433-2-ref.html
+needs-focus == 613433-1.html 613433-3-ref.html
+needs-focus == 613433-2.html 613433-1-ref.html
+needs-focus == 613433-2.html 613433-2-ref.html
+needs-focus == 613433-2.html 613433-3-ref.html
+needs-focus == 613433-3.html 613433-1-ref.html
+needs-focus == 613433-3.html 613433-2-ref.html
+needs-focus == 613433-3.html 613433-3-ref.html
 == 614272-1.svg  614272-1-ref.svg
 HTTP(..) == 615121-1.html 615121-1-ref.html
 HTTP(..) != 615121-2.html 615121-2-notref.html
 == 617242-1.html 617242-1-ref.html
 != 618071.html 618071-notref.html
 == 619117-1.html 619117-1-ref.html
 HTTP(..) == 621253-1-externalFilter.html 621253-1-ref.html # XXX update reference case after bug 541270 is fixed
 == 621253-1-internalFilter.html 621253-1-ref.html # XXX update reference case after bug 541270 is fixed
--- a/layout/reftests/css-placeholder/input/reftest.list
+++ b/layout/reftests/css-placeholder/input/reftest.list
@@ -1,11 +1,11 @@
 == placeholder-simple.html placeholder-simple-ref.html
-== placeholder-focus.html placeholder-focus-ref.html
-== placeholder-blur.html placeholder-simple-ref.html
+needs-focus == placeholder-focus.html placeholder-focus-ref.html
+needs-focus == placeholder-blur.html placeholder-simple-ref.html
 == placeholder-value.html placeholder-value-ref.html
 == placeholder-empty-string.html placeholder-empty-string-ref.html
 == placeholder-complex.html placeholder-complex-ref.html
 == placeholder-add.html placeholder-simple-ref.html
 == placeholder-removal.html input-ref.html
 == placeholder-value-set.html placeholder-value-ref.html
 == placeholder-value-unset.html placeholder-simple-ref.html
 == placeholder-value-reset.html placeholder-simple-ref.html
--- a/layout/reftests/css-placeholder/textarea/reftest.list
+++ b/layout/reftests/css-placeholder/textarea/reftest.list
@@ -1,11 +1,11 @@
 == placeholder-simple.html placeholder-simple-ref.html
-== placeholder-focus.html placeholder-focus-ref.html
-== placeholder-blur.html placeholder-simple-ref.html
+needs-focus == placeholder-focus.html placeholder-focus-ref.html
+needs-focus == placeholder-blur.html placeholder-simple-ref.html
 == placeholder-value.html placeholder-value-ref.html
 == placeholder-empty-string.html placeholder-empty-string-ref.html
 == placeholder-complex.html placeholder-complex-ref.html
 == placeholder-add.html placeholder-simple-ref.html
 == placeholder-removal.html textarea-ref.html
 == placeholder-value-set.html placeholder-value-ref.html
 == placeholder-value-unset.html placeholder-simple-ref.html
 == placeholder-value-reset.html placeholder-simple-ref.html
--- a/layout/reftests/css-ui-invalid/default-style/reftest.list
+++ b/layout/reftests/css-ui-invalid/default-style/reftest.list
@@ -1,11 +1,11 @@
 == input.html input-ref.html
 == button.html button-ref.html
 == textarea.html textarea-ref.html
 == select.html select-ref.html
 == fieldset.html fieldset-ref.html
 == output.html output-ref.html
-== input-focus.html input-focus-ref.html
-== button-focus.html button-focus-ref.html
-== textarea-focus.html textarea-focus-ref.html
-== select-focus.html select-focus-ref.html
-== textarea-focus.html textarea-focus-ref.html
+needs-focus == input-focus.html input-focus-ref.html
+needs-focus == button-focus.html button-focus-ref.html
+needs-focus == textarea-focus.html textarea-focus-ref.html
+needs-focus == select-focus.html select-focus-ref.html
+
--- a/layout/reftests/editor/reftest.list
+++ b/layout/reftests/editor/reftest.list
@@ -8,17 +8,17 @@ include xul/reftest.list
 == dynamic-1.html dynamic-ref.html
 == dynamic-type-1.html dynamic-ref.html
 == dynamic-type-2.html dynamic-ref.html
 == dynamic-type-3.html dynamic-ref.html
 == dynamic-type-4.html dynamic-ref.html
 == passwd-1.html passwd-ref.html
 != passwd-2.html passwd-ref.html
 == passwd-3.html passwd-ref.html
-asserts(1) == passwd-4.html passwd-ref.html # bug 596901
+needs-focus asserts(1) == passwd-4.html passwd-ref.html # bug 596901
 == emptypasswd-1.html emptypasswd-ref.html
 == emptypasswd-2.html emptypasswd-ref.html
 == caret_on_positioned.html caret_on_positioned-ref.html
 != spellcheck-input-disabled.html spellcheck-input-ref.html
 == spellcheck-input-attr-before.html spellcheck-input-ref.html
 == spellcheck-input-attr-after.html spellcheck-input-ref.html
 == spellcheck-input-attr-inherit.html spellcheck-input-ref.html
 == spellcheck-input-attr-dynamic.html spellcheck-input-ref.html
@@ -36,14 +36,14 @@ asserts(1) == passwd-4.html passwd-ref.h
 != spellcheck-textarea-attr-dynamic-inherit.html spellcheck-textarea-ref.html
 != spellcheck-textarea-property-dynamic.html spellcheck-textarea-ref.html
 != spellcheck-textarea-property-dynamic-inherit.html spellcheck-textarea-ref.html
 != spellcheck-textarea-attr-dynamic-override.html spellcheck-textarea-ref.html
 != spellcheck-textarea-attr-dynamic-override-inherit.html spellcheck-textarea-ref.html
 != spellcheck-textarea-property-dynamic-override.html spellcheck-textarea-ref.html
 != spellcheck-textarea-property-dynamic-override-inherit.html spellcheck-textarea-ref.html
 == caret_on_focus.html caret_on_focus-ref.html
-!= caret_on_textarea_lastline.html caret_on_textarea_lastline-ref.html
-== input-text-onfocus-reframe.html input-text-onfocus-reframe-ref.html
-== input-text-notheme-onfocus-reframe.html input-text-notheme-onfocus-reframe-ref.html
-== caret_after_reframe.html caret_after_reframe-ref.html
+needs-focus != caret_on_textarea_lastline.html caret_on_textarea_lastline-ref.html
+needs-focus == input-text-onfocus-reframe.html input-text-onfocus-reframe-ref.html
+needs-focus == input-text-notheme-onfocus-reframe.html input-text-notheme-onfocus-reframe-ref.html
+needs-focus == caret_after_reframe.html caret_after_reframe-ref.html
 == nobogusnode-1.html nobogusnode-ref.html
 == nobogusnode-2.html nobogusnode-ref.html
--- a/layout/reftests/forms/placeholder/reftest.list
+++ b/layout/reftests/forms/placeholder/reftest.list
@@ -3,24 +3,24 @@
 == placeholder-1-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-2.html placeholder-visible-ref.html
 == placeholder-2-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-3.html placeholder-overridden-ref.html
 == placeholder-4.html placeholder-overridden-ref.html
 == placeholder-5.html placeholder-visible-ref.html
 == placeholder-6.html placeholder-overflow-ref.html
 == placeholder-6-textarea.html placeholder-overflow-textarea-ref.html
-== placeholder-7.html placeholder-focus-ref.html
-== placeholder-8.html placeholder-focus-ref.html
-== placeholder-9.html placeholder-focus-ref.html
-== placeholder-10.html placeholder-visible-ref.html
+needs-focus == placeholder-7.html placeholder-focus-ref.html
+needs-focus == placeholder-8.html placeholder-focus-ref.html
+needs-focus == placeholder-9.html placeholder-focus-ref.html
+needs-focus == placeholder-10.html placeholder-visible-ref.html
 == placeholder-11.html placeholder-visible-ref.html
 == placeholder-12.html placeholder-visible-ref.html
 == placeholder-13.html placeholder-visible-ref.html
 == placeholder-14.html placeholder-visible-ref.html
-== placeholder-15.html placeholder-focus-ref.html
-== placeholder-16.html placeholder-focus-ref.html
-== placeholder-17.html placeholder-focus-ref.html
+needs-focus == placeholder-15.html placeholder-focus-ref.html
+needs-focus == placeholder-16.html placeholder-focus-ref.html
+needs-focus == placeholder-17.html placeholder-focus-ref.html
 == placeholder-18.html placeholder-overridden-ref.html
 == placeholder-19.xul  placeholder-overridden-ref.xul
-== placeholder-20.html placeholder-focus-ref.html
-== placeholder-21.html placeholder-focus-ref.html
-== placeholder-22.html placeholder-focus-ref.html
+needs-focus == placeholder-20.html placeholder-focus-ref.html
+needs-focus == placeholder-21.html placeholder-focus-ref.html
+needs-focus == placeholder-22.html placeholder-focus-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/needs-focus.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <script type="text/javascript">
+function focusInput() {
+  document.getElementById('i').focus();
+}
+function done() {
+  document.documentElement.className = '';
+}
+  </script>
+  <body onload="focusInput();">
+    <input type="text" id="i" onfocus="done();">
+  </body>
+</html>
--- a/layout/reftests/reftest-sanity/reftest.list
+++ b/layout/reftests/reftest-sanity/reftest.list
@@ -73,8 +73,11 @@ include urlprefixtests.list
 
 # Test that the harness gives the correct page dimensions.
 != page-width-3.9in.html page-width-4in.html
 == page-width-4.1in.html page-width-4in.html
 == page-width-auto.html page-width-4in.html
 != page-height-2in.html page-height-2.1in.html
 == page-height-2in.html page-height-nobreak.html
 == page-height-2.1in.html page-height-forcebreak.html
+
+# Check that tests that need focus are skipped when it's not available
+needs-focus load needs-focus.html
--- a/layout/tools/reftest/README.txt
+++ b/layout/tools/reftest/README.txt
@@ -53,16 +53,19 @@ 2. A test item
       fails  The test passes if the images of the two renderings DO NOT
              meet the conditions specified in the <type>.
 
       fails-if(condition) If the condition is met, the test passes if the 
                           images of the two renderings DO NOT meet the 
                           conditions of <type>. If the condition is not met,
                           the test passes if the conditions of <type> are met.
 
+      needs-focus  The test fails or times out if the reftest window is not
+                   focused.
+
       random  The results of the test are random and therefore not to be
               considered in the output.
 
       random-if(condition) The results of the test are random if a given
                            condition is met.
 
       silentfail This test may fail silently, and if that happens it should
                  count as if the test passed. This is useful for cases where
--- a/layout/tools/reftest/jar.mn
+++ b/layout/tools/reftest/jar.mn
@@ -1,11 +1,12 @@
 reftest.jar:
 % content reftest %content/
 *  content/quit.js (quit.js)
 *  content/reftest.js (reftest.js)
+  content/reftest-content.js (reftest-content.js)
   content/reftest.xul (reftest.xul)
   content/MozillaFileLogger.js (../../../testing/mochitest/tests/SimpleTest/MozillaFileLogger.js)
 #ifdef XPI_NAME
 %  component {32530271-8c1b-4b7d-a812-218e42c6bb23} components/reftest-cmdline.js
 %  contract @mozilla.org/commandlinehandler/general-startup;1?type=reftest {32530271-8c1b-4b7d-a812-218e42c6bb23}
 %  category command-line-handler m-reftest @mozilla.org/commandlinehandler/general-startup;1?type=reftest
 #endif
--- a/layout/tools/reftest/print-manifest-dirs.py
+++ b/layout/tools/reftest/print-manifest-dirs.py
@@ -34,17 +34,17 @@
 # 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 *****
 
 import sys, os.path, re
 
 commentRE = re.compile(r"\s+#")
-conditionsRE = re.compile(r"^(fails|random|skip|asserts)")
+conditionsRE = re.compile(r"^(fails|needs-focus|random|skip|asserts)")
 httpRE = re.compile(r"HTTP\((\.\.(\/\.\.)*)\)")
 protocolRE = re.compile(r"^\w+:")
 
 def parseManifest(manifest, dirs):
   """Parse the reftest manifest |manifest|, adding all directories containing
   tests (and the dirs containing the manifests themselves) to the set |dirs|."""
   manifestdir = os.path.dirname(os.path.abspath(manifest))
   dirs.add(manifestdir)
copy from layout/tools/reftest/reftest.js
copy to layout/tools/reftest/reftest-content.js
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -35,167 +35,94 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 const CC = Components.classes;
 const CI = Components.interfaces;
 const CR = Components.results;
 
+/**
+ * FIXME/bug 622224: work around lack of reliable setTimeout available
+ * to frame scripts.
+ */
+// This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4
+// days.  Should be fine as a temporary workaround.
+var gNextTimeoutId = 0;
+var gTimeoutTable = { };        // int -> nsITimer
+
+function setTimeout(callbackFn, delayMs) {
+    var id = gNextTimeoutId++;
+    var timer = CC["@mozilla.org/timer;1"].createInstance(CI.nsITimer);
+    timer.initWithCallback({
+        notify: function notify_callback() {
+                    clearTimeout(id);
+                    callbackFn();
+                }
+        },
+        delayMs,
+        timer.TYPE_ONE_SHOT);
+
+    gTimeoutTable[id] = timer;
+
+    return id;
+}
+
+function clearTimeout(id) {
+    var timer = gTimeoutTable[id];
+    if (timer) {
+        timer.cancel();
+        delete gTimeoutTable[id];
+    }
+}
+
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
-const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1";
-const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1";
-const IO_SERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
 const DEBUG_CONTRACTID = "@mozilla.org/xpcom/debug;1";
-const NS_LOCALFILEINPUTSTREAM_CONTRACTID =
-          "@mozilla.org/network/file-input-stream;1";
-const NS_SCRIPTSECURITYMANAGER_CONTRACTID =
-          "@mozilla.org/scriptsecuritymanager;1";
-const NS_REFTESTHELPER_CONTRACTID =
-          "@mozilla.org/reftest-helper;1";
-const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX =
-          "@mozilla.org/network/protocol;1?name=";
-const NS_XREAPPINFO_CONTRACTID =
-          "@mozilla.org/xre/app-info;1";
-
-var gLoadTimeout = 0;
-var gTimeoutHook = null;
-var gRemote = false;
-var gTotalChunks = 0;
-var gThisChunk = 0;
+const PRINTSETTINGS_CONTRACTID = "@mozilla.org/gfx/printsettings-service;1";
 
 // "<!--CLEAR-->"
 const BLANK_URL_FOR_CLEARING = "data:text/html,%3C%21%2D%2DCLEAR%2D%2D%3E";
 
-var gBrowser;
-var gCanvas1, gCanvas2;
-// gCurrentCanvas is non-null between InitCurrentCanvasWithSnapshot and the next
-// RecordResult.
-var gCurrentCanvas = null;
-var gURLs;
-// Map from URI spec to the number of times it remains to be used
-var gURIUseCounts;
-// Map from URI spec to the canvas rendered for that URI
-var gURICanvases;
-var gTestResults = {
-  // Successful...
-  Pass: 0,
-  LoadOnly: 0,
-  // Unexpected...
-  Exception: 0,
-  FailedLoad: 0,
-  UnexpectedFail: 0,
-  UnexpectedPass: 0,
-  AssertionUnexpected: 0,
-  AssertionUnexpectedFixed: 0,
-  // Known problems...
-  KnownFail : 0,
-  AssertionKnown: 0,
-  Random : 0,
-  Skip: 0,
-  Slow: 0,
-};
-var gTotalTests = 0;
-var gState;
+var gBrowserIsRemote;
+var gHaveCanvasSnapshot = false;
 // Plugin layers can be updated asynchronously, so to make sure that all
 // layer surfaces have the right content, we need to listen for explicit
 // "MozPaintWait" and "MozPaintWaitFinished" events that signal when it's OK
 // to take snapshots. We cannot take a snapshot while the number of
 // "MozPaintWait" events fired exceeds the number of "MozPaintWaitFinished"
 // events fired. We count the number of such excess events here. When
 // the counter reaches zero we call gExplicitPendingPaintsCompleteHook.
 var gExplicitPendingPaintCount = 0;
 var gExplicitPendingPaintsCompleteHook;
 var gCurrentURL;
+var gCurrentTestType;
+var gTimeoutHook = null;
 var gFailureTimeout = null;
 var gFailureReason;
-var gTestLog = [];
-var gServer;
-var gCount = 0;
 var gAssertionCount = 0;
 
-var gIOService;
 var gDebug;
-var gWindowUtils;
 
 var gCurrentTestStartTime;
-var gSlowestTestTime = 0;
-var gSlowestTestURL;
 var gClearingForAssertionCheck = false;
 
-var gDrawWindowFlags;
-
-const TYPE_REFTEST_EQUAL = '==';
-const TYPE_REFTEST_NOTEQUAL = '!=';
-const TYPE_LOAD = 'load';     // test without a reference (just test that it does
-                              // not assert, crash, hang, or leak)
 const TYPE_SCRIPT = 'script'; // test contains individual test results
 
-const EXPECTED_PASS = 0;
-const EXPECTED_FAIL = 1;
-const EXPECTED_RANDOM = 2;
-const EXPECTED_DEATH = 3;  // test must be skipped to avoid e.g. crash/hang
-
-const gProtocolRE = /^\w+:/;
-
-var HTTP_SERVER_PORT = 4444;
-const HTTP_SERVER_PORTS_TO_TRY = 50;
-
-// whether to run slow tests or not
-var gRunSlowTests = true;
-
-// whether we should skip caching canvases
-var gNoCanvasCache = false;
-
-var gRecycledCanvases = new Array();
-
-// By default we just log to stdout
-var gDumpLog = dump;
-
-function LogWarning(str)
-{
-    gDumpLog("REFTEST INFO | " + str + "\n");
-    gTestLog.push(str);
+function markupDocumentViewer() { 
+    return docShell.contentViewer.QueryInterface(CI.nsIMarkupDocumentViewer);
 }
 
-function LogInfo(str)
-{
-    // gDumpLog("REFTEST INFO | " + str + "\n");
-    gTestLog.push(str);
-}
-
-function FlushTestLog()
-{
-    for (var i = 0; i < gTestLog.length; ++i) {
-        gDumpLog("REFTEST INFO | Saved log: " + gTestLog[i] + "\n");
-    }
-    gTestLog = [];
+function webNavigation() {
+    return docShell.QueryInterface(CI.nsIWebNavigation);
 }
 
-function AllocateCanvas()
-{
-    var windowElem = document.documentElement;
-
-    if (gRecycledCanvases.length > 0)
-        return gRecycledCanvases.shift();
-
-    var canvas = document.createElementNS(XHTML_NS, "canvas");
-    var r = gBrowser.getBoundingClientRect();
-    canvas.setAttribute("width", Math.ceil(r.width));
-    canvas.setAttribute("height", Math.ceil(r.height));
-
-    return canvas;
-}
-
-function ReleaseCanvas(canvas)
-{
-    // store a maximum of 2 canvases, if we're not caching
-    if (!gNoCanvasCache || gRecycledCanvases.length < 2)
-        gRecycledCanvases.push(canvas);
+function windowUtils() {
+    return content.QueryInterface(CI.nsIInterfaceRequestor)
+                  .getInterface(CI.nsIDOMWindowUtils);
 }
 
 function IDForEventTarget(event)
 {
     try {
         return "'" + event.target.getAttribute('id') + "'";
     } catch (ex) {
         return "<unknown>";
@@ -217,738 +144,106 @@ function PaintWaitFinishedListener(event
         gExplicitPendingPaintCount = 0;
     }
     if (gExplicitPendingPaintCount == 0 &&
         gExplicitPendingPaintsCompleteHook) {
         gExplicitPendingPaintsCompleteHook();
     }
 }
 
-function OnRefTestLoad()
+function OnInitialLoad()
 {
-    gBrowser = document.getElementById("browser");
-
-    /* set the gLoadTimeout */
-    try {
-      var prefs = Components.classes["@mozilla.org/preferences-service;1"].
-                  getService(Components.interfaces.nsIPrefBranch2);
-      gLoadTimeout = prefs.getIntPref("reftest.timeout");
-      logFile = prefs.getCharPref("reftest.logFile");
-      if (logFile) {
-        try {
-          MozillaFileLogger.init(logFile);
-          // Set to mirror to stdout as well as the file
-          gDumpLog = function (msg) {dump(msg); MozillaFileLogger.log(msg);};
-        }
-        catch(e) {
-          // If there is a problem, just use stdout
-          gDumpLog = dump;
-        }
-      }
-      gRemote = prefs.getBoolPref("reftest.remote");
-    }
-    catch(e) {
-      gLoadTimeout = 5 * 60 * 1000; //5 minutes as per bug 479518
-    }
-
-
-    /* Support for running a chunk (subset) of tests.  In separate try as this is optional */
-    try {
-      gTotalChunks = prefs.getIntPref("reftest.totalChunks");
-      gThisChunk = prefs.getIntPref("reftest.thisChunk");
-    }
-    catch(e) {
-      gTotalChunks = 0;
-      gThisChunk = 0;
-    }
-
-    gBrowser.addEventListener("load", OnDocumentLoad, true);
-
-    try {
-        gWindowUtils = window.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
-        if (gWindowUtils && !gWindowUtils.compareCanvases)
-            gWindowUtils = null;
-    } catch (e) {
-        gWindowUtils = null;
-    }
-
-    var windowElem = document.documentElement;
-
-    gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService);
-    gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
-    
-    if (gRemote) {
-      gServer = null;
-    } else {
-      gServer = CC["@mozilla.org/server/jshttp;1"].
-                    createInstance(CI.nsIHttpServer);
-    }
-    try {
-        if (gServer)
-            StartHTTPServer();
-    } catch (ex) {
-        //gBrowser.loadURI('data:text/plain,' + ex);
-        ++gTestResults.Exception;
-        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n");
-        DoneTests();
-    }
+    removeEventListener("load", OnInitialLoad, true);
 
-    // Focus the content browser
-    gBrowser.focus();
-
-    // Connect to async rendering notifications
-    gBrowser.addEventListener("MozPaintWait", PaintWaitListener, true);
-    gBrowser.addEventListener("MozPaintWaitFinished", PaintWaitFinishedListener, true);
-
-    StartTests();
-}
-
-function StartHTTPServer()
-{
-    gServer.registerContentType("sjs", "sjs");
-    // We want to try different ports in case the port we want
-    // is being used.
-    var tries = HTTP_SERVER_PORTS_TO_TRY;
-    do {
-        try {
-            gServer.start(HTTP_SERVER_PORT);
-            return;
-        } catch (ex) {
-            ++HTTP_SERVER_PORT;
-            if (--tries == 0)
-                throw ex;
-        }
-    } while (true);
-}
-
-function StartTests()
-{
-    try {
-        // Need to read the manifest once we have the final HTTP_SERVER_PORT.
-        var args = window.arguments[0].wrappedJSObject;
+    gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
 
-        if ("nocache" in args && args["nocache"])
-            gNoCanvasCache = true;
-
-        if ("skipslowtests" in args && args.skipslowtests)
-            gRunSlowTests = false;
-
-        ReadTopManifest(args.uri);
-        BuildUseCounts();
-
-        if (gTotalChunks > 0 && gThisChunk > 0) {
-          var testsPerChunk = gURLs.length / gTotalChunks;
-          var start = Math.round((gThisChunk-1) * testsPerChunk);
-          var end = Math.round(gThisChunk * testsPerChunk);
-          gURLs = gURLs.slice(start, end);
-          gDumpLog("REFTEST INFO | Running chunk " + gThisChunk + " out of " + gTotalChunks + " chunks.  ")
-          gDumpLog("tests " + (start+1) + "-" + end + "/" + gURLs.length + "\n");
-        }
-        gTotalTests = gURLs.length;
-
-        if (!gTotalTests)
-            throw "No tests to run";
-
-        gURICanvases = {};
-        StartCurrentTest();
-    } catch (ex) {
-        //gBrowser.loadURI('data:text/plain,' + ex);
-        ++gTestResults.Exception;
-        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n");
-        DoneTests();
-    }
-}
-
-function OnRefTestUnload()
-{
-    gBrowser.removeEventListener("load", OnDocumentLoad, true);
-    MozillaFileLogger.close();
-}
+    RegisterMessageListeners();
 
-// Read all available data from an input stream and return it
-// as a string.
-function getStreamContent(inputStream)
-{
-  var streamBuf = "";
-  var sis = CC["@mozilla.org/scriptableinputstream;1"].
-                createInstance(CI.nsIScriptableInputStream);
-  sis.init(inputStream);
-
-  var available;
-  while ((available = sis.available()) != 0) {
-    streamBuf += sis.read(available);
-  }
-  
-  return streamBuf;
-}
-
-// Build the sandbox for fails-if(), etc., condition evaluation.
-function BuildConditionSandbox(aURL) {
-    var sandbox = new Components.utils.Sandbox(aURL.spec);
-    var xr = CC[NS_XREAPPINFO_CONTRACTID].getService(CI.nsIXULRuntime);
-    sandbox.isDebugBuild = gDebug.isDebugBuild;
-    sandbox.xulRuntime = {widgetToolkit: xr.widgetToolkit, OS: xr.OS, __exposedProps__: { widgetToolkit: "r", OS: "r", XPCOMABI: "r", shell: "r" } };
+    var initInfo = SendContentReady();
+    gBrowserIsRemote = initInfo.remote;
 
-    // xr.XPCOMABI throws exception for configurations without full ABI
-    // support (mobile builds on ARM)
-    try {
-      sandbox.xulRuntime.XPCOMABI = xr.XPCOMABI;
-    } catch(e) {
-      sandbox.xulRuntime.XPCOMABI = "";
-    }
-  
-    try {
-      // nsIGfxInfo is currently only implemented on Windows
-      sandbox.d2d = CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo).D2DEnabled;
-    } catch(e) {
-      sandbox.d2d = false;
-    }
-
-    if (gWindowUtils && gWindowUtils.layerManagerType != "Basic")
-      sandbox.layersGPUAccelerated = true;
-    else
-      sandbox.layersGPUAccelerated = false;
- 
-    // Shortcuts for widget toolkits.
-    sandbox.cocoaWidget = xr.widgetToolkit == "cocoa";
-    sandbox.gtk2Widget = xr.widgetToolkit == "gtk2";
-    sandbox.qtWidget = xr.widgetToolkit == "qt";
-    sandbox.winWidget = xr.widgetToolkit == "windows";
+    addEventListener("load", OnDocumentLoad, true);
 
-    var hh = CC[NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX + "http"].
-                 getService(CI.nsIHttpProtocolHandler);
-    sandbox.http = { __exposedProps__: {} };
-    for each (var prop in [ "userAgent", "appName", "appVersion",
-                            "vendor", "vendorSub",
-                            "product", "productSub",
-                            "platform", "oscpu", "language", "misc" ]) {
-        sandbox.http[prop] = hh[prop];
-        sandbox.http.__exposedProps__[prop] = "r";
-    }
-    // see if we have the test plugin available,
-    // and set a sandox prop accordingly
-    sandbox.haveTestPlugin = false;
-    for (var i = 0; i < navigator.mimeTypes.length; i++) {
-        if (navigator.mimeTypes[i].type == "application/x-test" &&
-            navigator.mimeTypes[i].enabledPlugin != null &&
-            navigator.mimeTypes[i].enabledPlugin.name == "Test Plug-in") {
-            sandbox.haveTestPlugin = true;
-            break;
-        }
-    }
-
-    // Set a flag on sandbox if the windows default theme is active
-    var box = document.createElement("box");
-    box.setAttribute("id", "_box_windowsDefaultTheme");
-    document.documentElement.appendChild(box);
-    sandbox.windowsDefaultTheme = (getComputedStyle(box, null).display == "none");
-    document.documentElement.removeChild(box);
-
-    var prefs = CC["@mozilla.org/preferences-service;1"].
-                getService(CI.nsIPrefBranch2);
-    try {
-        sandbox.nativeThemePref = !prefs.getBoolPref("mozilla.widget.disable-native-theme");
-    } catch (e) {
-        sandbox.nativeThemePref = true;
-    }
-
-    sandbox.prefs = {
-        __exposedProps__: {
-            getBoolPref: 'r',
-            getIntPref: 'r',
-        },
-        _prefs:      prefs,
-        getBoolPref: function(p) { return this._prefs.getBoolPref(p); },
-        getIntPref:  function(p) { return this._prefs.getIntPref(p); }
-    }
-
-    sandbox.testPluginIsOOP = function () {
-        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-        var prefservice = Components.classes["@mozilla.org/preferences-service;1"]
-                                    .getService(CI.nsIPrefBranch);
-
-        var testPluginIsOOP = false;
-        if (navigator.platform.indexOf("Mac") == 0) {
-            var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
-                                       .getService(CI.nsIXULAppInfo)
-                                       .QueryInterface(CI.nsIXULRuntime);
-            if (xulRuntime.XPCOMABI.match(/x86-/)) {
-                try {
-                    testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.i386.test.plugin");
-                } catch (e) {
-                    testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.i386");
-                }
-            }
-            else if (xulRuntime.XPCOMABI.match(/x86_64-/)) {
-                try {
-                    testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.x86_64.test.plugin");
-                } catch (e) {
-                    testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled.x86_64");
-                }
-            }
-        }
-        else {
-            testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled");
-        }
-
-        return testPluginIsOOP;
-    };
-
-    gDumpLog("REFTEST INFO | Dumping JSON representation of sandbox \n");
-    gDumpLog("REFTEST INFO | " + JSON.stringify(sandbox) + " \n");
-
-    return sandbox;
-}
-
-function ReadTopManifest(aFileURL)
-{
-    gURLs = new Array();
-    var url = gIOService.newURI(aFileURL, null, null);
-    if (!url)
-      throw "Expected a file or http URL for the manifest.";
-    ReadManifest(url);
+    addEventListener("MozPaintWait", PaintWaitListener, true);
+    addEventListener("MozPaintWaitFinished", PaintWaitFinishedListener, true);
+ 
+    LogWarning("Using browser remote="+ gBrowserIsRemote +"\n");
 }
 
-// Note: If you materially change the reftest manifest parsing,
-// please keep the parser in print-manifest-dirs.py in sync.
-function ReadManifest(aURL)
+function StartTestURI(type, uri, timeout)
 {
-    var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
-                     .getService(CI.nsIScriptSecurityManager);
-
-    var listURL = aURL;
-    var channel = gIOService.newChannelFromURI(aURL);
-    var inputStream = channel.open();
-    if (channel instanceof Components.interfaces.nsIHttpChannel
-        && channel.responseStatus != 200) {
-      gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | HTTP ERROR : " + 
-        channel.responseStatus + "\n");
-    }
-    var streamBuf = getStreamContent(inputStream);
-    inputStream.close();
-    var lines = streamBuf.split(/(\n|\r|\r\n)/);
-
-    // Build the sandbox for fails-if(), etc., condition evaluation.
-    var sandbox = BuildConditionSandbox(aURL);
-
-    var lineNo = 0;
-    var urlprefix = "";
-    for each (var str in lines) {
-        ++lineNo;
-        if (str.charAt(0) == "#")
-            continue; // entire line was a comment
-        var i = str.search(/\s+#/);
-        if (i >= 0)
-            str = str.substring(0, i);
-        // strip leading and trailing whitespace
-        str = str.replace(/^\s*/, '').replace(/\s*$/, '');
-        if (!str || str == "")
-            continue;
-        var items = str.split(/\s+/); // split on whitespace
-
-        if (items[0] == "url-prefix") {
-            if (items.length != 2)
-                throw "url-prefix requires one url in manifest file " + aURL.spec + " line " + lineNo;
-            urlprefix = items[1];
-            continue;
-        }
-
-        var expected_status = EXPECTED_PASS;
-        var allow_silent_fail = false;
-        var minAsserts = 0;
-        var maxAsserts = 0;
-        var slow = false;
-        while (items[0].match(/^(fails|random|skip|asserts|slow|silentfail)/)) {
-            var item = items.shift();
-            var stat;
-            var cond;
-            var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
-            if (m) {
-                stat = m[1];
-                // Note: m[2] contains the parentheses, and we want them.
-                cond = Components.utils.evalInSandbox(m[2], sandbox);
-            } else if (item.match(/^(fails|random|skip)$/)) {
-                stat = item;
-                cond = true;
-            } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
-                cond = false;
-                minAsserts = Number(m[1]);
-                maxAsserts = (m[2] == undefined) ? minAsserts
-                                                 : Number(m[2].substring(1));
-            } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) {
-                cond = false;
-                if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
-                    minAsserts = Number(m[2]);
-                    maxAsserts =
-                      (m[3] == undefined) ? minAsserts
-                                          : Number(m[3].substring(1));
-                }
-            } else if (item == "slow") {
-                cond = false;
-                slow = true;
-            } else if ((m = item.match(/^slow-if\((.*?)\)$/))) {
-                cond = false;
-                if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox))
-                    slow = true;
-            } else if (item == "silentfail") {
-                cond = false;
-                allow_silent_fail = true;
-            } else {
-                throw "Error 1 in manifest file " + aURL.spec + " line " + lineNo;
-            }
-
-            if (cond) {
-                if (stat == "fails") {
-                    expected_status = EXPECTED_FAIL;
-                } else if (stat == "random") {
-                    expected_status = EXPECTED_RANDOM;
-                } else if (stat == "skip") {
-                    expected_status = EXPECTED_DEATH;
-                } else if (stat == "silentfail") {
-                    allow_silent_fail = true;
-                }
-            }
-        }
-
-        if (minAsserts > maxAsserts) {
-            throw "Bad range in manifest file " + aURL.spec + " line " + lineNo;
-        }
-
-        var runHttp = false;
-        var httpDepth;
-        if (items[0] == "HTTP") {
-            runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server
-                                               // for non-local reftests.
-            httpDepth = 0;
-            items.shift();
-        } else if (items[0].match(/HTTP\(\.\.(\/\.\.)*\)/)) {
-            // Accept HTTP(..), HTTP(../..), HTTP(../../..), etc.
-            runHttp = (aURL.scheme == "file"); // We can't yet run the local HTTP server
-                                               // for non-local reftests.
-            httpDepth = (items[0].length - 5) / 3;
-            items.shift();
-        }
-
-        // do not prefix the url for include commands or urls specifying
-        // a protocol
-        if (urlprefix && items[0] != "include") {
-            if (items.length > 1 && !items[1].match(gProtocolRE)) {
-                items[1] = urlprefix + items[1];
-            }
-            if (items.length > 2 && !items[2].match(gProtocolRE)) {
-                items[2] = urlprefix + items[2];
-            }
-        }
-
-        if (items[0] == "include") {
-            if (items.length != 2 || runHttp)
-                throw "Error 2 in manifest file " + aURL.spec + " line " + lineNo;
-            var incURI = gIOService.newURI(items[1], null, listURL);
-            secMan.checkLoadURI(aURL, incURI,
-                                CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            ReadManifest(incURI);
-        } else if (items[0] == TYPE_LOAD) {
-            if (items.length != 2 ||
-                (expected_status != EXPECTED_PASS &&
-                 expected_status != EXPECTED_DEATH))
-                throw "Error 3 in manifest file " + aURL.spec + " line " + lineNo;
-            var [testURI] = runHttp
-                            ? ServeFiles(aURL, httpDepth,
-                                         listURL, [items[1]])
-                            : [gIOService.newURI(items[1], null, listURL)];
-            var prettyPath = runHttp
-                           ? gIOService.newURI(items[1], null, listURL).spec
-                           : testURI.spec;
-            secMan.checkLoadURI(aURL, testURI,
-                                CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            gURLs.push( { type: TYPE_LOAD,
-                          expected: expected_status,
-                          allowSilentFail: allow_silent_fail,
-                          prettyPath: prettyPath,
-                          minAsserts: minAsserts,
-                          maxAsserts: maxAsserts,
-                          slow: slow,
-                          url1: testURI,
-                          url2: null } );
-        } else if (items[0] == TYPE_SCRIPT) {
-            if (items.length != 2)
-                throw "Error 4 in manifest file " + aURL.spec + " line " + lineNo;
-            var [testURI] = runHttp
-                            ? ServeFiles(aURL, httpDepth,
-                                         listURL, [items[1]])
-                            : [gIOService.newURI(items[1], null, listURL)];
-            var prettyPath = runHttp
-                           ? gIOService.newURI(items[1], null, listURL).spec
-                           : testURI.spec;
-            secMan.checkLoadURI(aURL, testURI,
-                                CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            gURLs.push( { type: TYPE_SCRIPT,
-                          expected: expected_status,
-                          allowSilentFail: allow_silent_fail,
-                          prettyPath: prettyPath,
-                          minAsserts: minAsserts,
-                          maxAsserts: maxAsserts,
-                          slow: slow,
-                          url1: testURI,
-                          url2: null } );
-        } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL) {
-            if (items.length != 3)
-                throw "Error 5 in manifest file " + aURL.spec + " line " + lineNo;
-            var [testURI, refURI] = runHttp
-                                  ? ServeFiles(aURL, httpDepth,
-                                               listURL, [items[1], items[2]])
-                                  : [gIOService.newURI(items[1], null, listURL),
-                                     gIOService.newURI(items[2], null, listURL)];
-            var prettyPath = runHttp
-                           ? gIOService.newURI(items[1], null, listURL).spec
-                           : testURI.spec;
-            secMan.checkLoadURI(aURL, testURI,
-                                CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            secMan.checkLoadURI(aURL, refURI,
-                                CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-            gURLs.push( { type: items[0],
-                          expected: expected_status,
-                          allowSilentFail: allow_silent_fail,
-                          prettyPath: prettyPath,
-                          minAsserts: minAsserts,
-                          maxAsserts: maxAsserts,
-                          slow: slow,
-                          url1: testURI,
-                          url2: refURI } );
-        } else {
-            throw "Error 6 in manifest file " + aURL.spec + " line " + lineNo;
-        }
-    }
-}
-
-function AddURIUseCount(uri)
-{
-    if (uri == null)
-        return;
-
-    var spec = uri.spec;
-    if (spec in gURIUseCounts) {
-        gURIUseCounts[spec]++;
-    } else {
-        gURIUseCounts[spec] = 1;
-    }
-}
-
-function BuildUseCounts()
-{
-    gURIUseCounts = {};
-    for (var i = 0; i < gURLs.length; ++i) {
-        var url = gURLs[i];
-        if (url.expected != EXPECTED_DEATH &&
-            (url.type == TYPE_REFTEST_EQUAL ||
-             url.type == TYPE_REFTEST_NOTEQUAL)) {
-            AddURIUseCount(gURLs[i].url1);
-            AddURIUseCount(gURLs[i].url2);
-        }
-    }
-}
-
-function ServeFiles(manifestURL, depth, aURL, files)
-{
-    var listURL = aURL.QueryInterface(CI.nsIFileURL);
-    var directory = listURL.file.parent;
-
-    // Allow serving a tree that's an ancestor of the directory containing
-    // the files so that they can use resources in ../ (etc.).
-    var dirPath = "/";
-    while (depth > 0) {
-        dirPath = "/" + directory.leafName + dirPath;
-        directory = directory.parent;
-        --depth;
-    }
-
-    gCount++;
-    var path = "/" + Date.now() + "/" + gCount;
-    gServer.registerDirectory(path + "/", directory);
-
-    var secMan = CC[NS_SCRIPTSECURITYMANAGER_CONTRACTID]
-                     .getService(CI.nsIScriptSecurityManager);
-
-    var testbase = gIOService.newURI("http://localhost:" + HTTP_SERVER_PORT +
-                                         path + dirPath,
-                                     null, null);
-
-    function FileToURI(file)
-    {
-        // Only serve relative URIs via the HTTP server, not absolute
-        // ones like about:blank.
-        var testURI = gIOService.newURI(file, null, testbase);
-
-        // XXX necessary?  manifestURL guaranteed to be file, others always HTTP
-        secMan.checkLoadURI(manifestURL, testURI,
-                            CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
-
-        return testURI;
-    }
-
-    return files.map(FileToURI);
-}
-
-function StartCurrentTest()
-{
-    gTestLog = [];
-
-    // make sure we don't run tests that are expected to kill the browser
-    while (gURLs.length > 0) {
-        var test = gURLs[0];
-        if (test.expected == EXPECTED_DEATH) {
-            ++gTestResults.Skip;
-            gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIP)\n");
-            gURLs.shift();
-        } else if (test.slow && !gRunSlowTests) {
-            ++gTestResults.Slow;
-            gDumpLog("REFTEST TEST-KNOWN-SLOW | " + test.url1.spec + " | (SLOW)\n");
-            gURLs.shift();
-        } else {
-            break;
-        }
-    }
-
-    if (gURLs.length == 0) {
-        DoneTests();
-    }
-    else {
-        var currentTest = gTotalTests - gURLs.length;
-        document.title = "reftest: " + currentTest + " / " + gTotalTests +
-            " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)";
-        StartCurrentURI(1);
-    }
-}
-
-function StartCurrentURI(aState)
-{
-    gCurrentTestStartTime = Date.now();
-    if (gFailureTimeout != null) {
-        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " +
-             "| program error managing timeouts\n");
-        ++gTestResults.Exception;
-    }
-    gFailureTimeout = setTimeout(LoadFailed, gLoadTimeout);
-    gFailureReason = "timed out waiting for onload to fire";
-
-    gState = aState;
-    gCurrentURL = gURLs[0]["url" + aState].spec;
     // Reset gExplicitPendingPaintCount in case there was a timeout or
     // the count is out of sync for some other reason
     if (gExplicitPendingPaintCount != 0) {
         LogWarning("Resetting gExplicitPendingPaintCount to zero (currently " +
                    gExplicitPendingPaintCount + "\n");
         gExplicitPendingPaintCount = 0;
     }
 
-    if (gURICanvases[gCurrentURL] &&
-        (gURLs[0].type == TYPE_REFTEST_EQUAL ||
-         gURLs[0].type == TYPE_REFTEST_NOTEQUAL) &&
-        gURLs[0].maxAsserts == 0) {
-        // Pretend the document loaded --- RecordResult will notice
-        // there's already a canvas for this URL
-        setTimeout(RecordResult, 0);
-    } else {
-        gDumpLog("REFTEST TEST-START | " + gCurrentURL + "\n");
-        LogInfo("START " + gCurrentURL);
-        gBrowser.loadURI(gCurrentURL);
-    }
-}
-
-function DoneTests()
-{
-    gDumpLog("REFTEST FINISHED: Slowest test took " + gSlowestTestTime +
-         "ms (" + gSlowestTestURL + ")\n");
+    gCurrentTestType = type;
+    gCurrentURL = uri;
 
-    gDumpLog("REFTEST INFO | Result summary:\n");
-    var count = gTestResults.Pass + gTestResults.LoadOnly;
-    gDumpLog("REFTEST INFO | Successful: " + count + " (" +
-             gTestResults.Pass + " pass, " +
-             gTestResults.LoadOnly + " load only)\n");
-    count = gTestResults.Exception + gTestResults.FailedLoad +
-            gTestResults.UnexpectedFail + gTestResults.UnexpectedPass +
-            gTestResults.AssertionUnexpected +
-            gTestResults.AssertionUnexpectedFixed;
-    gDumpLog("REFTEST INFO | Unexpected: " + count + " (" +
-             gTestResults.UnexpectedFail + " unexpected fail, " +
-             gTestResults.UnexpectedPass + " unexpected pass, " +
-             gTestResults.AssertionUnexpected + " unexpected asserts, " +
-             gTestResults.AssertionUnexpectedFixed + " unexpected fixed asserts, " +
-             gTestResults.FailedLoad + " failed load, " +
-             gTestResults.Exception + " exception)\n");
-    count = gTestResults.KnownFail + gTestResults.AssertionKnown +
-            gTestResults.Random + gTestResults.Skip + gTestResults.Slow;
-    gDumpLog("REFTEST INFO | Known problems: " + count + " (" +
-             gTestResults.KnownFail + " known fail, " +
-             gTestResults.AssertionKnown + " known asserts, " +
-             gTestResults.Random + " random, " +
-             gTestResults.Skip + " skipped, " +
-             gTestResults.Slow + " slow)\n");
+    gCurrentTestStartTime = Date.now();
+    if (gFailureTimeout != null) {
+        SendException("program error managing timeouts\n");
+    }
+    gFailureTimeout = setTimeout(LoadFailed, timeout);
 
-    gDumpLog("REFTEST INFO | Total canvas count = " + gRecycledCanvases.length + "\n");
-
-    gDumpLog("REFTEST TEST-START | Shutdown\n");
-    function onStopped() {
-        goQuitApplication();
-    }
-    if (gServer)
-        gServer.stop(onStopped);
-    else
-        onStopped();
+    LoadURI(gCurrentURL);
 }
 
 function setupZoom(contentRootElement) {
     if (!contentRootElement || !contentRootElement.hasAttribute('reftest-zoom'))
         return;
-    gBrowser.markupDocumentViewer.fullZoom =
+    markupDocumentViewer().fullZoom =
         contentRootElement.getAttribute('reftest-zoom');
 }
 
 function resetZoom() {
-    gBrowser.markupDocumentViewer.fullZoom = 1.0;
+    markupDocumentViewer().fullZoom = 1.0;
 }
 
 function doPrintMode(contentRootElement) {
     // use getAttribute because className works differently in HTML and SVG
     return contentRootElement &&
            contentRootElement.hasAttribute('class') &&
            contentRootElement.getAttribute('class').split(/\s+/)
                              .indexOf("reftest-print") != -1;
 }
 
 function setupPrintMode() {
-   var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
-               .getService(Components.interfaces.nsIPrintSettingsService);
+   var PSSVC =
+       CC[PRINTSETTINGS_CONTRACTID].getService(CI.nsIPrintSettingsService);
    var ps = PSSVC.newPrintSettings;
    ps.paperWidth = 5;
    ps.paperHeight = 3;
 
    // Override any os-specific unwriteable margins
    ps.unwriteableMarginTop = 0;
    ps.unwriteableMarginLeft = 0;
    ps.unwriteableMarginBottom = 0;
    ps.unwriteableMarginRight = 0;
 
    ps.headerStrLeft = "";
    ps.headerStrCenter = "";
    ps.headerStrRight = "";
    ps.footerStrLeft = "";
    ps.footerStrCenter = "";
    ps.footerStrRight = "";
-   gBrowser.docShell.contentViewer.setPageMode(true, ps);
+   docShell.contentViewer.setPageMode(true, ps);
 }
 
 function shouldWaitForExplicitPaintWaiters() {
     return gExplicitPendingPaintCount > 0;
 }
 
 function shouldWaitForPendingPaints() {
-    // if gCurrentCanvas is null, we're not taking snapshots so there is
-    // no need to wait for pending paints to be flushed.
-    return gCurrentCanvas && gWindowUtils.isMozAfterPaintPending;
+    // if gHaveCanvasSnapshot is false, we're not taking snapshots so
+    // there is no need to wait for pending paints to be flushed.
+    return gHaveCanvasSnapshot && windowUtils().isMozAfterPaintPending;
 }
 
 function shouldWaitForReftestWaitRemoval(contentRootElement) {
     // use getAttribute because className works differently in HTML and SVG
     return contentRootElement &&
            contentRootElement.hasAttribute('class') &&
            contentRootElement.getAttribute('class').split(/\s+/)
                              .indexOf("reftest-wait") != -1;
@@ -963,16 +258,17 @@ const STATE_WAITING_TO_FIRE_INVALIDATE_E
 const STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL = 1;
 // When all MozAfterPaint events and all explicit paint waits are flushed, we're
 // done and can move to the COMPLETED state.
 const STATE_WAITING_TO_FINISH = 2;
 const STATE_COMPLETED = 3;
 
 function WaitForTestEnd(contentRootElement, inPrintMode) {
     var stopAfterPaintReceived = false;
+    var currentDoc = content.document;
     var state = STATE_WAITING_TO_FIRE_INVALIDATE_EVENT;
 
     function FlushRendering() {
         var anyPendingPaintsGeneratedInDescendants = false;
 
         function flushWindow(win) {
             var utils = win.QueryInterface(CI.nsIInterfaceRequestor)
                         .getInterface(CI.nsIDOMWindowUtils);
@@ -990,32 +286,33 @@ function WaitForTestEnd(contentRootEleme
                 anyPendingPaintsGeneratedInDescendants = true;
             }
 
             for (var i = 0; i < win.frames.length; ++i) {
                 flushWindow(win.frames[i]);
             }
         }
 
-        flushWindow(gBrowser.contentWindow);
+        flushWindow(content);
 
         if (anyPendingPaintsGeneratedInDescendants &&
-            !gWindowUtils.isMozAfterPaintPending) {
+            !windowUtils().isMozAfterPaintPending) {
             LogWarning("Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!");
         }
     }
 
     function AfterPaintListener(event) {
         LogInfo("AfterPaintListener in " + event.target.document.location.href);
-        if (event.target.document != document) {
+        if (event.target.document != currentDoc) {
             // ignore paint events for subframes or old documents in the window.
             // Invalidation in subframes will cause invalidation in the toplevel document anyway.
             return;
         }
-        UpdateCurrentCanvasForEvent(event);
+
+        SendUpdateCanvasForEvent(event);
         // These events are fired immediately after a paint. Don't
         // confuse ourselves by firing synchronously if we triggered the
         // paint ourselves.
         setTimeout(MakeProgress, 0);
     }
 
     function AttrModifiedListener() {
         LogInfo("AttrModifiedListener fired");
@@ -1030,19 +327,19 @@ function WaitForTestEnd(contentRootEleme
         LogInfo("ExplicitPaintsCompleteListener fired");
         // Since this can fire while painting, don't confuse ourselves by
         // firing synchronously. It's fine to do this asynchronously.
         setTimeout(MakeProgress, 0);
     }
 
     function RemoveListeners() {
         // OK, we can end the test now.
-        window.removeEventListener("MozAfterPaint", AfterPaintListener, false);
+        removeEventListener("MozAfterPaint", AfterPaintListener, false);
         if (contentRootElement) {
-          contentRootElement.removeEventListener("DOMAttrModified", AttrModifiedListener, false);
+            contentRootElement.removeEventListener("DOMAttrModified", AttrModifiedListener, false);
         }
         gExplicitPendingPaintsCompleteHook = null;
         gTimeoutHook = null;
         // Make sure we're in the COMPLETED state just in case
         // (this may be called via the test-timeout hook)
         state = STATE_COMPLETED;
     }
 
@@ -1071,18 +368,19 @@ function WaitForTestEnd(contentRootEleme
                     LogInfo("MakeProgress: waiting for MozAfterPaint");
                 }
                 return;
             }
 
             state = STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL;
             var hasReftestWait = shouldWaitForReftestWaitRemoval(contentRootElement);            
             // Notify the test document that now is a good time to test some invalidation
+            LogInfo("MakeProgress: dispatching MozReftestInvalidate");
             if (contentRootElement) {
-                var notification = document.createEvent("Events");
+                var notification = content.document.createEvent("Events");
                 notification.initEvent("MozReftestInvalidate", true, false);
                 contentRootElement.dispatchEvent(notification);
             }
             if (hasReftestWait && !shouldWaitForReftestWaitRemoval(contentRootElement)) {
                 // MozReftestInvalidate handler removed reftest-wait.
                 // We expect something to have been invalidated...
                 FlushRendering();
                 if (!shouldWaitForPendingPaints() && !shouldWaitForExplicitPaintWaiters()) {
@@ -1130,35 +428,35 @@ function WaitForTestEnd(contentRootEleme
             gFailureReason = "timed out while taking snapshot (bug in harness?)";
             RemoveListeners();
             setTimeout(RecordResult, 0);
             return;
         }
     }
 
     LogInfo("WaitForTestEnd: Adding listeners");
-    window.addEventListener("MozAfterPaint", AfterPaintListener, false);
+    addEventListener("MozAfterPaint", AfterPaintListener, false);
     // If contentRootElement is null then shouldWaitForReftestWaitRemoval will
     // always return false so we don't need a listener anyway
     if (contentRootElement) {
       contentRootElement.addEventListener("DOMAttrModified", AttrModifiedListener, false);
     }
     gExplicitPendingPaintsCompleteHook = ExplicitPaintsCompleteListener;
     gTimeoutHook = RemoveListeners;
 
     // Take a full snapshot now that all our listeners are set up. This
     // ensures it's impossible for us to miss updates between taking the snapshot
     // and adding our listeners.
-    InitCurrentCanvasWithSnapshot();
+    SendInitCanvasWithSnapshot();
     MakeProgress();
 }
 
 function OnDocumentLoad(event)
 {
-    var currentDoc = gBrowser.contentDocument;
+    var currentDoc = content.document;
     if (event.target != currentDoc)
         // Ignore load events for subframes.
         return;
 
     if (gClearingForAssertionCheck &&
         currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
         DoAssertionCheck();
         return;
@@ -1174,17 +472,17 @@ function OnDocumentLoad(event)
     setupZoom(contentRootElement);
     var inPrintMode = false;
 
     function AfterOnLoadScripts() {
         // Take a snapshot now. We need to do this before we check whether
         // we should wait, since this might trigger dispatching of
         // MozPaintWait events and make shouldWaitForExplicitPaintWaiters() true
         // below.
-        var painted = InitCurrentCanvasWithSnapshot();
+        var painted = SendInitCanvasWithSnapshot();
 
         if (shouldWaitForExplicitPaintWaiters() ||
             (!inPrintMode && doPrintMode(contentRootElement)) ||
             // If we didn't force a paint above, in
             // InitCurrentCanvasWithSnapshot, so we should wait for a
             // paint before we consider them done.
             !painted) {
             LogInfo("AfterOnLoadScripts belatedly entering WaitForTestEnd");
@@ -1196,110 +494,32 @@ function OnDocumentLoad(event)
     }
 
     if (shouldWaitForReftestWaitRemoval(contentRootElement) ||
         shouldWaitForExplicitPaintWaiters()) {
         // Go into reftest-wait mode immediately after painting has been
         // unsuppressed, after the onload event has finished dispatching.
         gFailureReason = "timed out waiting for test to complete (trying to get into WaitForTestEnd)";
         LogInfo("OnDocumentLoad triggering WaitForTestEnd");
-        setTimeout(WaitForTestEnd, 0, contentRootElement, inPrintMode);
+        setTimeout(function () { WaitForTestEnd(contentRootElement, inPrintMode); }, 0);
     } else {
         if (doPrintMode(contentRootElement)) {
             LogInfo("OnDocumentLoad setting up print mode");
             setupPrintMode();
             inPrintMode = true;
         }
 
         // Since we can't use a bubbling-phase load listener from chrome,
         // this is a capturing phase listener.  So do setTimeout twice, the
         // first to get us after the onload has fired in the content, and
         // the second to get us after any setTimeout(foo, 0) in the content.
         gFailureReason = "timed out waiting for test to complete (waiting for onload scripts to complete)";
         LogInfo("OnDocumentLoad triggering AfterOnLoadScripts");
-        setTimeout(setTimeout, 0, AfterOnLoadScripts, 0);
-    }
-}
-
-function UpdateCanvasCache(url, canvas)
-{
-    var spec = url.spec;
-
-    --gURIUseCounts[spec];
-
-    if (gNoCanvasCache || gURIUseCounts[spec] == 0) {
-        ReleaseCanvas(canvas);
-        delete gURICanvases[spec];
-    } else if (gURIUseCounts[spec] > 0) {
-        gURICanvases[spec] = canvas;
-    } else {
-        throw "Use counts were computed incorrectly";
-    }
-}
-
-// Recompute drawWindow flags for every drawWindow operation.
-// We have to do this every time since our window can be
-// asynchronously resized (e.g. by the window manager, to make
-// it fit on screen) at unpredictable times.
-// Fortunately this is pretty cheap.
-function DoDrawWindow(ctx, x, y, w, h)
-{
-    var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW;
-    var testRect = gBrowser.getBoundingClientRect();
-    if (0 <= testRect.left &&
-        0 <= testRect.top &&
-        window.innerWidth >= testRect.right &&
-        window.innerHeight >= testRect.bottom) {
-        // We can use the window's retained layer manager
-        // because the window is big enough to display the entire
-        // browser element
-        flags |= ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
+        setTimeout(function () { setTimeout(AfterOnLoadScripts, 0); }, 0);
     }
-
-    if (gDrawWindowFlags != flags) {
-        // Every time the flags change, dump the new state.
-        gDrawWindowFlags = flags;
-        var flagsStr = "DRAWWINDOW_DRAW_CARET | DRAWWINDOW_DRAW_VIEW";
-        if (flags & ctx.DRAWWINDOW_USE_WIDGET_LAYERS) {
-            flagsStr += " | DRAWWINDOW_USE_WIDGET_LAYERS";
-        } else {
-            // Output a special warning because we need to be able to detect
-            // this whenever it happens.
-            gDumpLog("REFTEST INFO | WARNING: USE_WIDGET_LAYERS disabled\n");
-        }
-        gDumpLog("REFTEST INFO | drawWindow flags = " + flagsStr +
-                 "; window size = " + window.innerWidth + "," + window.innerHeight +
-                 "; test browser size = " + testRect.width + "," + testRect.height +
-                 "\n");
-    }
-
-    LogInfo("DoDrawWindow " + x + "," + y + "," + w + "," + h);
-    ctx.drawWindow(window, x, y, w, h, "rgb(255,255,255)",
-                   gDrawWindowFlags);
-}
-
-function InitCurrentCanvasWithSnapshot()
-{
-    if (gURLs[0].type == TYPE_LOAD || gURLs[0].type == TYPE_SCRIPT) {
-        // We don't want to snapshot this kind of test
-        return false;
-    }
-
-    if (!gCurrentCanvas) {
-        gCurrentCanvas = AllocateCanvas();
-    }
-
-    var ctx = gCurrentCanvas.getContext("2d");
-    DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height);
-    return true;
-}
-
-function roundTo(x, fraction)
-{
-    return Math.round(x/fraction)*fraction;
 }
 
 function UpdateCurrentCanvasForEvent(event)
 {
     if (!gCurrentCanvas)
         return;
 
     var ctx = gCurrentCanvas.getContext("2d");
@@ -1318,265 +538,246 @@ function UpdateCurrentCanvasForEvent(eve
         ctx.restore();
     }
 }
 
 function RecordResult()
 {
     LogInfo("RecordResult fired");
 
-    // Keep track of which test was slowest, and how long it took.
     var currentTestRunTime = Date.now() - gCurrentTestStartTime;
-    if (currentTestRunTime > gSlowestTestTime) {
-        gSlowestTestTime = currentTestRunTime;
-        gSlowestTestURL  = gCurrentURL;
-    }
 
     clearTimeout(gFailureTimeout);
     gFailureReason = null;
     gFailureTimeout = null;
 
-    // Not 'const ...' because of 'EXPECTED_*' value dependency.
-    var outputs = {};
-    const randomMsg = "(EXPECTED RANDOM)";
-    outputs[EXPECTED_PASS] = {
-        true:  {s: "TEST-PASS"                  , n: "Pass"},
-        false: {s: "TEST-UNEXPECTED-FAIL"       , n: "UnexpectedFail"}
-    };
-    outputs[EXPECTED_FAIL] = {
-        true:  {s: "TEST-UNEXPECTED-PASS"       , n: "UnexpectedPass"},
-        false: {s: "TEST-KNOWN-FAIL"            , n: "KnownFail"}
-    };
-    outputs[EXPECTED_RANDOM] = {
-        true:  {s: "TEST-PASS" + randomMsg      , n: "Random"},
-        false: {s: "TEST-KNOWN-FAIL" + randomMsg, n: "Random"}
-    };
-    var output;
-
-    if (gURLs[0].type == TYPE_LOAD) {
-        ++gTestResults.LoadOnly;
-        gDumpLog("REFTEST TEST-PASS | " + gURLs[0].prettyPath + " | (LOAD ONLY)\n");
-        gCurrentCanvas = null;
-        FinishTestItem();
-        return;
-    }
-    if (gURLs[0].type == TYPE_SCRIPT) {
-        var missing_msg = false;
-        var testwindow = gBrowser.contentWindow;
-        expected = gURLs[0].expected;
+    if (gCurrentTestType == TYPE_SCRIPT) {
+        var error = '';
+        var testwindow = content;
 
         if (testwindow.wrappedJSObject)
             testwindow = testwindow.wrappedJSObject;
 
         var testcases;
-
         if (!testwindow.getTestCases || typeof testwindow.getTestCases != "function") {
             // Force an unexpected failure to alert the test author to fix the test.
-            expected = EXPECTED_PASS;
-            missing_msg = "test must provide a function getTestCases(). (SCRIPT)\n";
+            error = "test must provide a function getTestCases(). (SCRIPT)\n";
         }
         else if (!(testcases = testwindow.getTestCases())) {
             // Force an unexpected failure to alert the test author to fix the test.
-            expected = EXPECTED_PASS;
-            missing_msg = "test's getTestCases() must return an Array-like Object. (SCRIPT)\n";
+            error = "test's getTestCases() must return an Array-like Object. (SCRIPT)\n";
         }
         else if (testcases.length == 0) {
             // This failure may be due to a JavaScript Engine bug causing
             // early termination of the test. If we do not allow silent
-            // failure, report an error.
-            if (!gURLs[0].allowSilentFail)
-                missing_msg = "No test results reported. (SCRIPT)\n";
-            else
-                gDumpLog("REFTEST INFO | An expected silent failure occurred \n");
-        }
-
-        if (missing_msg) {
-            output = outputs[expected][false];
-            ++gTestResults[output.n];
-            var result = "REFTEST " + output.s + " | " +
-                gURLs[0].prettyPath + " | " + // the URL being tested
-                missing_msg;
-
-            gDumpLog(result);
-            FinishTestItem();
-            return;
+            // failure, the driver will report an error.
         }
 
-        var results = testcases.map(function(test) {
-                return { passed: test.testPassed(), description: test.testDescription()};
-            });
-        var anyFailed = results.some(function(result) { return !result.passed; });
-        var outputPair;
-        if (anyFailed && expected == EXPECTED_FAIL) {
-            // If we're marked as expected to fail, and some (but not all) tests
-            // passed, treat those tests as though they were marked random
-            // (since we can't tell whether they were really intended to be
-            // marked failing or not).
-            outputPair = { true: outputs[EXPECTED_RANDOM][true],
-                           false: outputs[expected][false] };
-        } else {
-            outputPair = outputs[expected];
-        }
-        var index = 0;
-        results.forEach(function(result) {
-                var output = outputPair[result.passed];
-
-                ++gTestResults[output.n];
-                result = "REFTEST " + output.s + " | " +
-                    gURLs[0].prettyPath + " | " + // the URL being tested
-                    result.description + " item " + (++index) + "\n";
-                gDumpLog(result);
-            });
-
-        if (anyFailed && expected == EXPECTED_PASS) {
-            FlushTestLog();
+        var results = [ ];
+        if (!error) {
+            // FIXME/bug 618176: temporary workaround
+            for (var i = 0; i < testcases.length; ++i) {
+                var test = testcases[i];
+                results.push({ passed: test.testPassed(),
+                               description: test.testDescription() });
+            }
+            //results = testcases.map(function(test) {
+            //        return { passed: test.testPassed(),
+            //                 description: test.testDescription() };
         }
 
+        SendScriptResults(currentTestRunTime, error, results);
         FinishTestItem();
         return;
     }
 
-    if (gURICanvases[gCurrentURL]) {
-        gCurrentCanvas = gURICanvases[gCurrentURL];
-    }
-    if (gState == 1) {
-        gCanvas1 = gCurrentCanvas;
-    } else {
-        gCanvas2 = gCurrentCanvas;
-    }
-    gCurrentCanvas = null;
-
-    resetZoom();
-
-    switch (gState) {
-        case 1:
-            // First document has been loaded.
-            // Proceed to load the second document.
-
-            StartCurrentURI(2);
-            break;
-        case 2:
-            // Both documents have been loaded. Compare the renderings and see
-            // if the comparison result matches the expected result specified
-            // in the manifest.
-
-            // number of different pixels
-            var differences;
-            // whether the two renderings match:
-            var equal;
-
-            if (gWindowUtils) {
-                differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, {});
-                equal = (differences == 0);
-            } else {
-                differences = -1;
-                var k1 = gCanvas1.toDataURL();
-                var k2 = gCanvas2.toDataURL();
-                equal = (k1 == k2);
-            }
-
-            // whether the comparison result matches what is in the manifest
-            var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL));
-            // what is expected on this platform (PASS, FAIL, or RANDOM)
-            var expected = gURLs[0].expected;
-            output = outputs[expected][test_passed];
-
-            ++gTestResults[output.n];
-
-            var result = "REFTEST " + output.s + " | " +
-                         gURLs[0].prettyPath + " | "; // the URL being tested
-            switch (gURLs[0].type) {
-                case TYPE_REFTEST_NOTEQUAL:
-                    result += "image comparison (!=) ";
-                    break;
-                case TYPE_REFTEST_EQUAL:
-                    result += "image comparison (==) ";
-                    break;
-            }
-            gDumpLog(result + "\n");
-
-            if (!test_passed && expected == EXPECTED_PASS ||
-                test_passed && expected == EXPECTED_FAIL) {
-                if (!equal) {
-                    gDumpLog("REFTEST   IMAGE 1 (TEST): " + gCanvas1.toDataURL() + "\n");
-                    gDumpLog("REFTEST   IMAGE 2 (REFERENCE): " + gCanvas2.toDataURL() + "\n");
-                    gDumpLog("REFTEST number of differing pixels: " + differences + "\n");
-                } else {
-                    gDumpLog("REFTEST   IMAGE: " + gCanvas1.toDataURL() + "\n");
-                }
-            }
-
-            if (!test_passed && expected == EXPECTED_PASS) {
-                FlushTestLog();
-            }
-
-            UpdateCanvasCache(gURLs[0].url1, gCanvas1);
-            UpdateCanvasCache(gURLs[0].url2, gCanvas2);
-
-            FinishTestItem();
-            break;
-        default:
-            throw "Unexpected state.";
-    }
+    SendTestDone(currentTestRunTime);
+    FinishTestItem();
 }
 
 function LoadFailed()
 {
     if (gTimeoutHook) {
         gTimeoutHook();
     }
     gFailureTimeout = null;
-    ++gTestResults.FailedLoad;
-    gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " +
-         gURLs[0]["url" + gState].spec + " | " + gFailureReason + "\n");
-    FlushTestLog();
-    FinishTestItem();
+    SendFailedLoad(gFailureReason);
 }
 
 function FinishTestItem()
 {
-    // Replace document with BLANK_URL_FOR_CLEARING in case there are
-    // assertions when unloading.
-    gDumpLog("REFTEST INFO | Loading a blank page\n");
-    gClearingForAssertionCheck = true;
-    gBrowser.loadURI(BLANK_URL_FOR_CLEARING);
+    gHaveCanvasSnapshot = false;
 }
 
 function DoAssertionCheck()
 {
     gClearingForAssertionCheck = false;
 
+    var numAsserts = 0;
     if (gDebug.isDebugBuild) {
         var newAssertionCount = gDebug.assertionCount;
-        var numAsserts = newAssertionCount - gAssertionCount;
+        numAsserts = newAssertionCount - gAssertionCount;
         gAssertionCount = newAssertionCount;
-
-        var minAsserts = gURLs[0].minAsserts;
-        var maxAsserts = gURLs[0].maxAsserts;
+    }
+    SendAssertionCount(numAsserts);
+}
 
-        var expectedAssertions = "expected " + minAsserts;
-        if (minAsserts != maxAsserts) {
-            expectedAssertions += " to " + maxAsserts;
-        }
-        expectedAssertions += " assertions";
+function LoadURI(uri)
+{
+    var flags = webNavigation().LOAD_FLAGS_NONE;
+    webNavigation().loadURI(uri, flags, null, null, null);
+}
 
-        if (numAsserts < minAsserts) {
-            ++gTestResults.AssertionUnexpectedFixed;
-            gDumpLog("REFTEST TEST-UNEXPECTED-PASS | " + gURLs[0].prettyPath +
-                 " | assertion count " + numAsserts + " is less than " +
-                 expectedAssertions + "\n");
-        } else if (numAsserts > maxAsserts) {
-            ++gTestResults.AssertionUnexpected;
-            gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gURLs[0].prettyPath +
-                 " | assertion count " + numAsserts + " is more than " +
-                 expectedAssertions + "\n");
-        } else if (numAsserts != 0) {
-            ++gTestResults.AssertionKnown;
-            gDumpLog("REFTEST TEST-KNOWN-FAIL | " + gURLs[0].prettyPath +
-                 " | assertion count " + numAsserts + " matches " +
-                 expectedAssertions + "\n");
-        }
+function LogWarning(str)
+{
+    sendAsyncMessage("reftest:Log", { type: "warning", msg: str });
+}
+
+function LogInfo(str)
+{
+    sendAsyncMessage("reftest:Log", { type: "info", msg: str });
+}
+
+var gDummyCanvas = null;
+function SynchronizeForSnapshot()
+{
+    if (gDummyCanvas == null) {
+        gDummyCanvas = content.document.createElementNS(XHTML_NS, "canvas");
+        gDummyCanvas.setAttribute("width", 1);
+        gDummyCanvas.setAttribute("height", 1);
     }
 
-    // And start the next test.
-    gURLs.shift();
-    StartCurrentTest();
+    var ctx = gDummyCanvas.getContext("2d");
+    var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
+    ctx.drawWindow(content, 0, 0, 1, 1, "rgb(255,255,255)", flags);
+}
+
+function RegisterMessageListeners()
+{
+    addMessageListener(
+        "reftest:Clear",
+        function (m) { RecvClear() }
+    );
+    addMessageListener(
+        "reftest:LoadScriptTest",
+        function (m) { RecvLoadScriptTest(m.json.uri, m.json.timeout); }
+    );
+    addMessageListener(
+        "reftest:LoadTest",
+        function (m) { RecvLoadTest(m.json.type, m.json.uri, m.json.timeout); }
+    );
+    addMessageListener(
+        "reftest:ResetZoom",
+        function (m) { RecvResetZoom(); }
+    );
+}
+
+function RecvClear()
+{
+    gClearingForAssertionCheck = true;
+    LoadURI(BLANK_URL_FOR_CLEARING);
+}
+
+function RecvLoadTest(type, uri, timeout)
+{
+    StartTestURI(type, uri, timeout);
+}
+
+function RecvLoadScriptTest(uri, timeout)
+{
+    StartTestURI(TYPE_SCRIPT, uri, timeout);
+}
+
+function RecvResetZoom()
+{
+    resetZoom();
+}
+
+function SendAssertionCount(numAssertions)
+{
+    sendAsyncMessage("reftest:AssertionCount", { count: numAssertions });
+}
+
+function SendContentReady()
+{
+    return sendSyncMessage("reftest:ContentReady")[0];
+}
+
+function SendException(what)
+{
+    sendAsyncMessage("reftest:Exception", { what: what });
+}
+
+function SendFailedLoad(why)
+{
+    sendAsyncMessage("reftest:FailedLoad", { why: why });
 }
+
+// Return true if a snapshot was taken.
+function SendInitCanvasWithSnapshot()
+{
+    // If we're in the same process as the top-level XUL window, then
+    // drawing that window will also update our layers, so no
+    // synchronization is needed.
+    //
+    // NB: this is a test-harness optimization only, it must not
+    // affect the validity of the tests.
+    if (gBrowserIsRemote) {
+        SynchronizeForSnapshot();
+    }
+
+    // For in-process browser, we have to make a synchronous request
+    // here to make the above optimization valid, so that MozWaitPaint
+    // events dispatched (synchronously) during painting are received
+    // before we check the paint-wait counter.  For out-of-process
+    // browser though, it doesn't wrt correctness whether this request
+    // is sync or async.
+    var ret = sendSyncMessage("reftest:InitCanvasWithSnapshot")[0];
+ 
+    gHaveCanvasSnapshot = ret.painted;
+    return ret.painted;
+}
+
+function SendScriptResults(runtimeMs, error, results)
+ {
+    sendAsyncMessage("reftest:ScriptResults",
+                     { runtimeMs: runtimeMs, error: error, results: results });
+}
+
+function SendTestDone(runtimeMs)
+{
+    sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs });
+}
+
+function roundTo(x, fraction)
+{
+    return Math.round(x/fraction)*fraction;
+}
+
+function SendUpdateCanvasForEvent(event)
+{
+    var win = content;
+    var scale = markupDocumentViewer().fullZoom;
+ 
+    var rects = [ ];
+    var rectList = event.clientRects;
+    for (var i = 0; i < rectList.length; ++i) {
+        var r = rectList[i];
+        // Set left/top/right/bottom to "device pixel" boundaries
+        var left = Math.floor(roundTo(r.left*scale, 0.001));
+        var top = Math.floor(roundTo(r.top*scale, 0.001));
+        var right = Math.ceil(roundTo(r.right*scale, 0.001));
+        var bottom = Math.ceil(roundTo(r.bottom*scale, 0.001));
+
+        rects.push({ left: left, top: top, right: right, bottom: bottom });
+    }
+
+    // See comments in SendInitCanvasWithSnapshot() re: the split
+    // logic here.
+    if (!gBrowserIsRemote) {
+        sendSyncMessage("reftest:UpdateCanvasForInvalidation", { rects: rects });
+    } else {
+        SynchronizeForSnapshot();
+        sendAsyncMessage("reftest:UpdateCanvasForInvalidation", { rects: rects });
+    }
+}
+
+addEventListener("load", OnInitialLoad, true);
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 const CC = Components.classes;
 const CI = Components.interfaces;
 const CR = Components.results;
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const NS_LOCAL_FILE_CONTRACTID = "@mozilla.org/file/local;1";
 const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1";
 const IO_SERVICE_CONTRACTID = "@mozilla.org/network/io-service;1";
 const DEBUG_CONTRACTID = "@mozilla.org/xpcom/debug;1";
 const NS_LOCALFILEINPUTSTREAM_CONTRACTID =
           "@mozilla.org/network/file-input-stream;1";
 const NS_SCRIPTSECURITYMANAGER_CONTRACTID =
@@ -62,16 +63,19 @@ var gTimeoutHook = null;
 var gRemote = false;
 var gTotalChunks = 0;
 var gThisChunk = 0;
 
 // "<!--CLEAR-->"
 const BLANK_URL_FOR_CLEARING = "data:text/html,%3C%21%2D%2DCLEAR%2D%2D%3E";
 
 var gBrowser;
+// Are we testing web content loaded in a separate process?
+var gBrowserIsRemote;           // bool
+var gBrowserMessageManager;
 var gCanvas1, gCanvas2;
 // gCurrentCanvas is non-null between InitCurrentCanvasWithSnapshot and the next
 // RecordResult.
 var gCurrentCanvas = null;
 var gURLs;
 // Map from URI spec to the number of times it remains to be used
 var gURIUseCounts;
 // Map from URI spec to the canvas rendered for that URI
@@ -91,41 +95,28 @@ var gTestResults = {
   KnownFail : 0,
   AssertionKnown: 0,
   Random : 0,
   Skip: 0,
   Slow: 0,
 };
 var gTotalTests = 0;
 var gState;
-// Plugin layers can be updated asynchronously, so to make sure that all
-// layer surfaces have the right content, we need to listen for explicit
-// "MozPaintWait" and "MozPaintWaitFinished" events that signal when it's OK
-// to take snapshots. We cannot take a snapshot while the number of
-// "MozPaintWait" events fired exceeds the number of "MozPaintWaitFinished"
-// events fired. We count the number of such excess events here. When
-// the counter reaches zero we call gExplicitPendingPaintsCompleteHook.
-var gExplicitPendingPaintCount = 0;
-var gExplicitPendingPaintsCompleteHook;
 var gCurrentURL;
-var gFailureTimeout = null;
-var gFailureReason;
 var gTestLog = [];
 var gServer;
 var gCount = 0;
 var gAssertionCount = 0;
 
 var gIOService;
 var gDebug;
 var gWindowUtils;
 
-var gCurrentTestStartTime;
 var gSlowestTestTime = 0;
 var gSlowestTestURL;
-var gClearingForAssertionCheck = false;
 
 var gDrawWindowFlags;
 
 const TYPE_REFTEST_EQUAL = '==';
 const TYPE_REFTEST_NOTEQUAL = '!=';
 const TYPE_LOAD = 'load';     // test without a reference (just test that it does
                               // not assert, crash, hang, or leak)
 const TYPE_SCRIPT = 'script'; // test contains individual test results
@@ -146,25 +137,29 @@ var gRunSlowTests = true;
 // whether we should skip caching canvases
 var gNoCanvasCache = false;
 
 var gRecycledCanvases = new Array();
 
 // By default we just log to stdout
 var gDumpLog = dump;
 
+// Only dump the sandbox once, because it doesn't depend on the
+// manifest URL (yet!).
+var gDumpedConditionSandbox = false;
+
 function LogWarning(str)
 {
     gDumpLog("REFTEST INFO | " + str + "\n");
     gTestLog.push(str);
 }
 
 function LogInfo(str)
 {
-    // gDumpLog("REFTEST INFO | " + str + "\n");
+//    gDumpLog("REFTEST INFO | " + str + "\n");
     gTestLog.push(str);
 }
 
 function FlushTestLog()
 {
     for (var i = 0; i < gTestLog.length; ++i) {
         gDumpLog("REFTEST INFO | Saved log: " + gTestLog[i] + "\n");
     }
@@ -197,40 +192,45 @@ function IDForEventTarget(event)
 {
     try {
         return "'" + event.target.getAttribute('id') + "'";
     } catch (ex) {
         return "<unknown>";
     }
 }
 
-function PaintWaitListener(event)
+function OnRefTestLoad()
 {
-    LogInfo("MozPaintWait received for ID " + IDForEventTarget(event));
-    gExplicitPendingPaintCount++;
+    var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+                getService(Components.interfaces.nsIPrefBranch2);
+    try {
+        gBrowserIsRemote = prefs.getBoolPref("browser.tabs.remote");
+    } catch (e) {
+        gBrowserIsRemote = false;
+    }
+
+    gBrowser = document.createElementNS(XUL_NS, "xul:browser");
+    gBrowser.setAttribute("id", "browser");
+    gBrowser.setAttribute("type", "content-primary");
+    gBrowser.setAttribute("remote", gBrowserIsRemote ? "true" : "false");
+    // Make sure the browser element is exactly 800x1000, no matter
+    // what size our window is
+    gBrowser.setAttribute("style", "min-width: 800px; min-height: 1000px; max-width: 800px; max-height: 1000px");
+
+    document.getElementById("reftest-window").appendChild(gBrowser);
+
+    gBrowserMessageManager = gBrowser.QueryInterface(CI.nsIFrameLoaderOwner)
+                             .frameLoader.messageManager;
+    // The content script waits for the initial onload, then notifies
+    // us.
+    RegisterMessageListenersAndLoadContentScript();
 }
 
-function PaintWaitFinishedListener(event)
+function InitAndStartRefTests()
 {
-    LogInfo("MozPaintWaitFinished received for ID " + IDForEventTarget(event));
-    gExplicitPendingPaintCount--;
-    if (gExplicitPendingPaintCount < 0) {
-        LogWarning("Underrun in gExplicitPendingPaintCount\n");
-        gExplicitPendingPaintCount = 0;
-    }
-    if (gExplicitPendingPaintCount == 0 &&
-        gExplicitPendingPaintsCompleteHook) {
-        gExplicitPendingPaintsCompleteHook();
-    }
-}
-
-function OnRefTestLoad()
-{
-    gBrowser = document.getElementById("browser");
-
     /* set the gLoadTimeout */
     try {
       var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                   getService(Components.interfaces.nsIPrefBranch2);
       gLoadTimeout = prefs.getIntPref("reftest.timeout");
       logFile = prefs.getCharPref("reftest.logFile");
       if (logFile) {
         try {
@@ -255,18 +255,16 @@ function OnRefTestLoad()
       gTotalChunks = prefs.getIntPref("reftest.totalChunks");
       gThisChunk = prefs.getIntPref("reftest.thisChunk");
     }
     catch(e) {
       gTotalChunks = 0;
       gThisChunk = 0;
     }
 
-    gBrowser.addEventListener("load", OnDocumentLoad, true);
-
     try {
         gWindowUtils = window.QueryInterface(CI.nsIInterfaceRequestor).getInterface(CI.nsIDOMWindowUtils);
         if (gWindowUtils && !gWindowUtils.compareCanvases)
             gWindowUtils = null;
     } catch (e) {
         gWindowUtils = null;
     }
 
@@ -289,20 +287,16 @@ function OnRefTestLoad()
         ++gTestResults.Exception;
         gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n");
         DoneTests();
     }
 
     // Focus the content browser
     gBrowser.focus();
 
-    // Connect to async rendering notifications
-    gBrowser.addEventListener("MozPaintWait", PaintWaitListener, true);
-    gBrowser.addEventListener("MozPaintWaitFinished", PaintWaitFinishedListener, true);
-
     StartTests();
 }
 
 function StartHTTPServer()
 {
     gServer.registerContentType("sjs", "sjs");
     // We want to try different ports in case the port we want
     // is being used.
@@ -354,17 +348,16 @@ function StartTests()
         ++gTestResults.Exception;
         gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | EXCEPTION: " + ex + "\n");
         DoneTests();
     }
 }
 
 function OnRefTestUnload()
 {
-    gBrowser.removeEventListener("load", OnDocumentLoad, true);
     MozillaFileLogger.close();
 }
 
 // Read all available data from an input stream and return it
 // as a string.
 function getStreamContent(inputStream)
 {
   var streamBuf = "";
@@ -392,17 +385,17 @@ function BuildConditionSandbox(aURL) {
     try {
       sandbox.xulRuntime.XPCOMABI = xr.XPCOMABI;
     } catch(e) {
       sandbox.xulRuntime.XPCOMABI = "";
     }
   
     try {
       // nsIGfxInfo is currently only implemented on Windows
-      sandbox.d2d = CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo).D2DEnabled;
+      sandbox.d2d = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo).D2DEnabled;
     } catch(e) {
       sandbox.d2d = false;
     }
 
     if (gWindowUtils && gWindowUtils.layerManagerType != "Basic")
       sandbox.layersGPUAccelerated = true;
     else
       sandbox.layersGPUAccelerated = false;
@@ -487,18 +480,25 @@ function BuildConditionSandbox(aURL) {
         }
         else {
             testPluginIsOOP = prefservice.getBoolPref("dom.ipc.plugins.enabled");
         }
 
         return testPluginIsOOP;
     };
 
-    gDumpLog("REFTEST INFO | Dumping JSON representation of sandbox \n");
-    gDumpLog("REFTEST INFO | " + JSON.stringify(sandbox) + " \n");
+    // Tests shouldn't care about this except for when they need to
+    // crash the content process
+    sandbox.browserIsRemote = gBrowserIsRemote;
+
+    if (!gDumpedConditionSandbox) {
+        dump("REFTEST INFO | Dumping JSON representation of sandbox \n");
+        dump("REFTEST INFO | " + JSON.stringify(sandbox) + " \n");
+        gDumpedConditionSandbox = true;
+    }
 
     return sandbox;
 }
 
 function ReadTopManifest(aFileURL)
 {
     gURLs = new Array();
     var url = gIOService.newURI(aFileURL, null, null);
@@ -550,29 +550,32 @@ function ReadManifest(aURL)
             urlprefix = items[1];
             continue;
         }
 
         var expected_status = EXPECTED_PASS;
         var allow_silent_fail = false;
         var minAsserts = 0;
         var maxAsserts = 0;
+        var needs_focus = false;
         var slow = false;
-        while (items[0].match(/^(fails|random|skip|asserts|slow|silentfail)/)) {
+        while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|silentfail)/)) {
             var item = items.shift();
             var stat;
             var cond;
             var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
             if (m) {
                 stat = m[1];
                 // Note: m[2] contains the parentheses, and we want them.
                 cond = Components.utils.evalInSandbox(m[2], sandbox);
             } else if (item.match(/^(fails|random|skip)$/)) {
                 stat = item;
                 cond = true;
+            } else if (item == "needs-focus") {
+                needs_focus = true;
             } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
                 cond = false;
                 minAsserts = Number(m[1]);
                 maxAsserts = (m[2] == undefined) ? minAsserts
                                                  : Number(m[2].substring(1));
             } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) {
                 cond = false;
                 if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
@@ -660,16 +663,17 @@ function ReadManifest(aURL)
             secMan.checkLoadURI(aURL, testURI,
                                 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
             gURLs.push( { type: TYPE_LOAD,
                           expected: expected_status,
                           allowSilentFail: allow_silent_fail,
                           prettyPath: prettyPath,
                           minAsserts: minAsserts,
                           maxAsserts: maxAsserts,
+                          needsFocus: needs_focus,
                           slow: slow,
                           url1: testURI,
                           url2: null } );
         } else if (items[0] == TYPE_SCRIPT) {
             if (items.length != 2)
                 throw "Error 4 in manifest file " + aURL.spec + " line " + lineNo;
             var [testURI] = runHttp
                             ? ServeFiles(aURL, httpDepth,
@@ -681,16 +685,17 @@ function ReadManifest(aURL)
             secMan.checkLoadURI(aURL, testURI,
                                 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
             gURLs.push( { type: TYPE_SCRIPT,
                           expected: expected_status,
                           allowSilentFail: allow_silent_fail,
                           prettyPath: prettyPath,
                           minAsserts: minAsserts,
                           maxAsserts: maxAsserts,
+                          needsFocus: needs_focus,
                           slow: slow,
                           url1: testURI,
                           url2: null } );
         } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL) {
             if (items.length != 3)
                 throw "Error 5 in manifest file " + aURL.spec + " line " + lineNo;
             var [testURI, refURI] = runHttp
                                   ? ServeFiles(aURL, httpDepth,
@@ -705,16 +710,17 @@ function ReadManifest(aURL)
             secMan.checkLoadURI(aURL, refURI,
                                 CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
             gURLs.push( { type: items[0],
                           expected: expected_status,
                           allowSilentFail: allow_silent_fail,
                           prettyPath: prettyPath,
                           minAsserts: minAsserts,
                           maxAsserts: maxAsserts,
+                          needsFocus: needs_focus,
                           slow: slow,
                           url1: testURI,
                           url2: refURI } );
         } else {
             throw "Error 6 in manifest file " + aURL.spec + " line " + lineNo;
         }
     }
 }
@@ -782,27 +788,51 @@ function ServeFiles(manifestURL, depth, 
                             CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
 
         return testURI;
     }
 
     return files.map(FileToURI);
 }
 
+// Return true iff this window is focused when this function returns.
+function Focus()
+{
+    // FIXME/bug 583976: focus doesn't yet work with out-of-process
+    // content.
+    if (gBrowserIsRemote) {
+        return false;
+    }
+
+    // FIXME/bug 623625: determine if the window is focused and/or try
+    // to acquire focus if it's not.
+    //
+    // NB: we can't add anything here that would return false on
+    // tinderbox, otherwise we could lose testing coverage due to
+    // problems on the test machines.  We might want a require-focus
+    // mode, defaulting to false for developers, but that's true on
+    // tinderbox.
+    return true;
+}
+
 function StartCurrentTest()
 {
     gTestLog = [];
 
     // make sure we don't run tests that are expected to kill the browser
     while (gURLs.length > 0) {
         var test = gURLs[0];
         if (test.expected == EXPECTED_DEATH) {
             ++gTestResults.Skip;
             gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIP)\n");
             gURLs.shift();
+        } else if (test.needsFocus && !Focus()) {
+            ++gTestResults.Skip;
+            gDumpLog("REFTEST TEST-KNOWN-FAIL | " + test.url1.spec + " | (SKIPPED; COULDN'T GET FOCUS)\n");
+            gURLs.shift();
         } else if (test.slow && !gRunSlowTests) {
             ++gTestResults.Slow;
             gDumpLog("REFTEST TEST-KNOWN-SLOW | " + test.url1.spec + " | (SLOW)\n");
             gURLs.shift();
         } else {
             break;
         }
     }
@@ -815,46 +845,35 @@ function StartCurrentTest()
         document.title = "reftest: " + currentTest + " / " + gTotalTests +
             " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)";
         StartCurrentURI(1);
     }
 }
 
 function StartCurrentURI(aState)
 {
-    gCurrentTestStartTime = Date.now();
-    if (gFailureTimeout != null) {
-        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " +
-             "| program error managing timeouts\n");
-        ++gTestResults.Exception;
-    }
-    gFailureTimeout = setTimeout(LoadFailed, gLoadTimeout);
-    gFailureReason = "timed out waiting for onload to fire";
-
     gState = aState;
     gCurrentURL = gURLs[0]["url" + aState].spec;
-    // Reset gExplicitPendingPaintCount in case there was a timeout or
-    // the count is out of sync for some other reason
-    if (gExplicitPendingPaintCount != 0) {
-        LogWarning("Resetting gExplicitPendingPaintCount to zero (currently " +
-                   gExplicitPendingPaintCount + "\n");
-        gExplicitPendingPaintCount = 0;
-    }
 
     if (gURICanvases[gCurrentURL] &&
         (gURLs[0].type == TYPE_REFTEST_EQUAL ||
          gURLs[0].type == TYPE_REFTEST_NOTEQUAL) &&
         gURLs[0].maxAsserts == 0) {
         // Pretend the document loaded --- RecordResult will notice
         // there's already a canvas for this URL
         setTimeout(RecordResult, 0);
     } else {
         gDumpLog("REFTEST TEST-START | " + gCurrentURL + "\n");
         LogInfo("START " + gCurrentURL);
-        gBrowser.loadURI(gCurrentURL);
+        var type = gURLs[0].type
+        if (TYPE_SCRIPT == type) {
+            SendLoadScriptTest(gCurrentURL, gLoadTimeout);
+        } else {
+            SendLoadTest(type, gCurrentURL, gLoadTimeout);
+        }
     }
 }
 
 function DoneTests()
 {
     gDumpLog("REFTEST FINISHED: Slowest test took " + gSlowestTestTime +
          "ms (" + gSlowestTestURL + ")\n");
 
@@ -890,340 +909,16 @@ function DoneTests()
         goQuitApplication();
     }
     if (gServer)
         gServer.stop(onStopped);
     else
         onStopped();
 }
 
-function setupZoom(contentRootElement) {
-    if (!contentRootElement || !contentRootElement.hasAttribute('reftest-zoom'))
-        return;
-    gBrowser.markupDocumentViewer.fullZoom =
-        contentRootElement.getAttribute('reftest-zoom');
-}
-
-function resetZoom() {
-    gBrowser.markupDocumentViewer.fullZoom = 1.0;
-}
-
-function doPrintMode(contentRootElement) {
-    // use getAttribute because className works differently in HTML and SVG
-    return contentRootElement &&
-           contentRootElement.hasAttribute('class') &&
-           contentRootElement.getAttribute('class').split(/\s+/)
-                             .indexOf("reftest-print") != -1;
-}
-
-function setupPrintMode() {
-   var PSSVC = Components.classes["@mozilla.org/gfx/printsettings-service;1"]
-               .getService(Components.interfaces.nsIPrintSettingsService);
-   var ps = PSSVC.newPrintSettings;
-   ps.paperWidth = 5;
-   ps.paperHeight = 3;
-
-   // Override any os-specific unwriteable margins
-   ps.unwriteableMarginTop = 0;
-   ps.unwriteableMarginLeft = 0;
-   ps.unwriteableMarginBottom = 0;
-   ps.unwriteableMarginRight = 0;
-
-   ps.headerStrLeft = "";
-   ps.headerStrCenter = "";
-   ps.headerStrRight = "";
-   ps.footerStrLeft = "";
-   ps.footerStrCenter = "";
-   ps.footerStrRight = "";
-   gBrowser.docShell.contentViewer.setPageMode(true, ps);
-}
-
-function shouldWaitForExplicitPaintWaiters() {
-    return gExplicitPendingPaintCount > 0;
-}
-
-function shouldWaitForPendingPaints() {
-    // if gCurrentCanvas is null, we're not taking snapshots so there is
-    // no need to wait for pending paints to be flushed.
-    return gCurrentCanvas && gWindowUtils.isMozAfterPaintPending;
-}
-
-function shouldWaitForReftestWaitRemoval(contentRootElement) {
-    // use getAttribute because className works differently in HTML and SVG
-    return contentRootElement &&
-           contentRootElement.hasAttribute('class') &&
-           contentRootElement.getAttribute('class').split(/\s+/)
-                             .indexOf("reftest-wait") != -1;
-}
-
-// Initial state. When the document has loaded and all MozAfterPaint events and
-// all explicit paint waits are flushed, we can fire the MozReftestInvalidate
-// event and move to the next state.
-const STATE_WAITING_TO_FIRE_INVALIDATE_EVENT = 0;
-// When reftest-wait has been removed from the root element, we can move to the
-// next state.
-const STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL = 1;
-// When all MozAfterPaint events and all explicit paint waits are flushed, we're
-// done and can move to the COMPLETED state.
-const STATE_WAITING_TO_FINISH = 2;
-const STATE_COMPLETED = 3;
-
-function WaitForTestEnd(contentRootElement, inPrintMode) {
-    var stopAfterPaintReceived = false;
-    var state = STATE_WAITING_TO_FIRE_INVALIDATE_EVENT;
-
-    function FlushRendering() {
-        var anyPendingPaintsGeneratedInDescendants = false;
-
-        function flushWindow(win) {
-            var utils = win.QueryInterface(CI.nsIInterfaceRequestor)
-                        .getInterface(CI.nsIDOMWindowUtils);
-            var afterPaintWasPending = utils.isMozAfterPaintPending;
-
-            try {
-                // Flush pending restyles and reflows for this window
-                win.document.documentElement.getBoundingClientRect();
-            } catch (e) {
-                LogWarning("flushWindow failed: " + e + "\n");
-            }
-            
-            if (!afterPaintWasPending && utils.isMozAfterPaintPending) {
-                LogInfo("FlushRendering generated paint for window " + win.location.href);
-                anyPendingPaintsGeneratedInDescendants = true;
-            }
-
-            for (var i = 0; i < win.frames.length; ++i) {
-                flushWindow(win.frames[i]);
-            }
-        }
-
-        flushWindow(gBrowser.contentWindow);
-
-        if (anyPendingPaintsGeneratedInDescendants &&
-            !gWindowUtils.isMozAfterPaintPending) {
-            LogWarning("Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!");
-        }
-    }
-
-    function AfterPaintListener(event) {
-        LogInfo("AfterPaintListener in " + event.target.document.location.href);
-        if (event.target.document != document) {
-            // ignore paint events for subframes or old documents in the window.
-            // Invalidation in subframes will cause invalidation in the toplevel document anyway.
-            return;
-        }
-        UpdateCurrentCanvasForEvent(event);
-        // These events are fired immediately after a paint. Don't
-        // confuse ourselves by firing synchronously if we triggered the
-        // paint ourselves.
-        setTimeout(MakeProgress, 0);
-    }
-
-    function AttrModifiedListener() {
-        LogInfo("AttrModifiedListener fired");
-        // Wait for the next return-to-event-loop before continuing --- for
-        // example, the attribute may have been modified in an subdocument's
-        // load event handler, in which case we need load event processing
-        // to complete and unsuppress painting before we check isMozAfterPaintPending.
-        setTimeout(MakeProgress, 0);
-    }
-
-    function ExplicitPaintsCompleteListener() {
-        LogInfo("ExplicitPaintsCompleteListener fired");
-        // Since this can fire while painting, don't confuse ourselves by
-        // firing synchronously. It's fine to do this asynchronously.
-        setTimeout(MakeProgress, 0);
-    }
-
-    function RemoveListeners() {
-        // OK, we can end the test now.
-        window.removeEventListener("MozAfterPaint", AfterPaintListener, false);
-        if (contentRootElement) {
-          contentRootElement.removeEventListener("DOMAttrModified", AttrModifiedListener, false);
-        }
-        gExplicitPendingPaintsCompleteHook = null;
-        gTimeoutHook = null;
-        // Make sure we're in the COMPLETED state just in case
-        // (this may be called via the test-timeout hook)
-        state = STATE_COMPLETED;
-    }
-
-    // Everything that could cause shouldWaitForXXX() to
-    // change from returning true to returning false is monitored via some kind
-    // of event listener which eventually calls this function.
-    function MakeProgress() {
-        if (state >= STATE_COMPLETED) {
-            LogInfo("MakeProgress: STATE_COMPLETED");
-            return;
-        }
-
-        FlushRendering();
-
-        switch (state) {
-        case STATE_WAITING_TO_FIRE_INVALIDATE_EVENT: {
-            LogInfo("MakeProgress: STATE_WAITING_TO_FIRE_INVALIDATE_EVENT");
-            if (shouldWaitForExplicitPaintWaiters() || shouldWaitForPendingPaints()) {
-                gFailureReason = "timed out waiting for pending paint count to reach zero";
-                if (shouldWaitForExplicitPaintWaiters()) {
-                    gFailureReason += " (waiting for MozPaintWaitFinished)";
-                    LogInfo("MakeProgress: waiting for MozPaintWaitFinished");
-                }
-                if (shouldWaitForPendingPaints()) {
-                    gFailureReason += " (waiting for MozAfterPaint)";
-                    LogInfo("MakeProgress: waiting for MozAfterPaint");
-                }
-                return;
-            }
-
-            state = STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL;
-            var hasReftestWait = shouldWaitForReftestWaitRemoval(contentRootElement);            
-            // Notify the test document that now is a good time to test some invalidation
-            if (contentRootElement) {
-                var notification = document.createEvent("Events");
-                notification.initEvent("MozReftestInvalidate", true, false);
-                contentRootElement.dispatchEvent(notification);
-            }
-            if (hasReftestWait && !shouldWaitForReftestWaitRemoval(contentRootElement)) {
-                // MozReftestInvalidate handler removed reftest-wait.
-                // We expect something to have been invalidated...
-                FlushRendering();
-                if (!shouldWaitForPendingPaints() && !shouldWaitForExplicitPaintWaiters()) {
-                    LogWarning("MozInvalidateEvent didn't invalidate");
-                }
-            }
-            // Try next state
-            MakeProgress();
-            return;
-        }
-
-        case STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL:
-            LogInfo("MakeProgress: STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL");
-            if (shouldWaitForReftestWaitRemoval(contentRootElement)) {
-                gFailureReason = "timed out waiting for reftest-wait to be removed";
-                LogInfo("MakeProgress: waiting for reftest-wait to be removed");
-                return;
-            }
-            state = STATE_WAITING_TO_FINISH;
-            if (!inPrintMode && doPrintMode(contentRootElement)) {
-                LogInfo("MakeProgress: setting up print mode");
-                setupPrintMode();
-            }
-            // Try next state
-            MakeProgress();
-            return;
-
-        case STATE_WAITING_TO_FINISH:
-            LogInfo("MakeProgress: STATE_WAITING_TO_FINISH");
-            if (shouldWaitForExplicitPaintWaiters() || shouldWaitForPendingPaints()) {
-                gFailureReason = "timed out waiting for pending paint count to " +
-                    "reach zero (after reftest-wait removed and switch to print mode)";
-                if (shouldWaitForExplicitPaintWaiters()) {
-                    gFailureReason += " (waiting for MozPaintWaitFinished)";
-                    LogInfo("MakeProgress: waiting for MozPaintWaitFinished");
-                }
-                if (shouldWaitForPendingPaints()) {
-                    gFailureReason += " (waiting for MozAfterPaint)";
-                    LogInfo("MakeProgress: waiting for MozAfterPaint");
-                }
-                return;
-            }
-            LogInfo("MakeProgress: Completed");
-            state = STATE_COMPLETED;
-            gFailureReason = "timed out while taking snapshot (bug in harness?)";
-            RemoveListeners();
-            setTimeout(RecordResult, 0);
-            return;
-        }
-    }
-
-    LogInfo("WaitForTestEnd: Adding listeners");
-    window.addEventListener("MozAfterPaint", AfterPaintListener, false);
-    // If contentRootElement is null then shouldWaitForReftestWaitRemoval will
-    // always return false so we don't need a listener anyway
-    if (contentRootElement) {
-      contentRootElement.addEventListener("DOMAttrModified", AttrModifiedListener, false);
-    }
-    gExplicitPendingPaintsCompleteHook = ExplicitPaintsCompleteListener;
-    gTimeoutHook = RemoveListeners;
-
-    // Take a full snapshot now that all our listeners are set up. This
-    // ensures it's impossible for us to miss updates between taking the snapshot
-    // and adding our listeners.
-    InitCurrentCanvasWithSnapshot();
-    MakeProgress();
-}
-
-function OnDocumentLoad(event)
-{
-    var currentDoc = gBrowser.contentDocument;
-    if (event.target != currentDoc)
-        // Ignore load events for subframes.
-        return;
-
-    if (gClearingForAssertionCheck &&
-        currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
-        DoAssertionCheck();
-        return;
-    }
-
-    if (currentDoc.location.href != gCurrentURL) {
-        LogInfo("OnDocumentLoad fired for previous document");
-        // Ignore load events for previous documents.
-        return;
-    }
-
-    var contentRootElement = currentDoc ? currentDoc.documentElement : null;
-    setupZoom(contentRootElement);
-    var inPrintMode = false;
-
-    function AfterOnLoadScripts() {
-        // Take a snapshot now. We need to do this before we check whether
-        // we should wait, since this might trigger dispatching of
-        // MozPaintWait events and make shouldWaitForExplicitPaintWaiters() true
-        // below.
-        var painted = InitCurrentCanvasWithSnapshot();
-
-        if (shouldWaitForExplicitPaintWaiters() ||
-            (!inPrintMode && doPrintMode(contentRootElement)) ||
-            // If we didn't force a paint above, in
-            // InitCurrentCanvasWithSnapshot, so we should wait for a
-            // paint before we consider them done.
-            !painted) {
-            LogInfo("AfterOnLoadScripts belatedly entering WaitForTestEnd");
-            // Go into reftest-wait mode belatedly.
-            WaitForTestEnd(contentRootElement, inPrintMode);
-        } else {
-            RecordResult();
-        }
-    }
-
-    if (shouldWaitForReftestWaitRemoval(contentRootElement) ||
-        shouldWaitForExplicitPaintWaiters()) {
-        // Go into reftest-wait mode immediately after painting has been
-        // unsuppressed, after the onload event has finished dispatching.
-        gFailureReason = "timed out waiting for test to complete (trying to get into WaitForTestEnd)";
-        LogInfo("OnDocumentLoad triggering WaitForTestEnd");
-        setTimeout(WaitForTestEnd, 0, contentRootElement, inPrintMode);
-    } else {
-        if (doPrintMode(contentRootElement)) {
-            LogInfo("OnDocumentLoad setting up print mode");
-            setupPrintMode();
-            inPrintMode = true;
-        }
-
-        // Since we can't use a bubbling-phase load listener from chrome,
-        // this is a capturing phase listener.  So do setTimeout twice, the
-        // first to get us after the onload has fired in the content, and
-        // the second to get us after any setTimeout(foo, 0) in the content.
-        gFailureReason = "timed out waiting for test to complete (waiting for onload scripts to complete)";
-        LogInfo("OnDocumentLoad triggering AfterOnLoadScripts");
-        setTimeout(setTimeout, 0, AfterOnLoadScripts, 0);
-    }
-}
-
 function UpdateCanvasCache(url, canvas)
 {
     var spec = url.spec;
 
     --gURIUseCounts[spec];
 
     if (gNoCanvasCache || gURIUseCounts[spec] == 0) {
         ReleaseCanvas(canvas);
@@ -1247,16 +942,19 @@ function DoDrawWindow(ctx, x, y, w, h)
     if (0 <= testRect.left &&
         0 <= testRect.top &&
         window.innerWidth >= testRect.right &&
         window.innerHeight >= testRect.bottom) {
         // We can use the window's retained layer manager
         // because the window is big enough to display the entire
         // browser element
         flags |= ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
+    } else if (gBrowserIsRemote) {
+        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | can't drawWindow remote content\n");
+        ++gTestResults.Exception;
     }
 
     if (gDrawWindowFlags != flags) {
         // Every time the flags change, dump the new state.
         gDrawWindowFlags = flags;
         var flagsStr = "DRAWWINDOW_DRAW_CARET | DRAWWINDOW_DRAW_VIEW";
         if (flags & ctx.DRAWWINDOW_USE_WIDGET_LAYERS) {
             flagsStr += " | DRAWWINDOW_USE_WIDGET_LAYERS";
@@ -1273,72 +971,66 @@ function DoDrawWindow(ctx, x, y, w, h)
 
     LogInfo("DoDrawWindow " + x + "," + y + "," + w + "," + h);
     ctx.drawWindow(window, x, y, w, h, "rgb(255,255,255)",
                    gDrawWindowFlags);
 }
 
 function InitCurrentCanvasWithSnapshot()
 {
+    LogInfo("Initializing canvas snapshot");
+
     if (gURLs[0].type == TYPE_LOAD || gURLs[0].type == TYPE_SCRIPT) {
         // We don't want to snapshot this kind of test
         return false;
     }
 
     if (!gCurrentCanvas) {
         gCurrentCanvas = AllocateCanvas();
     }
 
     var ctx = gCurrentCanvas.getContext("2d");
     DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height);
     return true;
 }
 
-function roundTo(x, fraction)
+function UpdateCurrentCanvasForInvalidation(rects)
 {
-    return Math.round(x/fraction)*fraction;
-}
+    LogInfo("Updating canvas for invalidation");
 
-function UpdateCurrentCanvasForEvent(event)
-{
-    if (!gCurrentCanvas)
+    if (!gCurrentCanvas) {
         return;
+    }
 
     var ctx = gCurrentCanvas.getContext("2d");
-    var rectList = event.clientRects;
-    for (var i = 0; i < rectList.length; ++i) {
-        var r = rectList[i];
+    for (var i = 0; i < rects.length; ++i) {
+        var r = rects[i];
         // Set left/top/right/bottom to pixel boundaries
         var left = Math.floor(r.left);
         var top = Math.floor(r.top);
         var right = Math.ceil(r.right);
         var bottom = Math.ceil(r.bottom);
 
         ctx.save();
         ctx.translate(left, top);
         DoDrawWindow(ctx, left, top, right - left, bottom - top);
         ctx.restore();
     }
 }
 
-function RecordResult()
+function RecordResult(testRunTime, errorMsg, scriptResults)
 {
     LogInfo("RecordResult fired");
 
     // Keep track of which test was slowest, and how long it took.
-    var currentTestRunTime = Date.now() - gCurrentTestStartTime;
-    if (currentTestRunTime > gSlowestTestTime) {
-        gSlowestTestTime = currentTestRunTime;
+    if (testRunTime > gSlowestTestTime) {
+        gSlowestTestTime = testRunTime;
         gSlowestTestURL  = gCurrentURL;
     }
 
-    clearTimeout(gFailureTimeout);
-    gFailureReason = null;
-    gFailureTimeout = null;
-
     // Not 'const ...' because of 'EXPECTED_*' value dependency.
     var outputs = {};
     const randomMsg = "(EXPECTED RANDOM)";
     outputs[EXPECTED_PASS] = {
         true:  {s: "TEST-PASS"                  , n: "Pass"},
         false: {s: "TEST-UNEXPECTED-FAIL"       , n: "UnexpectedFail"}
     };
     outputs[EXPECTED_FAIL] = {
@@ -1354,74 +1046,57 @@ function RecordResult()
     if (gURLs[0].type == TYPE_LOAD) {
         ++gTestResults.LoadOnly;
         gDumpLog("REFTEST TEST-PASS | " + gURLs[0].prettyPath + " | (LOAD ONLY)\n");
         gCurrentCanvas = null;
         FinishTestItem();
         return;
     }
     if (gURLs[0].type == TYPE_SCRIPT) {
-        var missing_msg = false;
-        var testwindow = gBrowser.contentWindow;
-        expected = gURLs[0].expected;
+        var expected = gURLs[0].expected;
 
-        if (testwindow.wrappedJSObject)
-            testwindow = testwindow.wrappedJSObject;
-
-        var testcases;
-
-        if (!testwindow.getTestCases || typeof testwindow.getTestCases != "function") {
+        if (errorMsg) {
             // Force an unexpected failure to alert the test author to fix the test.
             expected = EXPECTED_PASS;
-            missing_msg = "test must provide a function getTestCases(). (SCRIPT)\n";
-        }
-        else if (!(testcases = testwindow.getTestCases())) {
-            // Force an unexpected failure to alert the test author to fix the test.
-            expected = EXPECTED_PASS;
-            missing_msg = "test's getTestCases() must return an Array-like Object. (SCRIPT)\n";
-        }
-        else if (testcases.length == 0) {
-            // This failure may be due to a JavaScript Engine bug causing
-            // early termination of the test. If we do not allow silent
-            // failure, report an error.
-            if (!gURLs[0].allowSilentFail)
-                missing_msg = "No test results reported. (SCRIPT)\n";
-            else
-                gDumpLog("REFTEST INFO | An expected silent failure occurred \n");
+        } else if (scriptResults.length == 0) {
+             // This failure may be due to a JavaScript Engine bug causing
+             // early termination of the test. If we do not allow silent
+             // failure, report an error.
+             if (!gURLs[0].allowSilentFail)
+                 errorMsg = "No test results reported. (SCRIPT)\n";
+             else
+                 gDumpLog("REFTEST INFO | An expected silent failure occurred \n");
         }
 
-        if (missing_msg) {
+        if (errorMsg) {
             output = outputs[expected][false];
             ++gTestResults[output.n];
             var result = "REFTEST " + output.s + " | " +
                 gURLs[0].prettyPath + " | " + // the URL being tested
-                missing_msg;
+                errorMsg;
 
             gDumpLog(result);
             FinishTestItem();
             return;
         }
 
-        var results = testcases.map(function(test) {
-                return { passed: test.testPassed(), description: test.testDescription()};
-            });
-        var anyFailed = results.some(function(result) { return !result.passed; });
+        var anyFailed = scriptResults.some(function(result) { return !result.passed; });
         var outputPair;
         if (anyFailed && expected == EXPECTED_FAIL) {
             // If we're marked as expected to fail, and some (but not all) tests
             // passed, treat those tests as though they were marked random
             // (since we can't tell whether they were really intended to be
             // marked failing or not).
             outputPair = { true: outputs[EXPECTED_RANDOM][true],
                            false: outputs[expected][false] };
         } else {
             outputPair = outputs[expected];
         }
         var index = 0;
-        results.forEach(function(result) {
+        scriptResults.forEach(function(result) {
                 var output = outputPair[result.passed];
 
                 ++gTestResults[output.n];
                 result = "REFTEST " + output.s + " | " +
                     gURLs[0].prettyPath + " | " + // the URL being tested
                     result.description + " item " + (++index) + "\n";
                 gDumpLog(result);
             });
@@ -1432,24 +1107,28 @@ function RecordResult()
 
         FinishTestItem();
         return;
     }
 
     if (gURICanvases[gCurrentURL]) {
         gCurrentCanvas = gURICanvases[gCurrentURL];
     }
+    if (gCurrentCanvas == null) {
+        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | program error managing snapshots\n");
+        ++gTestResults.Exception;
+    }
     if (gState == 1) {
         gCanvas1 = gCurrentCanvas;
     } else {
         gCanvas2 = gCurrentCanvas;
     }
     gCurrentCanvas = null;
 
-    resetZoom();
+    SendResetZoom();
 
     switch (gState) {
         case 1:
             // First document has been loaded.
             // Proceed to load the second document.
 
             StartCurrentURI(2);
             break;
@@ -1513,46 +1192,47 @@ function RecordResult()
 
             FinishTestItem();
             break;
         default:
             throw "Unexpected state.";
     }
 }
 
-function LoadFailed()
+function LoadFailed(why)
 {
-    if (gTimeoutHook) {
-        gTimeoutHook();
-    }
-    gFailureTimeout = null;
     ++gTestResults.FailedLoad;
     gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " +
-         gURLs[0]["url" + gState].spec + " | " + gFailureReason + "\n");
+         gURLs[0]["url" + gState].spec + " | " + why + "\n");
     FlushTestLog();
     FinishTestItem();
 }
 
 function FinishTestItem()
 {
     // Replace document with BLANK_URL_FOR_CLEARING in case there are
     // assertions when unloading.
     gDumpLog("REFTEST INFO | Loading a blank page\n");
-    gClearingForAssertionCheck = true;
-    gBrowser.loadURI(BLANK_URL_FOR_CLEARING);
+    // After clearing, content will notify us of the assertion count
+    // and tests will continue.
+    SendClear();
 }
 
-function DoAssertionCheck()
+function DoAssertionCheck(numAsserts)
 {
-    gClearingForAssertionCheck = false;
+    if (gDebug.isDebugBuild) {
+        if (gBrowserIsRemote) {
+            // Count chrome-process asserts too when content is out of
+            // process.
+            var newAssertionCount = gDebug.assertionCount;
+            var numLocalAsserts = newAssertionCount - gAssertionCount;
+            gAssertionCount = newAssertionCount;
 
-    if (gDebug.isDebugBuild) {
-        var newAssertionCount = gDebug.assertionCount;
-        var numAsserts = newAssertionCount - gAssertionCount;
-        gAssertionCount = newAssertionCount;
+            numAsserts += numLocalAsserts;
+        }
 
         var minAsserts = gURLs[0].minAsserts;
         var maxAsserts = gURLs[0].maxAsserts;
 
         var expectedAssertions = "expected " + minAsserts;
         if (minAsserts != maxAsserts) {
             expectedAssertions += " to " + maxAsserts;
         }
@@ -1575,8 +1255,130 @@ function DoAssertionCheck()
                  expectedAssertions + "\n");
         }
     }
 
     // And start the next test.
     gURLs.shift();
     StartCurrentTest();
 }
+
+
+function RegisterMessageListenersAndLoadContentScript()
+{
+    gBrowserMessageManager.addMessageListener(
+        "reftest:AssertionCount",
+        function (m) { RecvAssertionCount(m.json.count); }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:ContentReady",
+        function (m) { return RecvContentReady() }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:Exception",
+        function (m) { RecvException(m.json.what) }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:FailedLoad",
+        function (m) { RecvFailedLoad(m.json.why); }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:InitCanvasWithSnapshot",
+        function (m) { return RecvInitCanvasWithSnapshot(); }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:Log",
+        function (m) { RecvLog(m.json.type, m.json.msg); }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:ScriptResults",
+        function (m) { RecvScriptResults(m.json.runtimeMs, m.json.error, m.json.results); }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:TestDone",
+        function (m) { RecvTestDone(m.json.runtimeMs); }
+    );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:UpdateCanvasForInvalidation",
+        function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); }
+    );
+
+    gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true);
+}
+
+function RecvAssertionCount(count)
+{
+    DoAssertionCheck(count);
+}
+
+function RecvContentReady()
+{
+    InitAndStartRefTests();
+    return { remote: gBrowserIsRemote };
+}
+
+function RecvException(what)
+{
+    gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | "+ what +"\n");
+    ++gTestResults.Exception;
+}
+
+function RecvFailedLoad(why)
+{
+    LoadFailed(why);
+}
+
+function RecvInitCanvasWithSnapshot()
+{
+    var painted = InitCurrentCanvasWithSnapshot();
+    return { painted: painted };
+}
+
+function RecvLog(type, msg)
+{
+    msg = "[CONTENT] "+ msg;
+    if (type == "info") {
+        LogInfo(msg);
+    } else if (type == "warning") {
+        LogWarning(msg);
+    } else {
+        gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | | unknown log type "+ type +"\n");
+        ++gTestResults.Exception;
+    }
+}
+
+function RecvScriptResults(runtimeMs, error, results)
+{
+    RecordResult(runtimeMs, error, results);
+}
+
+function RecvTestDone(runtimeMs)
+{
+    RecordResult(runtimeMs, '', [ ]);
+}
+
+function RecvUpdateCanvasForInvalidation(rects)
+{
+    UpdateCurrentCanvasForInvalidation(rects);
+}
+
+function SendClear()
+{
+    gBrowserMessageManager.sendAsyncMessage("reftest:Clear");
+}
+
+function SendLoadScriptTest(uri, timeout)
+{
+    gBrowserMessageManager.sendAsyncMessage("reftest:LoadScriptTest",
+                                            { uri: uri, timeout: timeout });
+}
+
+function SendLoadTest(type, uri, timeout)
+{
+    gBrowserMessageManager.sendAsyncMessage("reftest:LoadTest",
+                                            { type: type, uri: uri, timeout: timeout }
+    );
+}
+
+function SendResetZoom()
+{
+    gBrowserMessageManager.sendAsyncMessage("reftest:ResetZoom");
+}
--- a/layout/tools/reftest/reftest.xul
+++ b/layout/tools/reftest/reftest.xul
@@ -47,12 +47,10 @@
         hidechrome="true"
         onload="OnRefTestLoad();"
         onunload="OnRefTestUnload();"
         style="background:white; overflow:hidden"
         >
     <script type="application/ecmascript" src="quit.js" />
     <script type="application/ecmascript" src="reftest.js" />
     <script type="application/ecmascript" src="MozillaFileLogger.js" />
-    <!-- Make sure the browser element is exactly 800x1000, no matter what size our window is -->
-    <browser id="browser" type="content-primary"
-             style="min-width: 800px; min-height: 1000px; max-width: 800px; max-height: 1000px" />
+    <!-- The reftest browser element is dynamically created, here -->
 </window>
--- a/modules/plugin/test/crashtests/crashtests.list
+++ b/modules/plugin/test/crashtests/crashtests.list
@@ -1,7 +1,11 @@
 load 41276-1.html
 load 48856-1.html
 load 110650-1.html
-skip-if(!haveTestPlugin) skip-if(cocoaWidget) script 539897-1.html
-skip-if(!haveTestPlugin) script 540114-1.html
+skip-if(browserIsRemote||!haveTestPlugin||cocoaWidget) script 539897-1.html # browserIsRemote is bug XXXXXX
+skip-if(browserIsRemote||!haveTestPlugin) script 540114-1.html # browserIsRemote is bug XXXXXX
 load 570884.html
-skip-if(!haveTestPlugin||http.platform!="X11"||!testPluginIsOOP()) load 598862.html
+# This test relies on the reading of screenX/Y forcing a round trip to
+# the X server, which is a bad assumption for <browser remote>.
+# Plugin arch is going to change anyway with OOP content so skipping
+# this test for now is OK.
+skip-if(browserIsRemote||!haveTestPlugin||http.platform!="X11"||!testPluginIsOOP()) load 598862.html
--- a/security/manager/ssl/crashtests/crashtests.list
+++ b/security/manager/ssl/crashtests/crashtests.list
@@ -1,2 +1,2 @@
-load 327524-1.html
-load 398665-1.html
+asserts-if(browserIsRemote,1) load 327524-1.html # bug 582297
+asserts-if(browserIsRemote,1) load 398665-1.html # bug 582297
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -107,26 +107,55 @@ else
 	$(RUN_MOCHITEST) --setpref=dom.ipc.plugins.enabled=true --test-path=modules/plugin/test
 endif
 	$(CHECK_TEST_ERROR)
 
 # Usage: |make [EXTRA_TEST_ARGS=...] *test|.
 RUN_REFTEST = rm -f ./$@.log && $(PYTHON) _tests/reftest/runreftest.py \
   $(SYMBOLS_PATH) $(EXTRA_TEST_ARGS) $(1) | tee ./$@.log
 
+ifeq ($(OS_ARCH),WINNT) #{
+# GPU-rendered shadow layers are unsupported here
+OOP_CONTENT = --setpref=browser.tabs.remote=true --setpref=layers.acceleration.disabled=true
+GPU_RENDERING =
+else
+OOP_CONTENT = --setpref=browser.tabs.remote=true
+GPU_RENDERING = --setpref=layers.acceleration.force-enabled=true
+endif #}
+
 reftest: TEST_PATH?=layout/reftests/reftest.list
 reftest:
 	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH))
 	$(CHECK_TEST_ERROR)
 
+reftest-ipc: TEST_PATH?=layout/reftests/reftest.list
+reftest-ipc:
+	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH) $(OOP_CONTENT))
+	$(CHECK_TEST_ERROR)
+
+reftest-ipc-gpu: TEST_PATH?=layout/reftests/reftest.list
+reftest-ipc-gpu:
+	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH) $(OOP_CONTENT) $(GPU_RENDERING))
+	$(CHECK_TEST_ERROR)
+
 crashtest: TEST_PATH?=testing/crashtest/crashtests.list
 crashtest:
 	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH))
 	$(CHECK_TEST_ERROR)
 
+crashtest-ipc: TEST_PATH?=testing/crashtest/crashtests.list
+crashtest-ipc:
+	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH) $(OOP_CONTENT))
+	$(CHECK_TEST_ERROR)
+
+crashtest-ipc-gpu: TEST_PATH?=testing/crashtest/crashtests.list
+crashtest-ipc-gpu:
+	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH) $(OOP_CONTENT) $(GPU_RENDERING))
+	$(CHECK_TEST_ERROR)
+
 jstestbrowser: TEST_PATH?=js/src/tests/jstests.list
 jstestbrowser:
 	$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH) --extra-profile-file=$(topsrcdir)/js/src/tests/user.js)
 	$(CHECK_TEST_ERROR)
 
 GARBAGE += $(addsuffix .log,$(MOCHITESTS) reftest crashtest jstestbrowser)
 
 # Execute all xpcshell tests in the directories listed in the manifest.