| Index: third_party/WebKit/Source/core/html/ImageDocument.cpp | 
| diff --git a/third_party/WebKit/Source/core/html/ImageDocument.cpp b/third_party/WebKit/Source/core/html/ImageDocument.cpp | 
| index a3d63fca4c7b905591e9d751fec82c526f22ccf4..88826f4d3894c9781ee2ab8eba50a45a760c2b64 100644 | 
| --- a/third_party/WebKit/Source/core/html/ImageDocument.cpp | 
| +++ b/third_party/WebKit/Source/core/html/ImageDocument.cpp | 
| @@ -54,6 +54,14 @@ | 
|  | 
| using namespace std; | 
|  | 
| +namespace { | 
| + | 
| +// The base square size is set to 10 because it rounds nicely for both the | 
| +// minimum scale (0.1) and maximum scale (5.0). | 
| +const int kBaseCheckerSize = 10; | 
| + | 
| +}  // namespace | 
| + | 
| namespace blink { | 
|  | 
| using namespace HTMLNames; | 
| @@ -123,6 +131,13 @@ static String imageTitle(const String& filename, const IntSize& size) { | 
| return result.toString(); | 
| } | 
|  | 
| +static LayoutSize cachedImageSize(HTMLImageElement* element) { | 
| +  DCHECK(element->cachedImage()); | 
| +  return element->cachedImage()->imageSize( | 
| +      LayoutObject::shouldRespectImageOrientation(element->layoutObject()), | 
| +      1.0f); | 
| +} | 
| + | 
| void ImageDocumentParser::appendBytes(const char* data, size_t length) { | 
| if (!length) | 
| return; | 
| @@ -155,10 +170,7 @@ void ImageDocumentParser::finish() { | 
| // Report the natural image size in the page title, regardless of zoom | 
| // level.  At a zoom level of 1 the image is guaranteed to have an integer | 
| // size. | 
| -    IntSize size = flooredIntSize( | 
| -        cachedImage->imageSize(LayoutObject::shouldRespectImageOrientation( | 
| -                                   document()->imageElement()->layoutObject()), | 
| -                               1.0f)); | 
| +    IntSize size = flooredIntSize(cachedImageSize(document()->imageElement())); | 
| if (size.width()) { | 
| // Compute the title, we use the decoded filename of the resource, falling | 
| // back on the (decoded) hostname if there is no path. | 
| @@ -172,6 +184,7 @@ void ImageDocumentParser::finish() { | 
| } | 
|  | 
| document()->imageUpdated(); | 
| +    document()->imageLoaded(); | 
| } | 
|  | 
| if (!isDetached()) | 
| @@ -187,6 +200,8 @@ ImageDocument::ImageDocument(const DocumentInit& initializer) | 
| m_imageSizeIsKnown(false), | 
| m_didShrinkImage(false), | 
| m_shouldShrinkImage(shouldShrinkToFit()), | 
| +      m_imageIsLoaded(false), | 
| +      m_checkerSize(0), | 
| m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport | 
| : Desktop) { | 
| setCompatibilityMode(QuirksMode); | 
| @@ -218,9 +233,9 @@ void ImageDocument::createDocumentStructure() { | 
|  | 
| if (shouldShrinkToFit()) { | 
| // Display the image prominently centered in the frame. | 
| -    body->setAttribute(styleAttr, "margin: 0px;"); | 
| +    body->setAttribute(styleAttr, "margin: 0px; background: #0e0e0e;"); | 
|  | 
| -    // See w3c example on how to centering an element: | 
| +    // See w3c example on how to center an element: | 
| // https://www.w3.org/Style/Examples/007/center.en.html | 
| m_divElement = HTMLDivElement::create(*this); | 
| m_divElement->setAttribute(styleAttr, | 
| @@ -243,12 +258,8 @@ void ImageDocument::createDocumentStructure() { | 
|  | 
| willInsertBody(); | 
|  | 
| -  StringBuilder imageStyle; | 
| -  imageStyle.append("-webkit-user-select: none;"); | 
| -  if (shouldShrinkToFit() && m_shrinkToFitMode == Viewport) | 
| -    imageStyle.append("max-width: 100%"); | 
| m_imageElement = HTMLImageElement::create(*this); | 
| -  m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); | 
| +  updateImageStyle(); | 
| m_imageElement->setLoadingImageDocument(); | 
| m_imageElement->setSrc(url().getString()); | 
| body->appendChild(m_imageElement.get()); | 
| @@ -260,9 +271,16 @@ void ImageDocument::createDocumentStructure() { | 
| // Add event listeners | 
| EventListener* listener = ImageEventListener::create(this); | 
| if (LocalDOMWindow* domWindow = this->domWindow()) | 
| -      domWindow->addEventListener("resize", listener, false); | 
| -    if (m_shrinkToFitMode == Desktop) | 
| -      m_imageElement->addEventListener("click", listener, false); | 
| +      domWindow->addEventListener(EventTypeNames::resize, listener, false); | 
| + | 
| +    if (m_shrinkToFitMode == Desktop) { | 
| +      m_imageElement->addEventListener(EventTypeNames::click, listener, false); | 
| +    } else if (m_shrinkToFitMode == Viewport) { | 
| +      m_imageElement->addEventListener(EventTypeNames::touchend, listener, | 
| +                                       false); | 
| +      m_imageElement->addEventListener(EventTypeNames::touchcancel, listener, | 
| +                                       false); | 
| +    } | 
| } | 
|  | 
| rootElement->appendChild(head); | 
| @@ -301,11 +319,7 @@ void ImageDocument::resizeImageToFit() { | 
| if (!m_imageElement || m_imageElement->document() != this) | 
| return; | 
|  | 
| -  DCHECK(m_imageElement->cachedImage()); | 
| -  LayoutSize imageSize = m_imageElement->cachedImage()->imageSize( | 
| -      LayoutObject::shouldRespectImageOrientation( | 
| -          m_imageElement->layoutObject()), | 
| -      1.f); | 
| +  LayoutSize imageSize = cachedImageSize(m_imageElement); | 
|  | 
| const float scale = this->scale(); | 
| m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); | 
| @@ -346,6 +360,77 @@ void ImageDocument::imageClicked(int x, int y) { | 
| } | 
| } | 
|  | 
| +void ImageDocument::imageLoaded() { | 
| +  m_imageIsLoaded = true; | 
| + | 
| +  if (shouldShrinkToFit()) { | 
| +    // The checkerboard background needs to be inserted. | 
| +    updateImageStyle(); | 
| +  } | 
| +} | 
| + | 
| +void ImageDocument::updateImageStyle() { | 
| +  StringBuilder imageStyle; | 
| +  imageStyle.append("-webkit-user-select: none;"); | 
| + | 
| +  if (shouldShrinkToFit()) { | 
| +    if (m_shrinkToFitMode == Viewport) | 
| +      imageStyle.append("max-width: 100%;"); | 
| + | 
| +    // Once the image has fully loaded, it is displayed atop a checkerboard to | 
| +    // show transparency more faithfully.  The pattern is generated via CSS. | 
| +    if (m_imageIsLoaded) { | 
| +      int newCheckerSize = kBaseCheckerSize; | 
| + | 
| +      if (m_shrinkToFitMode == Viewport) { | 
| +        double scale; | 
| + | 
| +        if (hasFinishedParsing()) { | 
| +          // To ensure the checker pattern is visible for large images, the | 
| +          // checker size is dynamically adjusted to account for how much the | 
| +          // page is currently being scaled. | 
| +          scale = frame()->host()->visualViewport().scale(); | 
| +        } else { | 
| +          // The checker pattern is initialized based on how large the image is | 
| +          // relative to the viewport. | 
| +          int viewportWidth = frame()->host()->visualViewport().size().width(); | 
| +          scale = viewportWidth / static_cast<double>(calculateDivWidth()); | 
| +        } | 
| + | 
| +        newCheckerSize = round(std::max(1.0, newCheckerSize / scale)); | 
| +      } | 
| + | 
| +      // The only thing that can differ between updates is the checker size. | 
| +      if (newCheckerSize == m_checkerSize) | 
| +        return; | 
| +      m_checkerSize = newCheckerSize; | 
| + | 
| +      imageStyle.append("background-position: 0px 0px, "); | 
| +      imageStyle.append(AtomicString::number(m_checkerSize)); | 
| +      imageStyle.append("px "); | 
| +      imageStyle.append(AtomicString::number(m_checkerSize)); | 
| +      imageStyle.append("px;"); | 
| + | 
| +      int tileSize = m_checkerSize * 2; | 
| +      imageStyle.append("background-size: "); | 
| +      imageStyle.append(AtomicString::number(tileSize)); | 
| +      imageStyle.append("px "); | 
| +      imageStyle.append(AtomicString::number(tileSize)); | 
| +      imageStyle.append("px;"); | 
| + | 
| +      imageStyle.append( | 
| +          "background-color: white;" | 
| +          "background-image:" | 
| +          "linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, " | 
| +          "#eee 75%, #eee 100%)," | 
| +          "linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, " | 
| +          "#eee 75%, #eee 100%);"); | 
| +    } | 
| +  } | 
| + | 
| +  m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); | 
| +} | 
| + | 
| void ImageDocument::imageUpdated() { | 
| DCHECK(m_imageElement); | 
|  | 
| @@ -377,10 +462,7 @@ void ImageDocument::restoreImageSize() { | 
| return; | 
|  | 
| DCHECK(m_imageElement->cachedImage()); | 
| -  LayoutSize imageSize = m_imageElement->cachedImage()->imageSize( | 
| -      LayoutObject::shouldRespectImageOrientation( | 
| -          m_imageElement->layoutObject()), | 
| -      1.0f); | 
| +  LayoutSize imageSize = cachedImageSize(m_imageElement); | 
| m_imageElement->setWidth(imageSize.width().toInt()); | 
| m_imageElement->setHeight(imageSize.height().toInt()); | 
|  | 
| @@ -397,40 +479,42 @@ bool ImageDocument::imageFitsInWindow() const { | 
| return this->scale() >= 1; | 
| } | 
|  | 
| +int ImageDocument::calculateDivWidth() { | 
| +  // Zooming in and out of an image being displayed within a viewport is done | 
| +  // by changing the page scale factor of the page instead of changing the | 
| +  // size of the image.  The size of the image is set so that: | 
| +  // * Images wider than the viewport take the full width of the screen. | 
| +  // * Images taller than the viewport are initially aligned with the top of | 
| +  //   of the frame. | 
| +  // * Images smaller in either dimension are centered along that axis. | 
| +  LayoutSize imageSize = cachedImageSize(m_imageElement); | 
| +  int viewportWidth = frame()->host()->visualViewport().size().width(); | 
| + | 
| +  // For huge images, minimum-scale=0.1 is still too big on small screens. | 
| +  // Set the <div> width so that the image will shrink to fit the width of the | 
| +  // screen when the scale is minimum. | 
| +  int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); | 
| +  return std::max(viewportWidth, maxWidth); | 
| +} | 
| + | 
| void ImageDocument::windowSizeChanged() { | 
| if (!m_imageElement || !m_imageSizeIsKnown || | 
| m_imageElement->document() != this) | 
| return; | 
|  | 
| if (m_shrinkToFitMode == Viewport) { | 
| -    // Zooming in and out of an image being displayed within a viewport is done | 
| -    // by changing the page scale factor of the page instead of changing the | 
| -    // size of the image.  The size of the image is set so that: | 
| -    // * Images wider than the viewport take the full width of the screen. | 
| -    // * Images taller than the viewport are initially aligned with the top of | 
| -    //   of the frame. | 
| -    // * Images smaller in either dimension are centered along that axis. | 
| -    LayoutSize imageSize = m_imageElement->cachedImage()->imageSize( | 
| -        LayoutObject::shouldRespectImageOrientation( | 
| -            m_imageElement->layoutObject()), | 
| -        1.f); | 
| -    int viewportWidth = frame()->host()->visualViewport().size().width(); | 
| -    int viewportHeight = frame()->host()->visualViewport().size().height(); | 
| -    float viewportAspectRatio = (float)viewportWidth / viewportHeight; | 
| - | 
| -    // For huge images, minimum-scale=0.1 is still too big on small screens. | 
| -    // Set the <div> width so that the image will shrink to fit the width of the | 
| -    // screen when the scale is minimum. | 
| -    int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); | 
| -    int divWidth = std::max(viewportWidth, maxWidth); | 
| +    LayoutSize imageSize = cachedImageSize(m_imageElement); | 
| +    int divWidth = calculateDivWidth(); | 
| m_divElement->setInlineStyleProperty(CSSPropertyWidth, divWidth, | 
| CSSPrimitiveValue::UnitType::Pixels); | 
|  | 
| // Explicitly set the height of the <div> containing the <img> so that it | 
| // can display the full image without shrinking it, allowing a full-width | 
| // reading mode for normal-width-huge-height images. | 
| +    float viewportAspectRatio = | 
| +        frame()->host()->visualViewport().size().aspectRatio(); | 
| int divHeight = std::max(imageSize.height().toInt(), | 
| -                             (int)(divWidth / viewportAspectRatio)); | 
| +                             static_cast<int>(divWidth / viewportAspectRatio)); | 
| m_divElement->setInlineStyleProperty(CSSPropertyHeight, divHeight, | 
| CSSPrimitiveValue::UnitType::Pixels); | 
| return; | 
| @@ -495,6 +579,10 @@ void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { | 
| } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { | 
| MouseEvent* mouseEvent = toMouseEvent(event); | 
| m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); | 
| +  } else if ((event->type() == EventTypeNames::touchend || | 
| +              event->type() == EventTypeNames::touchcancel) && | 
| +             event->isTouchEvent()) { | 
| +    m_doc->updateImageStyle(); | 
| } | 
| } | 
|  | 
|  |