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(); |
} |
} |