layout/tools/reftest/reftest-content.js

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
     2 /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 const CC = Components.classes;
     8 const CI = Components.interfaces;
     9 const CR = Components.results;
    10 const CU = Components.utils;
    12 const XHTML_NS = "http://www.w3.org/1999/xhtml";
    14 const DEBUG_CONTRACTID = "@mozilla.org/xpcom/debug;1";
    15 const PRINTSETTINGS_CONTRACTID = "@mozilla.org/gfx/printsettings-service;1";
    16 const ENVIRONMENT_CONTRACTID = "@mozilla.org/process/environment;1";
    18 // "<!--CLEAR-->"
    19 const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
    21 CU.import("resource://gre/modules/Timer.jsm");
    22 CU.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm");
    24 var gBrowserIsRemote;
    25 var gHaveCanvasSnapshot = false;
    26 // Plugin layers can be updated asynchronously, so to make sure that all
    27 // layer surfaces have the right content, we need to listen for explicit
    28 // "MozPaintWait" and "MozPaintWaitFinished" events that signal when it's OK
    29 // to take snapshots. We cannot take a snapshot while the number of
    30 // "MozPaintWait" events fired exceeds the number of "MozPaintWaitFinished"
    31 // events fired. We count the number of such excess events here. When
    32 // the counter reaches zero we call gExplicitPendingPaintsCompleteHook.
    33 var gExplicitPendingPaintCount = 0;
    34 var gExplicitPendingPaintsCompleteHook;
    35 var gCurrentURL;
    36 var gCurrentTestType;
    37 var gTimeoutHook = null;
    38 var gFailureTimeout = null;
    39 var gFailureReason;
    40 var gAssertionCount = 0;
    42 var gDebug;
    43 var gVerbose = false;
    45 var gCurrentTestStartTime;
    46 var gClearingForAssertionCheck = false;
    48 const TYPE_LOAD = 'load';  // test without a reference (just test that it does
    49                            // not assert, crash, hang, or leak)
    50 const TYPE_SCRIPT = 'script'; // test contains individual test results
    52 function markupDocumentViewer() {
    53     return docShell.contentViewer.QueryInterface(CI.nsIMarkupDocumentViewer);
    54 }
    56 function webNavigation() {
    57     return docShell.QueryInterface(CI.nsIWebNavigation);
    58 }
    60 function windowUtils() {
    61     return content.QueryInterface(CI.nsIInterfaceRequestor)
    62                   .getInterface(CI.nsIDOMWindowUtils);
    63 }
    65 function IDForEventTarget(event)
    66 {
    67     try {
    68         return "'" + event.target.getAttribute('id') + "'";
    69     } catch (ex) {
    70         return "<unknown>";
    71     }
    72 }
    74 function PaintWaitListener(event)
    75 {
    76     LogInfo("MozPaintWait received for ID " + IDForEventTarget(event));
    77     gExplicitPendingPaintCount++;
    78 }
    80 function PaintWaitFinishedListener(event)
    81 {
    82     LogInfo("MozPaintWaitFinished received for ID " + IDForEventTarget(event));
    83     gExplicitPendingPaintCount--;
    84     if (gExplicitPendingPaintCount < 0) {
    85         LogWarning("Underrun in gExplicitPendingPaintCount\n");
    86         gExplicitPendingPaintCount = 0;
    87     }
    88     if (gExplicitPendingPaintCount == 0 &&
    89         gExplicitPendingPaintsCompleteHook) {
    90         gExplicitPendingPaintsCompleteHook();
    91     }
    92 }
    94 function OnInitialLoad()
    95 {
    96 #ifndef REFTEST_B2G
    97     removeEventListener("load", OnInitialLoad, true);
    98 #endif
   100     gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2);
   101     var env = CC[ENVIRONMENT_CONTRACTID].getService(CI.nsIEnvironment);
   102     gVerbose = !!env.get("MOZ_REFTEST_VERBOSE");
   104     RegisterMessageListeners();
   106     var initInfo = SendContentReady();
   107     gBrowserIsRemote = initInfo.remote;
   109     addEventListener("load", OnDocumentLoad, true);
   111     addEventListener("MozPaintWait", PaintWaitListener, true);
   112     addEventListener("MozPaintWaitFinished", PaintWaitFinishedListener, true);
   114     LogWarning("Using browser remote="+ gBrowserIsRemote +"\n");
   115 }
   117 function StartTestURI(type, uri, timeout)
   118 {
   119     // Reset gExplicitPendingPaintCount in case there was a timeout or
   120     // the count is out of sync for some other reason
   121     if (gExplicitPendingPaintCount != 0) {
   122         LogWarning("Resetting gExplicitPendingPaintCount to zero (currently " +
   123                    gExplicitPendingPaintCount + "\n");
   124         gExplicitPendingPaintCount = 0;
   125     }
   127     gCurrentTestType = type;
   128     gCurrentURL = uri;
   130     gCurrentTestStartTime = Date.now();
   131     if (gFailureTimeout != null) {
   132         SendException("program error managing timeouts\n");
   133     }
   134     gFailureTimeout = setTimeout(LoadFailed, timeout);
   136     LoadURI(gCurrentURL);
   137 }
   139 function setupZoom(contentRootElement) {
   140     if (!contentRootElement || !contentRootElement.hasAttribute('reftest-zoom'))
   141         return;
   142     markupDocumentViewer().fullZoom =
   143         contentRootElement.getAttribute('reftest-zoom');
   144 }
   146 function resetZoom() {
   147     markupDocumentViewer().fullZoom = 1.0;
   148 }
   150 function doPrintMode(contentRootElement) {
   151 #if REFTEST_B2G
   152     // nsIPrintSettings not available in B2G
   153     return false;
   154 #else
   155     // use getAttribute because className works differently in HTML and SVG
   156     return contentRootElement &&
   157            contentRootElement.hasAttribute('class') &&
   158            contentRootElement.getAttribute('class').split(/\s+/)
   159                              .indexOf("reftest-print") != -1;
   160 #endif
   161 }
   163 function setupPrintMode() {
   164    var PSSVC =
   165        CC[PRINTSETTINGS_CONTRACTID].getService(CI.nsIPrintSettingsService);
   166    var ps = PSSVC.newPrintSettings;
   167    ps.paperWidth = 5;
   168    ps.paperHeight = 3;
   170    // Override any os-specific unwriteable margins
   171    ps.unwriteableMarginTop = 0;
   172    ps.unwriteableMarginLeft = 0;
   173    ps.unwriteableMarginBottom = 0;
   174    ps.unwriteableMarginRight = 0;
   176    ps.headerStrLeft = "";
   177    ps.headerStrCenter = "";
   178    ps.headerStrRight = "";
   179    ps.footerStrLeft = "";
   180    ps.footerStrCenter = "";
   181    ps.footerStrRight = "";
   182    docShell.contentViewer.setPageMode(true, ps);
   183 }
   185 function attrOrDefault(element, attr, def) {
   186     return element.hasAttribute(attr) ? Number(element.getAttribute(attr)) : def;
   187 }
   189 function setupViewport(contentRootElement) {
   190     if (!contentRootElement) {
   191         return;
   192     }
   194     var vw = attrOrDefault(contentRootElement, "reftest-viewport-w", 0);
   195     var vh = attrOrDefault(contentRootElement, "reftest-viewport-h", 0);
   196     if (vw !== 0 || vh !== 0) {
   197         LogInfo("Setting viewport to <w="+ vw +", h="+ vh +">");
   198         windowUtils().setCSSViewport(vw, vh);
   199     }
   201     // XXX support resolution when needed
   203     // XXX support viewconfig when needed
   204 }
   206 function setupDisplayport(contentRootElement) {
   207     if (!contentRootElement) {
   208         return;
   209     }
   211     function setupDisplayportForElement(element) {
   212         var dpw = attrOrDefault(element, "reftest-displayport-w", 0);
   213         var dph = attrOrDefault(element, "reftest-displayport-h", 0);
   214         var dpx = attrOrDefault(element, "reftest-displayport-x", 0);
   215         var dpy = attrOrDefault(element, "reftest-displayport-y", 0);
   216         if (dpw !== 0 || dph !== 0 || dpx != 0 || dpy != 0) {
   217             LogInfo("Setting displayport to <x="+ dpx +", y="+ dpy +", w="+ dpw +", h="+ dph +">");
   218             windowUtils().setDisplayPortForElement(dpx, dpy, dpw, dph, element, 1);
   219         }
   220     }
   222     function setupDisplayportForElementSubtree(element) {
   223         setupDisplayportForElement(element);
   224         for (var c = element.firstElementChild; c; c = c.nextElementSibling) {
   225             setupDisplayportForElementSubtree(c);
   226         }
   227     }
   229     if (contentRootElement.hasAttribute("reftest-async-scroll")) {
   230         SendEnableAsyncScroll();
   231         setupDisplayportForElementSubtree(contentRootElement);
   232     } else {
   233         setupDisplayportForElement(contentRootElement);
   234     }
   235 }
   237 function setupAsyncScrollOffsets(options) {
   238     var currentDoc = content.document;
   239     var contentRootElement = currentDoc ? currentDoc.documentElement : null;
   241     if (!contentRootElement) {
   242         return;
   243     }
   245     function setupAsyncScrollOffsetsForElement(element) {
   246         var sx = attrOrDefault(element, "reftest-async-scroll-x", 0);
   247         var sy = attrOrDefault(element, "reftest-async-scroll-y", 0);
   248         if (sx != 0 || sy != 0) {
   249             try {
   250                 // This might fail when called from RecordResult since layers
   251                 // may not have been constructed yet
   252                 windowUtils().setAsyncScrollOffset(element, sx, sy);
   253             } catch (e) {
   254                 if (!options.allowFailure) {
   255                     throw e;
   256                 }
   257             }
   258         }
   259     }
   261     function setupAsyncScrollOffsetsForElementSubtree(element) {
   262         setupAsyncScrollOffsetsForElement(element);
   263         for (var c = element.firstElementChild; c; c = c.nextElementSibling) {
   264             setupAsyncScrollOffsetsForElementSubtree(c);
   265         }
   266     }
   268     var asyncScroll = contentRootElement.hasAttribute("reftest-async-scroll");
   269     if (asyncScroll) {
   270         setupAsyncScrollOffsetsForElementSubtree(contentRootElement);
   271     }
   272 }
   274 function resetDisplayportAndViewport() {
   275     // XXX currently the displayport configuration lives on the
   276     // presshell and so is "reset" on nav when we get a new presshell.
   277 }
   279 function shouldWaitForExplicitPaintWaiters() {
   280     return gExplicitPendingPaintCount > 0;
   281 }
   283 function shouldWaitForPendingPaints() {
   284     // if gHaveCanvasSnapshot is false, we're not taking snapshots so
   285     // there is no need to wait for pending paints to be flushed.
   286     return gHaveCanvasSnapshot && windowUtils().isMozAfterPaintPending;
   287 }
   289 function shouldWaitForReftestWaitRemoval(contentRootElement) {
   290     // use getAttribute because className works differently in HTML and SVG
   291     return contentRootElement &&
   292            contentRootElement.hasAttribute('class') &&
   293            contentRootElement.getAttribute('class').split(/\s+/)
   294                              .indexOf("reftest-wait") != -1;
   295 }
   297 function shouldSnapshotWholePage(contentRootElement) {
   298     // use getAttribute because className works differently in HTML and SVG
   299     return contentRootElement &&
   300            contentRootElement.hasAttribute('class') &&
   301            contentRootElement.getAttribute('class').split(/\s+/)
   302                              .indexOf("reftest-snapshot-all") != -1;
   303 }
   305 function getNoPaintElements(contentRootElement) {
   306   return contentRootElement.getElementsByClassName('reftest-no-paint');
   307 }
   309 // Initial state. When the document has loaded and all MozAfterPaint events and
   310 // all explicit paint waits are flushed, we can fire the MozReftestInvalidate
   311 // event and move to the next state.
   312 const STATE_WAITING_TO_FIRE_INVALIDATE_EVENT = 0;
   313 // When reftest-wait has been removed from the root element, we can move to the
   314 // next state.
   315 const STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL = 1;
   316 // When spell checking is done on all spell-checked elements, we can move to the
   317 // next state.
   318 const STATE_WAITING_FOR_SPELL_CHECKS = 2;
   319 // When all MozAfterPaint events and all explicit paint waits are flushed, we're
   320 // done and can move to the COMPLETED state.
   321 const STATE_WAITING_TO_FINISH = 3;
   322 const STATE_COMPLETED = 4;
   324 function FlushRendering() {
   325     var anyPendingPaintsGeneratedInDescendants = false;
   327     function flushWindow(win) {
   328         var utils = win.QueryInterface(CI.nsIInterfaceRequestor)
   329                     .getInterface(CI.nsIDOMWindowUtils);
   330         var afterPaintWasPending = utils.isMozAfterPaintPending;
   332         if (win.document.documentElement) {
   333             try {
   334                 // Flush pending restyles and reflows for this window
   335                 win.document.documentElement.getBoundingClientRect();
   336             } catch (e) {
   337                 LogWarning("flushWindow failed: " + e + "\n");
   338             }
   339         }
   341         if (!afterPaintWasPending && utils.isMozAfterPaintPending) {
   342             LogInfo("FlushRendering generated paint for window " + win.location.href);
   343             anyPendingPaintsGeneratedInDescendants = true;
   344         }
   346         for (var i = 0; i < win.frames.length; ++i) {
   347             flushWindow(win.frames[i]);
   348         }
   349     }
   351     flushWindow(content);
   353     if (anyPendingPaintsGeneratedInDescendants &&
   354         !windowUtils().isMozAfterPaintPending) {
   355         LogWarning("Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!");
   356     }
   357 }
   359 function WaitForTestEnd(contentRootElement, inPrintMode, spellCheckedElements) {
   360     var stopAfterPaintReceived = false;
   361     var currentDoc = content.document;
   362     var state = STATE_WAITING_TO_FIRE_INVALIDATE_EVENT;
   364     function AfterPaintListener(event) {
   365         LogInfo("AfterPaintListener in " + event.target.document.location.href);
   366         if (event.target.document != currentDoc) {
   367             // ignore paint events for subframes or old documents in the window.
   368             // Invalidation in subframes will cause invalidation in the toplevel document anyway.
   369             return;
   370         }
   372         SendUpdateCanvasForEvent(event, contentRootElement);
   373         // These events are fired immediately after a paint. Don't
   374         // confuse ourselves by firing synchronously if we triggered the
   375         // paint ourselves.
   376         setTimeout(MakeProgress, 0);
   377     }
   379     function AttrModifiedListener() {
   380         LogInfo("AttrModifiedListener fired");
   381         // Wait for the next return-to-event-loop before continuing --- for
   382         // example, the attribute may have been modified in an subdocument's
   383         // load event handler, in which case we need load event processing
   384         // to complete and unsuppress painting before we check isMozAfterPaintPending.
   385         setTimeout(MakeProgress, 0);
   386     }
   388     function ExplicitPaintsCompleteListener() {
   389         LogInfo("ExplicitPaintsCompleteListener fired");
   390         // Since this can fire while painting, don't confuse ourselves by
   391         // firing synchronously. It's fine to do this asynchronously.
   392         setTimeout(MakeProgress, 0);
   393     }
   395     function RemoveListeners() {
   396         // OK, we can end the test now.
   397         removeEventListener("MozAfterPaint", AfterPaintListener, false);
   398         if (contentRootElement) {
   399             contentRootElement.removeEventListener("DOMAttrModified", AttrModifiedListener, false);
   400         }
   401         gExplicitPendingPaintsCompleteHook = null;
   402         gTimeoutHook = null;
   403         // Make sure we're in the COMPLETED state just in case
   404         // (this may be called via the test-timeout hook)
   405         state = STATE_COMPLETED;
   406     }
   408     // Everything that could cause shouldWaitForXXX() to
   409     // change from returning true to returning false is monitored via some kind
   410     // of event listener which eventually calls this function.
   411     function MakeProgress() {
   412         if (state >= STATE_COMPLETED) {
   413             LogInfo("MakeProgress: STATE_COMPLETED");
   414             return;
   415         }
   417         FlushRendering();
   419         switch (state) {
   420         case STATE_WAITING_TO_FIRE_INVALIDATE_EVENT: {
   421             LogInfo("MakeProgress: STATE_WAITING_TO_FIRE_INVALIDATE_EVENT");
   422             if (shouldWaitForExplicitPaintWaiters() || shouldWaitForPendingPaints()) {
   423                 gFailureReason = "timed out waiting for pending paint count to reach zero";
   424                 if (shouldWaitForExplicitPaintWaiters()) {
   425                     gFailureReason += " (waiting for MozPaintWaitFinished)";
   426                     LogInfo("MakeProgress: waiting for MozPaintWaitFinished");
   427                 }
   428                 if (shouldWaitForPendingPaints()) {
   429                     gFailureReason += " (waiting for MozAfterPaint)";
   430                     LogInfo("MakeProgress: waiting for MozAfterPaint");
   431                 }
   432                 return;
   433             }
   435             state = STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL;
   436             var hasReftestWait = shouldWaitForReftestWaitRemoval(contentRootElement);
   437             // Notify the test document that now is a good time to test some invalidation
   438             LogInfo("MakeProgress: dispatching MozReftestInvalidate");
   439             if (contentRootElement) {
   440                 var elements = getNoPaintElements(contentRootElement);
   441                 for (var i = 0; i < elements.length; ++i) {
   442                   windowUtils().checkAndClearPaintedState(elements[i]);
   443                 }
   444                 var notification = content.document.createEvent("Events");
   445                 notification.initEvent("MozReftestInvalidate", true, false);
   446                 contentRootElement.dispatchEvent(notification);
   447             }
   449             if (!inPrintMode && doPrintMode(contentRootElement)) {
   450                 LogInfo("MakeProgress: setting up print mode");
   451                 setupPrintMode();
   452             }
   454             if (hasReftestWait && !shouldWaitForReftestWaitRemoval(contentRootElement)) {
   455                 // MozReftestInvalidate handler removed reftest-wait.
   456                 // We expect something to have been invalidated...
   457                 FlushRendering();
   458                 if (!shouldWaitForPendingPaints() && !shouldWaitForExplicitPaintWaiters()) {
   459                     LogWarning("MozInvalidateEvent didn't invalidate");
   460                 }
   461             }
   462             // Try next state
   463             MakeProgress();
   464             return;
   465         }
   467         case STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL:
   468             LogInfo("MakeProgress: STATE_WAITING_FOR_REFTEST_WAIT_REMOVAL");
   469             if (shouldWaitForReftestWaitRemoval(contentRootElement)) {
   470                 gFailureReason = "timed out waiting for reftest-wait to be removed";
   471                 LogInfo("MakeProgress: waiting for reftest-wait to be removed");
   472                 return;
   473             }
   475             // Try next state
   476             state = STATE_WAITING_FOR_SPELL_CHECKS;
   477             MakeProgress();
   478             return;
   480         case STATE_WAITING_FOR_SPELL_CHECKS:
   481             LogInfo("MakeProgress: STATE_WAITING_FOR_SPELL_CHECKS");
   482             if (numPendingSpellChecks) {
   483                 gFailureReason = "timed out waiting for spell checks to end";
   484                 LogInfo("MakeProgress: waiting for spell checks to end");
   485                 return;
   486             }
   488             state = STATE_WAITING_TO_FINISH;
   489             // Try next state
   490             MakeProgress();
   491             return;
   493         case STATE_WAITING_TO_FINISH:
   494             LogInfo("MakeProgress: STATE_WAITING_TO_FINISH");
   495             if (shouldWaitForExplicitPaintWaiters() || shouldWaitForPendingPaints()) {
   496                 gFailureReason = "timed out waiting for pending paint count to " +
   497                     "reach zero (after reftest-wait removed and switch to print mode)";
   498                 if (shouldWaitForExplicitPaintWaiters()) {
   499                     gFailureReason += " (waiting for MozPaintWaitFinished)";
   500                     LogInfo("MakeProgress: waiting for MozPaintWaitFinished");
   501                 }
   502                 if (shouldWaitForPendingPaints()) {
   503                     gFailureReason += " (waiting for MozAfterPaint)";
   504                     LogInfo("MakeProgress: waiting for MozAfterPaint");
   505                 }
   506                 return;
   507             }
   508             if (contentRootElement) {
   509               var elements = getNoPaintElements(contentRootElement);
   510               for (var i = 0; i < elements.length; ++i) {
   511                   if (windowUtils().checkAndClearPaintedState(elements[i])) {
   512                       SendFailedNoPaint();
   513                   }
   514               }
   515             }
   516             LogInfo("MakeProgress: Completed");
   517             state = STATE_COMPLETED;
   518             gFailureReason = "timed out while taking snapshot (bug in harness?)";
   519             RemoveListeners();
   520             CheckForProcessCrashExpectation();
   521             setTimeout(RecordResult, 0);
   522             return;
   523         }
   524     }
   526     LogInfo("WaitForTestEnd: Adding listeners");
   527     addEventListener("MozAfterPaint", AfterPaintListener, false);
   528     // If contentRootElement is null then shouldWaitForReftestWaitRemoval will
   529     // always return false so we don't need a listener anyway
   530     if (contentRootElement) {
   531       contentRootElement.addEventListener("DOMAttrModified", AttrModifiedListener, false);
   532     }
   533     gExplicitPendingPaintsCompleteHook = ExplicitPaintsCompleteListener;
   534     gTimeoutHook = RemoveListeners;
   536     // Listen for spell checks on spell-checked elements.
   537     var numPendingSpellChecks = spellCheckedElements.length;
   538     function decNumPendingSpellChecks() {
   539         --numPendingSpellChecks;
   540         MakeProgress();
   541     }
   542     for (let editable of spellCheckedElements) {
   543         try {
   544             onSpellCheck(editable, decNumPendingSpellChecks);
   545         } catch (err) {
   546             // The element may not have an editor, so ignore it.
   547             setTimeout(decNumPendingSpellChecks, 0);
   548         }
   549     }
   551     // Take a full snapshot now that all our listeners are set up. This
   552     // ensures it's impossible for us to miss updates between taking the snapshot
   553     // and adding our listeners.
   554     SendInitCanvasWithSnapshot();
   555     MakeProgress();
   556 }
   558 function OnDocumentLoad(event)
   559 {
   560     var currentDoc = content.document;
   561     if (event.target != currentDoc)
   562         // Ignore load events for subframes.
   563         return;
   565     if (gClearingForAssertionCheck &&
   566         currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
   567         DoAssertionCheck();
   568         return;
   569     }
   571     if (currentDoc.location.href != gCurrentURL) {
   572         LogInfo("OnDocumentLoad fired for previous document");
   573         // Ignore load events for previous documents.
   574         return;
   575     }
   577     // Collect all editable, spell-checked elements.  It may be the case that
   578     // not all the elements that match this selector will be spell checked: for
   579     // example, a textarea without a spellcheck attribute may have a parent with
   580     // spellcheck=false, or script may set spellcheck=false on an element whose
   581     // markup sets it to true.  But that's OK since onSpellCheck detects the
   582     // absence of spell checking, too.
   583     var querySelector =
   584         '*[class~="spell-checked"],' +
   585         'textarea:not([spellcheck="false"]),' +
   586         'input[spellcheck]:-moz-any([spellcheck=""],[spellcheck="true"]),' +
   587         '*[contenteditable]:-moz-any([contenteditable=""],[contenteditable="true"])';
   588     var spellCheckedElements = currentDoc.querySelectorAll(querySelector);
   590     var contentRootElement = currentDoc ? currentDoc.documentElement : null;
   591     currentDoc = null;
   592     setupZoom(contentRootElement);
   593     setupViewport(contentRootElement);
   594     setupDisplayport(contentRootElement);
   595     var inPrintMode = false;
   597     function AfterOnLoadScripts() {
   598         // Regrab the root element, because the document may have changed.
   599         var contentRootElement =
   600           content.document ? content.document.documentElement : null;
   602         // Flush the document in case it got modified in a load event handler.
   603         FlushRendering();
   605         // Take a snapshot now. We need to do this before we check whether
   606         // we should wait, since this might trigger dispatching of
   607         // MozPaintWait events and make shouldWaitForExplicitPaintWaiters() true
   608         // below.
   609         var painted = SendInitCanvasWithSnapshot();
   611         if (shouldWaitForExplicitPaintWaiters() ||
   612             (!inPrintMode && doPrintMode(contentRootElement)) ||
   613             // If we didn't force a paint above, in
   614             // InitCurrentCanvasWithSnapshot, so we should wait for a
   615             // paint before we consider them done.
   616             !painted) {
   617             LogInfo("AfterOnLoadScripts belatedly entering WaitForTestEnd");
   618             // Go into reftest-wait mode belatedly.
   619             WaitForTestEnd(contentRootElement, inPrintMode, []);
   620         } else {
   621             CheckForProcessCrashExpectation();
   622             RecordResult();
   623         }
   624     }
   626     if (shouldWaitForReftestWaitRemoval(contentRootElement) ||
   627         shouldWaitForExplicitPaintWaiters() ||
   628         spellCheckedElements.length) {
   629         // Go into reftest-wait mode immediately after painting has been
   630         // unsuppressed, after the onload event has finished dispatching.
   631         gFailureReason = "timed out waiting for test to complete (trying to get into WaitForTestEnd)";
   632         LogInfo("OnDocumentLoad triggering WaitForTestEnd");
   633         setTimeout(function () { WaitForTestEnd(contentRootElement, inPrintMode, spellCheckedElements); }, 0);
   634     } else {
   635         if (doPrintMode(contentRootElement)) {
   636             LogInfo("OnDocumentLoad setting up print mode");
   637             setupPrintMode();
   638             inPrintMode = true;
   639         }
   641         // Since we can't use a bubbling-phase load listener from chrome,
   642         // this is a capturing phase listener.  So do setTimeout twice, the
   643         // first to get us after the onload has fired in the content, and
   644         // the second to get us after any setTimeout(foo, 0) in the content.
   645         gFailureReason = "timed out waiting for test to complete (waiting for onload scripts to complete)";
   646         LogInfo("OnDocumentLoad triggering AfterOnLoadScripts");
   647         setTimeout(function () { setTimeout(AfterOnLoadScripts, 0); }, 0);
   648     }
   649 }
   651 function CheckForProcessCrashExpectation()
   652 {
   653     var contentRootElement = content.document.documentElement;
   654     if (contentRootElement &&
   655         contentRootElement.hasAttribute('class') &&
   656         contentRootElement.getAttribute('class').split(/\s+/)
   657                           .indexOf("reftest-expect-process-crash") != -1) {
   658         SendExpectProcessCrash();
   659     }
   660 }
   662 function RecordResult()
   663 {
   664     LogInfo("RecordResult fired");
   666     var currentTestRunTime = Date.now() - gCurrentTestStartTime;
   668     clearTimeout(gFailureTimeout);
   669     gFailureReason = null;
   670     gFailureTimeout = null;
   672     if (gCurrentTestType == TYPE_SCRIPT) {
   673         var error = '';
   674         var testwindow = content;
   676         if (testwindow.wrappedJSObject)
   677             testwindow = testwindow.wrappedJSObject;
   679         var testcases;
   680         if (!testwindow.getTestCases || typeof testwindow.getTestCases != "function") {
   681             // Force an unexpected failure to alert the test author to fix the test.
   682             error = "test must provide a function getTestCases(). (SCRIPT)\n";
   683         }
   684         else if (!(testcases = testwindow.getTestCases())) {
   685             // Force an unexpected failure to alert the test author to fix the test.
   686             error = "test's getTestCases() must return an Array-like Object. (SCRIPT)\n";
   687         }
   688         else if (testcases.length == 0) {
   689             // This failure may be due to a JavaScript Engine bug causing
   690             // early termination of the test. If we do not allow silent
   691             // failure, the driver will report an error.
   692         }
   694         var results = [ ];
   695         if (!error) {
   696             // FIXME/bug 618176: temporary workaround
   697             for (var i = 0; i < testcases.length; ++i) {
   698                 var test = testcases[i];
   699                 results.push({ passed: test.testPassed(),
   700                                description: test.testDescription() });
   701             }
   702             //results = testcases.map(function(test) {
   703             //        return { passed: test.testPassed(),
   704             //                 description: test.testDescription() };
   705         }
   707         SendScriptResults(currentTestRunTime, error, results);
   708         FinishTestItem();
   709         return;
   710     }
   712     // Setup async scroll offsets now in case SynchronizeForSnapshot is not
   713     // called (due to reftest-no-sync-layers being supplied).
   714     setupAsyncScrollOffsets({allowFailure:true});
   715     SendTestDone(currentTestRunTime);
   716     FinishTestItem();
   717 }
   719 function LoadFailed()
   720 {
   721     if (gTimeoutHook) {
   722         gTimeoutHook();
   723     }
   724     gFailureTimeout = null;
   725     SendFailedLoad(gFailureReason);
   726 }
   728 function FinishTestItem()
   729 {
   730     gHaveCanvasSnapshot = false;
   731 }
   733 function DoAssertionCheck()
   734 {
   735     gClearingForAssertionCheck = false;
   737     var numAsserts = 0;
   738     if (gDebug.isDebugBuild) {
   739         var newAssertionCount = gDebug.assertionCount;
   740         numAsserts = newAssertionCount - gAssertionCount;
   741         gAssertionCount = newAssertionCount;
   742     }
   743     SendAssertionCount(numAsserts);
   744 }
   746 function LoadURI(uri)
   747 {
   748     var flags = webNavigation().LOAD_FLAGS_NONE;
   749     webNavigation().loadURI(uri, flags, null, null, null);
   750 }
   752 function LogWarning(str)
   753 {
   754     if (gVerbose) {
   755         sendSyncMessage("reftest:Log", { type: "warning", msg: str });
   756     } else {
   757         sendAsyncMessage("reftest:Log", { type: "warning", msg: str });
   758     }
   759 }
   761 function LogInfo(str)
   762 {
   763     if (gVerbose) {
   764         sendSyncMessage("reftest:Log", { type: "info", msg: str });
   765     } else {
   766         sendAsyncMessage("reftest:Log", { type: "info", msg: str });
   767     }
   768 }
   770 const SYNC_DEFAULT = 0x0;
   771 const SYNC_ALLOW_DISABLE = 0x1;
   772 function SynchronizeForSnapshot(flags)
   773 {
   774     if (gCurrentTestType == TYPE_SCRIPT ||
   775         gCurrentTestType == TYPE_LOAD) {
   776         // Script tests or load-only tests do not need any snapshotting
   777         return;
   778     }
   780     if (flags & SYNC_ALLOW_DISABLE) {
   781         var docElt = content.document.documentElement;
   782         if (docElt && docElt.hasAttribute("reftest-no-sync-layers")) {
   783             LogInfo("Test file chose to skip SynchronizeForSnapshot");
   784             return;
   785         }
   786     }
   788     var dummyCanvas = content.document.createElementNS(XHTML_NS, "canvas");
   789     dummyCanvas.setAttribute("width", 1);
   790     dummyCanvas.setAttribute("height", 1);
   792     var ctx = dummyCanvas.getContext("2d");
   793     var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
   794     ctx.drawWindow(content, 0, 0, 1, 1, "rgb(255,255,255)", flags);
   796     // Setup async scroll offsets now, because any scrollable layers should
   797     // have had their AsyncPanZoomControllers created.
   798     setupAsyncScrollOffsets({allowFailure:false});
   799 }
   801 function RegisterMessageListeners()
   802 {
   803     addMessageListener(
   804         "reftest:Clear",
   805         function (m) { RecvClear() }
   806     );
   807     addMessageListener(
   808         "reftest:LoadScriptTest",
   809         function (m) { RecvLoadScriptTest(m.json.uri, m.json.timeout); }
   810     );
   811     addMessageListener(
   812         "reftest:LoadTest",
   813         function (m) { RecvLoadTest(m.json.type, m.json.uri, m.json.timeout); }
   814     );
   815     addMessageListener(
   816         "reftest:ResetRenderingState",
   817         function (m) { RecvResetRenderingState(); }
   818     );
   819 }
   821 function RecvClear()
   822 {
   823     gClearingForAssertionCheck = true;
   824     LoadURI(BLANK_URL_FOR_CLEARING);
   825 }
   827 function RecvLoadTest(type, uri, timeout)
   828 {
   829     StartTestURI(type, uri, timeout);
   830 }
   832 function RecvLoadScriptTest(uri, timeout)
   833 {
   834     StartTestURI(TYPE_SCRIPT, uri, timeout);
   835 }
   837 function RecvResetRenderingState()
   838 {
   839     resetZoom();
   840     resetDisplayportAndViewport();
   841 }
   843 function SendAssertionCount(numAssertions)
   844 {
   845     sendAsyncMessage("reftest:AssertionCount", { count: numAssertions });
   846 }
   848 function SendContentReady()
   849 {
   850     return sendSyncMessage("reftest:ContentReady")[0];
   851 }
   853 function SendException(what)
   854 {
   855     sendAsyncMessage("reftest:Exception", { what: what });
   856 }
   858 function SendFailedLoad(why)
   859 {
   860     sendAsyncMessage("reftest:FailedLoad", { why: why });
   861 }
   863 function SendFailedNoPaint()
   864 {
   865     sendAsyncMessage("reftest:FailedNoPaint");
   866 }
   868 function SendEnableAsyncScroll()
   869 {
   870     sendAsyncMessage("reftest:EnableAsyncScroll");
   871 }
   873 // Return true if a snapshot was taken.
   874 function SendInitCanvasWithSnapshot()
   875 {
   876     // If we're in the same process as the top-level XUL window, then
   877     // drawing that window will also update our layers, so no
   878     // synchronization is needed.
   879     //
   880     // NB: this is a test-harness optimization only, it must not
   881     // affect the validity of the tests.
   882     if (gBrowserIsRemote) {
   883         SynchronizeForSnapshot(SYNC_DEFAULT);
   884     }
   886     // For in-process browser, we have to make a synchronous request
   887     // here to make the above optimization valid, so that MozWaitPaint
   888     // events dispatched (synchronously) during painting are received
   889     // before we check the paint-wait counter.  For out-of-process
   890     // browser though, it doesn't wrt correctness whether this request
   891     // is sync or async.
   892     var ret = sendSyncMessage("reftest:InitCanvasWithSnapshot")[0];
   894     gHaveCanvasSnapshot = ret.painted;
   895     return ret.painted;
   896 }
   898 function SendScriptResults(runtimeMs, error, results)
   899 {
   900     sendAsyncMessage("reftest:ScriptResults",
   901                      { runtimeMs: runtimeMs, error: error, results: results });
   902 }
   904 function SendExpectProcessCrash(runtimeMs)
   905 {
   906     sendAsyncMessage("reftest:ExpectProcessCrash");
   907 }
   909 function SendTestDone(runtimeMs)
   910 {
   911     sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs });
   912 }
   914 function roundTo(x, fraction)
   915 {
   916     return Math.round(x/fraction)*fraction;
   917 }
   919 function SendUpdateCanvasForEvent(event, contentRootElement)
   920 {
   921     var win = content;
   922     var scale = markupDocumentViewer().fullZoom;
   924     var rects = [ ];
   925     if (shouldSnapshotWholePage(contentRootElement)) {
   926       // See comments in SendInitCanvasWithSnapshot() re: the split
   927       // logic here.
   928       if (!gBrowserIsRemote) {
   929           sendSyncMessage("reftest:UpdateWholeCanvasForInvalidation");
   930       } else {
   931           SynchronizeForSnapshot(SYNC_ALLOW_DISABLE);
   932           sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation");
   933       }
   934       return;
   935     }
   937     var rectList = event.clientRects;
   938     LogInfo("SendUpdateCanvasForEvent with " + rectList.length + " rects");
   939     for (var i = 0; i < rectList.length; ++i) {
   940         var r = rectList[i];
   941         // Set left/top/right/bottom to "device pixel" boundaries
   942         var left = Math.floor(roundTo(r.left*scale, 0.001));
   943         var top = Math.floor(roundTo(r.top*scale, 0.001));
   944         var right = Math.ceil(roundTo(r.right*scale, 0.001));
   945         var bottom = Math.ceil(roundTo(r.bottom*scale, 0.001));
   946         LogInfo("Rect: " + left + " " + top + " " + right + " " + bottom);
   948         rects.push({ left: left, top: top, right: right, bottom: bottom });
   949     }
   951     // See comments in SendInitCanvasWithSnapshot() re: the split
   952     // logic here.
   953     if (!gBrowserIsRemote) {
   954         sendSyncMessage("reftest:UpdateCanvasForInvalidation", { rects: rects });
   955     } else {
   956         SynchronizeForSnapshot(SYNC_ALLOW_DISABLE);
   957         sendAsyncMessage("reftest:UpdateCanvasForInvalidation", { rects: rects });
   958     }
   959 }
   960 #if REFTEST_B2G
   961 OnInitialLoad();
   962 #else
   963 addEventListener("load", OnInitialLoad, true);
   964 #endif

mercurial