Bug 1205027 - Only size <img> to broken-image size if it's actually broken. r=dholbert
☠☠ backed out by ea07d83adaf4 ☠ ☠
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Sun, 29 May 2016 07:51:49 +0900
changeset 338446 17dcafc58287fb1f605ea6c396589f467aeff811
parent 338445 de635a6b22cf9ab2688470310fc584a720138b74
child 338447 daf0dcb26912c67498d49e67d7bc3425c96870d5
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1205027
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1205027 - Only size <img> to broken-image size if it's actually broken. r=dholbert
layout/generic/nsImageFrame.cpp
layout/generic/test/file_SlowImage.sjs
layout/generic/test/mochitest.ini
layout/generic/test/test_intrinsic_size_on_loading.html
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -785,25 +785,40 @@ nsImageFrame::EnsureIntrinsicSizeAndRati
       mIntrinsicSize.height.GetCoordValue() == 0) {
 
     if (mImage) {
       UpdateIntrinsicSize(mImage);
       UpdateIntrinsicRatio(mImage);
     } else {
       // image request is null or image size not known, probably an
       // invalid image specified
-      // - make the image big enough for the icon (it may not be
-      // used if inline alt expansion is used instead)
       if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
-        nscoord edgeLengthToUse =
-          nsPresContext::CSSPixelsToAppUnits(
-            ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
-        mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
-        mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
-        mIntrinsicRatio.SizeTo(1, 1);
+        bool imageBroken = false;
+        // check for broken images. valid null images (eg. img src="") are
+        // not considered broken because they have no image requests
+        nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
+        if (imageLoader) {
+          nsCOMPtr<imgIRequest> currentRequest;
+          imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
+                                  getter_AddRefs(currentRequest));
+          uint32_t imageStatus;
+          imageBroken =
+            currentRequest &&
+            NS_SUCCEEDED(currentRequest->GetImageStatus(&imageStatus)) &&
+            (imageStatus & imgIRequest::STATUS_ERROR);
+        }
+        // invalid image specified. make the image big enough for the "broken" icon
+        if (imageBroken) {
+          nscoord edgeLengthToUse =
+            nsPresContext::CSSPixelsToAppUnits(
+              ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH)));
+          mIntrinsicSize.width.SetCoordValue(edgeLengthToUse);
+          mIntrinsicSize.height.SetCoordValue(edgeLengthToUse);
+          mIntrinsicRatio.SizeTo(1, 1);
+        }
       }
     }
   }
 }
 
 /* virtual */
 LogicalSize
 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext,
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/file_SlowImage.sjs
@@ -0,0 +1,45 @@
+"use strict";
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const IMG_BYTES = atob(
+  "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA" +
+  "DUlEQVQImWNgY2P7DwABOgESJhRQtgAAAABJRU5ErkJggg==");
+
+function handleRequest(request, response) {
+  response.processAsync();
+  getObjectState("context", function(obj) {
+    let ctx;
+    if (obj == null) {
+      ctx = {
+        QueryInterface: function(iid) {
+          if (iid.equals(Components.interfaces.nsISupports))
+            return this;
+          throw Components.results.NS_ERROR_NO_INTERFACE;
+        }
+      };
+      ctx.wrappedJSObject = ctx;
+
+      ctx.promise = new Promise(resolve => {
+        ctx.resolve = resolve;
+      });
+
+      setObjectState("context", ctx);
+    } else {
+      ctx = obj.wrappedJSObject;
+    }
+    Promise.resolve(ctx).then(next);
+  });
+
+  function next(ctx) {
+    if (request.queryString.indexOf("continue") >= 0) {
+      ctx.resolve();
+    }
+
+    ctx.promise.then(() => {
+      response.setHeader("Content-Type", "image/png");
+      response.write(IMG_BYTES);
+      response.finish();
+    });
+  }
+}
--- a/layout/generic/test/mochitest.ini
+++ b/layout/generic/test/mochitest.ini
@@ -9,16 +9,17 @@ support-files =
   plugin_clipping_helper_transformed.xhtml
   plugin_clipping_helper_table.xhtml
   plugin_clipping_lib.js
   plugin_focus_helper.html
   file_BrokenImageReference.png
   file_Dolske.png
   file_IconTestServer.sjs
   file_LoadingImageReference.png
+  file_SlowImage.sjs
   bug1174521.html
 
 [test_bug240933.html]
 [test_bug263683.html]
 [test_bug288789.html]
 [test_bug290397.html]
 [test_bug323656.html]
 [test_bug344830.html]
@@ -101,16 +102,17 @@ skip-if = buildapp == 'b2g' # b2g(Target
 [test_bug1062406.html]
 [test_bug1174521.html]
 [test_contained_plugin_transplant.html]
 skip-if = os=='win'
 [test_image_selection.html]
 [test_image_selection_2.html]
 [test_invalidate_during_plugin_paint.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(plugins not supported) b2g-debug(plugins not supported) b2g-desktop(plugins not supported)
+[test_intrinsic_size_on_loading.html]
 [test_movement_by_characters.html]
 [test_movement_by_words.html]
 # Disable the caret movement by word test on Linux because the shortcut keys
 # are defined in system level.  So, it depends on the environment.
 # Disable on Windows for too many intermittent failures (bug 916143).
 skip-if = (toolkit == "gtk2") || (toolkit == "gtk3") || (os == "win")
 [test_overflow_event.html]
 [test_page_scroll_with_fixed_pos.html]
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/test_intrinsic_size_on_loading.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1205027
+-->
+<head>
+  <title>Test for images intrinsic size while load is pending</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+    body { margin: 0; }
+    img { display: block; }
+  </style>
+  <script>
+    SimpleTest.waitForExplicitFinish();
+    var initialOffsetTop;
+    var finalOffsetTop;
+
+    function report() {
+      finalOffsetTop = marker.offsetTop;
+      is(initialOffsetTop, 0, "initial offsetTop: " + initialOffsetTop);
+      is(finalOffsetTop, 8, "final offsetTop: " + finalOffsetTop);
+      ok(initialOffsetTop < finalOffsetTop, "offsetTop should get larger.");
+      SimpleTest.finish();
+    }
+
+    function fail() {
+      ok(false, "image loading should not fail.");
+    }
+  </script>
+</head>
+<body onload="report();">
+  <!-- Bunch of tiny images: -->
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+  <img src="file_SlowImage.sjs" onerror="fail();">
+
+  <div id="marker">Marker div</div>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205027">Mozilla Bug 1205027</a>
+  <script>
+    initialOffsetTop = marker.offsetTop;
+    // prompt the sjs "server" to proceed to serve data to our <img> elements
+    var img = new Image();
+    img.onerror = fail;
+    img.src = "file_SlowImage.sjs?continue";
+  </script>
+</body>
+</html>