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 id18526
push usercjones@mozilla.com
push dateThu, 03 Feb 2011 19:54:32 +0000
treeherdermozilla-central@c1523d3f7841 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, roc, ted, a
bugs615386
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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.