| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 47 #include "core/layout/LayoutObject.h" | 47 #include "core/layout/LayoutObject.h" |
| 48 #include "core/loader/DocumentLoader.h" | 48 #include "core/loader/DocumentLoader.h" |
| 49 #include "core/loader/FrameLoader.h" | 49 #include "core/loader/FrameLoader.h" |
| 50 #include "core/loader/FrameLoaderClient.h" | 50 #include "core/loader/FrameLoaderClient.h" |
| 51 #include "platform/HostWindow.h" | 51 #include "platform/HostWindow.h" |
| 52 #include "wtf/text/StringBuilder.h" | 52 #include "wtf/text/StringBuilder.h" |
| 53 #include <limits> | 53 #include <limits> |
| 54 | 54 |
| 55 using namespace std; | 55 using namespace std; |
| 56 | 56 |
| 57 namespace { |
| 58 |
| 59 // The base square size is set to 10 because it rounds nicely for both the |
| 60 // minimum scale (0.1) and maximum scale (5.0). |
| 61 const int kBaseCheckerSize = 10; |
| 62 |
| 63 } // namespace |
| 64 |
| 57 namespace blink { | 65 namespace blink { |
| 58 | 66 |
| 59 using namespace HTMLNames; | 67 using namespace HTMLNames; |
| 60 | 68 |
| 61 class ImageEventListener : public EventListener { | 69 class ImageEventListener : public EventListener { |
| 62 public: | 70 public: |
| 63 static ImageEventListener* create(ImageDocument* document) { | 71 static ImageEventListener* create(ImageDocument* document) { |
| 64 return new ImageEventListener(document); | 72 return new ImageEventListener(document); |
| 65 } | 73 } |
| 66 static const ImageEventListener* cast(const EventListener* listener) { | 74 static const ImageEventListener* cast(const EventListener* listener) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 result.append(" ("); | 124 result.append(" ("); |
| 117 // FIXME: Localize numbers. Safari/OSX shows localized numbers with group | 125 // FIXME: Localize numbers. Safari/OSX shows localized numbers with group |
| 118 // separaters. For example, "1,920x1,080". | 126 // separaters. For example, "1,920x1,080". |
| 119 result.appendNumber(size.width()); | 127 result.appendNumber(size.width()); |
| 120 result.append(static_cast<UChar>(0xD7)); // U+00D7 (multiplication sign) | 128 result.append(static_cast<UChar>(0xD7)); // U+00D7 (multiplication sign) |
| 121 result.appendNumber(size.height()); | 129 result.appendNumber(size.height()); |
| 122 result.append(')'); | 130 result.append(')'); |
| 123 return result.toString(); | 131 return result.toString(); |
| 124 } | 132 } |
| 125 | 133 |
| 134 static LayoutSize cachedImageSize(HTMLImageElement* element) { |
| 135 DCHECK(element->cachedImage()); |
| 136 return element->cachedImage()->imageSize( |
| 137 LayoutObject::shouldRespectImageOrientation(element->layoutObject()), |
| 138 1.0f); |
| 139 } |
| 140 |
| 126 void ImageDocumentParser::appendBytes(const char* data, size_t length) { | 141 void ImageDocumentParser::appendBytes(const char* data, size_t length) { |
| 127 if (!length) | 142 if (!length) |
| 128 return; | 143 return; |
| 129 | 144 |
| 130 LocalFrame* frame = document()->frame(); | 145 LocalFrame* frame = document()->frame(); |
| 131 Settings* settings = frame->settings(); | 146 Settings* settings = frame->settings(); |
| 132 if (!frame->loader().client()->allowImage( | 147 if (!frame->loader().client()->allowImage( |
| 133 !settings || settings->imagesEnabled(), document()->url())) | 148 !settings || settings->imagesEnabled(), document()->url())) |
| 134 return; | 149 return; |
| 135 | 150 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 148 void ImageDocumentParser::finish() { | 163 void ImageDocumentParser::finish() { |
| 149 if (!isStopped() && document()->imageElement() && document()->cachedImage()) { | 164 if (!isStopped() && document()->imageElement() && document()->cachedImage()) { |
| 150 ImageResource* cachedImage = document()->cachedImage(); | 165 ImageResource* cachedImage = document()->cachedImage(); |
| 151 DocumentLoader* loader = document()->loader(); | 166 DocumentLoader* loader = document()->loader(); |
| 152 cachedImage->setResponse(loader->response()); | 167 cachedImage->setResponse(loader->response()); |
| 153 cachedImage->finish(loader->timing().responseEnd()); | 168 cachedImage->finish(loader->timing().responseEnd()); |
| 154 | 169 |
| 155 // Report the natural image size in the page title, regardless of zoom | 170 // Report the natural image size in the page title, regardless of zoom |
| 156 // level. At a zoom level of 1 the image is guaranteed to have an integer | 171 // level. At a zoom level of 1 the image is guaranteed to have an integer |
| 157 // size. | 172 // size. |
| 158 IntSize size = flooredIntSize( | 173 IntSize size = flooredIntSize(cachedImageSize(document()->imageElement())); |
| 159 cachedImage->imageSize(LayoutObject::shouldRespectImageOrientation( | |
| 160 document()->imageElement()->layoutObject()), | |
| 161 1.0f)); | |
| 162 if (size.width()) { | 174 if (size.width()) { |
| 163 // Compute the title, we use the decoded filename of the resource, falling | 175 // Compute the title, we use the decoded filename of the resource, falling |
| 164 // back on the (decoded) hostname if there is no path. | 176 // back on the (decoded) hostname if there is no path. |
| 165 String fileName = | 177 String fileName = |
| 166 decodeURLEscapeSequences(document()->url().lastPathComponent()); | 178 decodeURLEscapeSequences(document()->url().lastPathComponent()); |
| 167 if (fileName.isEmpty()) | 179 if (fileName.isEmpty()) |
| 168 fileName = document()->url().host(); | 180 fileName = document()->url().host(); |
| 169 document()->setTitle(imageTitle(fileName, size)); | 181 document()->setTitle(imageTitle(fileName, size)); |
| 170 if (isDetached()) | 182 if (isDetached()) |
| 171 return; | 183 return; |
| 172 } | 184 } |
| 173 | 185 |
| 174 document()->imageUpdated(); | 186 document()->imageUpdated(); |
| 187 document()->imageLoaded(); |
| 175 } | 188 } |
| 176 | 189 |
| 177 if (!isDetached()) | 190 if (!isDetached()) |
| 178 document()->finishedParsing(); | 191 document()->finishedParsing(); |
| 179 } | 192 } |
| 180 | 193 |
| 181 // -------- | 194 // -------- |
| 182 | 195 |
| 183 ImageDocument::ImageDocument(const DocumentInit& initializer) | 196 ImageDocument::ImageDocument(const DocumentInit& initializer) |
| 184 : HTMLDocument(initializer, ImageDocumentClass), | 197 : HTMLDocument(initializer, ImageDocumentClass), |
| 185 m_divElement(nullptr), | 198 m_divElement(nullptr), |
| 186 m_imageElement(nullptr), | 199 m_imageElement(nullptr), |
| 187 m_imageSizeIsKnown(false), | 200 m_imageSizeIsKnown(false), |
| 188 m_didShrinkImage(false), | 201 m_didShrinkImage(false), |
| 189 m_shouldShrinkImage(shouldShrinkToFit()), | 202 m_shouldShrinkImage(shouldShrinkToFit()), |
| 203 m_imageIsLoaded(false), |
| 204 m_checkerSize(0), |
| 190 m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport | 205 m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport |
| 191 : Desktop) { | 206 : Desktop) { |
| 192 setCompatibilityMode(QuirksMode); | 207 setCompatibilityMode(QuirksMode); |
| 193 lockCompatibilityMode(); | 208 lockCompatibilityMode(); |
| 194 UseCounter::count(*this, UseCounter::ImageDocument); | 209 UseCounter::count(*this, UseCounter::ImageDocument); |
| 195 if (!isInMainFrame()) | 210 if (!isInMainFrame()) |
| 196 UseCounter::count(*this, UseCounter::ImageDocumentInFrame); | 211 UseCounter::count(*this, UseCounter::ImageDocumentInFrame); |
| 197 } | 212 } |
| 198 | 213 |
| 199 DocumentParser* ImageDocument::createParser() { | 214 DocumentParser* ImageDocument::createParser() { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 211 HTMLHeadElement* head = HTMLHeadElement::create(*this); | 226 HTMLHeadElement* head = HTMLHeadElement::create(*this); |
| 212 HTMLMetaElement* meta = HTMLMetaElement::create(*this); | 227 HTMLMetaElement* meta = HTMLMetaElement::create(*this); |
| 213 meta->setAttribute(nameAttr, "viewport"); | 228 meta->setAttribute(nameAttr, "viewport"); |
| 214 meta->setAttribute(contentAttr, "width=device-width, minimum-scale=0.1"); | 229 meta->setAttribute(contentAttr, "width=device-width, minimum-scale=0.1"); |
| 215 head->appendChild(meta); | 230 head->appendChild(meta); |
| 216 | 231 |
| 217 HTMLBodyElement* body = HTMLBodyElement::create(*this); | 232 HTMLBodyElement* body = HTMLBodyElement::create(*this); |
| 218 | 233 |
| 219 if (shouldShrinkToFit()) { | 234 if (shouldShrinkToFit()) { |
| 220 // Display the image prominently centered in the frame. | 235 // Display the image prominently centered in the frame. |
| 221 body->setAttribute(styleAttr, "margin: 0px;"); | 236 body->setAttribute(styleAttr, "margin: 0px; background: #0e0e0e;"); |
| 222 | 237 |
| 223 // See w3c example on how to centering an element: | 238 // See w3c example on how to center an element: |
| 224 // https://www.w3.org/Style/Examples/007/center.en.html | 239 // https://www.w3.org/Style/Examples/007/center.en.html |
| 225 m_divElement = HTMLDivElement::create(*this); | 240 m_divElement = HTMLDivElement::create(*this); |
| 226 m_divElement->setAttribute(styleAttr, | 241 m_divElement->setAttribute(styleAttr, |
| 227 "display: flex;" | 242 "display: flex;" |
| 228 "flex-direction: column;" | 243 "flex-direction: column;" |
| 229 "justify-content: center;" | 244 "justify-content: center;" |
| 230 "align-items: center;" | 245 "align-items: center;" |
| 231 "min-height: min-content;" | 246 "min-height: min-content;" |
| 232 "min-width: min-content;" | 247 "min-width: min-content;" |
| 233 "height: 100%;" | 248 "height: 100%;" |
| 234 "width: 100%;"); | 249 "width: 100%;"); |
| 235 HTMLContentElement* content = HTMLContentElement::create(*this); | 250 HTMLContentElement* content = HTMLContentElement::create(*this); |
| 236 m_divElement->appendChild(content); | 251 m_divElement->appendChild(content); |
| 237 | 252 |
| 238 ShadowRoot& shadowRoot = body->ensureUserAgentShadowRoot(); | 253 ShadowRoot& shadowRoot = body->ensureUserAgentShadowRoot(); |
| 239 shadowRoot.appendChild(m_divElement); | 254 shadowRoot.appendChild(m_divElement); |
| 240 } else { | 255 } else { |
| 241 body->setAttribute(styleAttr, "margin: 0px;"); | 256 body->setAttribute(styleAttr, "margin: 0px;"); |
| 242 } | 257 } |
| 243 | 258 |
| 244 willInsertBody(); | 259 willInsertBody(); |
| 245 | 260 |
| 246 StringBuilder imageStyle; | |
| 247 imageStyle.append("-webkit-user-select: none;"); | |
| 248 if (shouldShrinkToFit() && m_shrinkToFitMode == Viewport) | |
| 249 imageStyle.append("max-width: 100%"); | |
| 250 m_imageElement = HTMLImageElement::create(*this); | 261 m_imageElement = HTMLImageElement::create(*this); |
| 251 m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); | 262 updateImageStyle(); |
| 252 m_imageElement->setLoadingImageDocument(); | 263 m_imageElement->setLoadingImageDocument(); |
| 253 m_imageElement->setSrc(url().getString()); | 264 m_imageElement->setSrc(url().getString()); |
| 254 body->appendChild(m_imageElement.get()); | 265 body->appendChild(m_imageElement.get()); |
| 255 if (loader() && m_imageElement->cachedImage()) | 266 if (loader() && m_imageElement->cachedImage()) |
| 256 m_imageElement->cachedImage()->responseReceived(loader()->response(), | 267 m_imageElement->cachedImage()->responseReceived(loader()->response(), |
| 257 nullptr); | 268 nullptr); |
| 258 | 269 |
| 259 if (shouldShrinkToFit()) { | 270 if (shouldShrinkToFit()) { |
| 260 // Add event listeners | 271 // Add event listeners |
| 261 EventListener* listener = ImageEventListener::create(this); | 272 EventListener* listener = ImageEventListener::create(this); |
| 262 if (LocalDOMWindow* domWindow = this->domWindow()) | 273 if (LocalDOMWindow* domWindow = this->domWindow()) |
| 263 domWindow->addEventListener("resize", listener, false); | 274 domWindow->addEventListener(EventTypeNames::resize, listener, false); |
| 264 if (m_shrinkToFitMode == Desktop) | 275 |
| 265 m_imageElement->addEventListener("click", listener, false); | 276 if (m_shrinkToFitMode == Desktop) { |
| 277 m_imageElement->addEventListener(EventTypeNames::click, listener, false); |
| 278 } else if (m_shrinkToFitMode == Viewport) { |
| 279 m_imageElement->addEventListener(EventTypeNames::touchend, listener, |
| 280 false); |
| 281 m_imageElement->addEventListener(EventTypeNames::touchcancel, listener, |
| 282 false); |
| 283 } |
| 266 } | 284 } |
| 267 | 285 |
| 268 rootElement->appendChild(head); | 286 rootElement->appendChild(head); |
| 269 rootElement->appendChild(body); | 287 rootElement->appendChild(body); |
| 270 } | 288 } |
| 271 | 289 |
| 272 float ImageDocument::scale() const { | 290 float ImageDocument::scale() const { |
| 273 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 291 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
| 274 if (!m_imageElement || m_imageElement->document() != this) | 292 if (!m_imageElement || m_imageElement->document() != this) |
| 275 return 1.0f; | 293 return 1.0f; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 294 view->height() * manualZoom / imageSize.height().toFloat(); | 312 view->height() * manualZoom / imageSize.height().toFloat(); |
| 295 | 313 |
| 296 return min(widthScale, heightScale); | 314 return min(widthScale, heightScale); |
| 297 } | 315 } |
| 298 | 316 |
| 299 void ImageDocument::resizeImageToFit() { | 317 void ImageDocument::resizeImageToFit() { |
| 300 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 318 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
| 301 if (!m_imageElement || m_imageElement->document() != this) | 319 if (!m_imageElement || m_imageElement->document() != this) |
| 302 return; | 320 return; |
| 303 | 321 |
| 304 DCHECK(m_imageElement->cachedImage()); | 322 LayoutSize imageSize = cachedImageSize(m_imageElement); |
| 305 LayoutSize imageSize = m_imageElement->cachedImage()->imageSize( | |
| 306 LayoutObject::shouldRespectImageOrientation( | |
| 307 m_imageElement->layoutObject()), | |
| 308 1.f); | |
| 309 | 323 |
| 310 const float scale = this->scale(); | 324 const float scale = this->scale(); |
| 311 m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); | 325 m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); |
| 312 m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale)); | 326 m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale)); |
| 313 | 327 |
| 314 m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomIn); | 328 m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomIn); |
| 315 } | 329 } |
| 316 | 330 |
| 317 void ImageDocument::imageClicked(int x, int y) { | 331 void ImageDocument::imageClicked(int x, int y) { |
| 318 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 332 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 339 float scrollX = | 353 float scrollX = |
| 340 imageX / scale - static_cast<float>(frame()->view()->width()) / 2; | 354 imageX / scale - static_cast<float>(frame()->view()->width()) / 2; |
| 341 float scrollY = | 355 float scrollY = |
| 342 imageY / scale - static_cast<float>(frame()->view()->height()) / 2; | 356 imageY / scale - static_cast<float>(frame()->view()->height()) / 2; |
| 343 | 357 |
| 344 frame()->view()->layoutViewportScrollableArea()->setScrollOffset( | 358 frame()->view()->layoutViewportScrollableArea()->setScrollOffset( |
| 345 ScrollOffset(scrollX, scrollY), ProgrammaticScroll); | 359 ScrollOffset(scrollX, scrollY), ProgrammaticScroll); |
| 346 } | 360 } |
| 347 } | 361 } |
| 348 | 362 |
| 363 void ImageDocument::imageLoaded() { |
| 364 m_imageIsLoaded = true; |
| 365 |
| 366 if (shouldShrinkToFit()) { |
| 367 // The checkerboard background needs to be inserted. |
| 368 updateImageStyle(); |
| 369 } |
| 370 } |
| 371 |
| 372 void ImageDocument::updateImageStyle() { |
| 373 StringBuilder imageStyle; |
| 374 imageStyle.append("-webkit-user-select: none;"); |
| 375 |
| 376 if (shouldShrinkToFit()) { |
| 377 if (m_shrinkToFitMode == Viewport) |
| 378 imageStyle.append("max-width: 100%;"); |
| 379 |
| 380 // Once the image has fully loaded, it is displayed atop a checkerboard to |
| 381 // show transparency more faithfully. The pattern is generated via CSS. |
| 382 if (m_imageIsLoaded) { |
| 383 int newCheckerSize = kBaseCheckerSize; |
| 384 |
| 385 if (m_shrinkToFitMode == Viewport) { |
| 386 double scale; |
| 387 |
| 388 if (hasFinishedParsing()) { |
| 389 // To ensure the checker pattern is visible for large images, the |
| 390 // checker size is dynamically adjusted to account for how much the |
| 391 // page is currently being scaled. |
| 392 scale = frame()->host()->visualViewport().scale(); |
| 393 } else { |
| 394 // The checker pattern is initialized based on how large the image is |
| 395 // relative to the viewport. |
| 396 int viewportWidth = frame()->host()->visualViewport().size().width(); |
| 397 scale = viewportWidth / static_cast<double>(calculateDivWidth()); |
| 398 } |
| 399 |
| 400 newCheckerSize = round(std::max(1.0, newCheckerSize / scale)); |
| 401 } |
| 402 |
| 403 // The only thing that can differ between updates is the checker size. |
| 404 if (newCheckerSize == m_checkerSize) |
| 405 return; |
| 406 m_checkerSize = newCheckerSize; |
| 407 |
| 408 imageStyle.append("background-position: 0px 0px, "); |
| 409 imageStyle.append(AtomicString::number(m_checkerSize)); |
| 410 imageStyle.append("px "); |
| 411 imageStyle.append(AtomicString::number(m_checkerSize)); |
| 412 imageStyle.append("px;"); |
| 413 |
| 414 int tileSize = m_checkerSize * 2; |
| 415 imageStyle.append("background-size: "); |
| 416 imageStyle.append(AtomicString::number(tileSize)); |
| 417 imageStyle.append("px "); |
| 418 imageStyle.append(AtomicString::number(tileSize)); |
| 419 imageStyle.append("px;"); |
| 420 |
| 421 imageStyle.append( |
| 422 "background-color: white;" |
| 423 "background-image:" |
| 424 "linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, " |
| 425 "#eee 75%, #eee 100%)," |
| 426 "linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, " |
| 427 "#eee 75%, #eee 100%);"); |
| 428 } |
| 429 } |
| 430 |
| 431 m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); |
| 432 } |
| 433 |
| 349 void ImageDocument::imageUpdated() { | 434 void ImageDocument::imageUpdated() { |
| 350 DCHECK(m_imageElement); | 435 DCHECK(m_imageElement); |
| 351 | 436 |
| 352 if (m_imageSizeIsKnown) | 437 if (m_imageSizeIsKnown) |
| 353 return; | 438 return; |
| 354 | 439 |
| 355 updateStyleAndLayoutTree(); | 440 updateStyleAndLayoutTree(); |
| 356 if (!m_imageElement->cachedImage() || | 441 if (!m_imageElement->cachedImage() || |
| 357 m_imageElement->cachedImage() | 442 m_imageElement->cachedImage() |
| 358 ->imageSize(LayoutObject::shouldRespectImageOrientation( | 443 ->imageSize(LayoutObject::shouldRespectImageOrientation( |
| (...skipping 11 matching lines...) Expand all Loading... |
| 370 } | 455 } |
| 371 | 456 |
| 372 void ImageDocument::restoreImageSize() { | 457 void ImageDocument::restoreImageSize() { |
| 373 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 458 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
| 374 | 459 |
| 375 if (!m_imageElement || !m_imageSizeIsKnown || | 460 if (!m_imageElement || !m_imageSizeIsKnown || |
| 376 m_imageElement->document() != this) | 461 m_imageElement->document() != this) |
| 377 return; | 462 return; |
| 378 | 463 |
| 379 DCHECK(m_imageElement->cachedImage()); | 464 DCHECK(m_imageElement->cachedImage()); |
| 380 LayoutSize imageSize = m_imageElement->cachedImage()->imageSize( | 465 LayoutSize imageSize = cachedImageSize(m_imageElement); |
| 381 LayoutObject::shouldRespectImageOrientation( | |
| 382 m_imageElement->layoutObject()), | |
| 383 1.0f); | |
| 384 m_imageElement->setWidth(imageSize.width().toInt()); | 466 m_imageElement->setWidth(imageSize.width().toInt()); |
| 385 m_imageElement->setHeight(imageSize.height().toInt()); | 467 m_imageElement->setHeight(imageSize.height().toInt()); |
| 386 | 468 |
| 387 if (imageFitsInWindow()) | 469 if (imageFitsInWindow()) |
| 388 m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); | 470 m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); |
| 389 else | 471 else |
| 390 m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomOut); | 472 m_imageElement->setInlineStyleProperty(CSSPropertyCursor, CSSValueZoomOut); |
| 391 | 473 |
| 392 m_didShrinkImage = false; | 474 m_didShrinkImage = false; |
| 393 } | 475 } |
| 394 | 476 |
| 395 bool ImageDocument::imageFitsInWindow() const { | 477 bool ImageDocument::imageFitsInWindow() const { |
| 396 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 478 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
| 397 return this->scale() >= 1; | 479 return this->scale() >= 1; |
| 398 } | 480 } |
| 399 | 481 |
| 482 int ImageDocument::calculateDivWidth() { |
| 483 // Zooming in and out of an image being displayed within a viewport is done |
| 484 // by changing the page scale factor of the page instead of changing the |
| 485 // size of the image. The size of the image is set so that: |
| 486 // * Images wider than the viewport take the full width of the screen. |
| 487 // * Images taller than the viewport are initially aligned with the top of |
| 488 // of the frame. |
| 489 // * Images smaller in either dimension are centered along that axis. |
| 490 LayoutSize imageSize = cachedImageSize(m_imageElement); |
| 491 int viewportWidth = frame()->host()->visualViewport().size().width(); |
| 492 |
| 493 // For huge images, minimum-scale=0.1 is still too big on small screens. |
| 494 // Set the <div> width so that the image will shrink to fit the width of the |
| 495 // screen when the scale is minimum. |
| 496 int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); |
| 497 return std::max(viewportWidth, maxWidth); |
| 498 } |
| 499 |
| 400 void ImageDocument::windowSizeChanged() { | 500 void ImageDocument::windowSizeChanged() { |
| 401 if (!m_imageElement || !m_imageSizeIsKnown || | 501 if (!m_imageElement || !m_imageSizeIsKnown || |
| 402 m_imageElement->document() != this) | 502 m_imageElement->document() != this) |
| 403 return; | 503 return; |
| 404 | 504 |
| 405 if (m_shrinkToFitMode == Viewport) { | 505 if (m_shrinkToFitMode == Viewport) { |
| 406 // Zooming in and out of an image being displayed within a viewport is done | 506 LayoutSize imageSize = cachedImageSize(m_imageElement); |
| 407 // by changing the page scale factor of the page instead of changing the | 507 int divWidth = calculateDivWidth(); |
| 408 // size of the image. The size of the image is set so that: | |
| 409 // * Images wider than the viewport take the full width of the screen. | |
| 410 // * Images taller than the viewport are initially aligned with the top of | |
| 411 // of the frame. | |
| 412 // * Images smaller in either dimension are centered along that axis. | |
| 413 LayoutSize imageSize = m_imageElement->cachedImage()->imageSize( | |
| 414 LayoutObject::shouldRespectImageOrientation( | |
| 415 m_imageElement->layoutObject()), | |
| 416 1.f); | |
| 417 int viewportWidth = frame()->host()->visualViewport().size().width(); | |
| 418 int viewportHeight = frame()->host()->visualViewport().size().height(); | |
| 419 float viewportAspectRatio = (float)viewportWidth / viewportHeight; | |
| 420 | |
| 421 // For huge images, minimum-scale=0.1 is still too big on small screens. | |
| 422 // Set the <div> width so that the image will shrink to fit the width of the | |
| 423 // screen when the scale is minimum. | |
| 424 int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); | |
| 425 int divWidth = std::max(viewportWidth, maxWidth); | |
| 426 m_divElement->setInlineStyleProperty(CSSPropertyWidth, divWidth, | 508 m_divElement->setInlineStyleProperty(CSSPropertyWidth, divWidth, |
| 427 CSSPrimitiveValue::UnitType::Pixels); | 509 CSSPrimitiveValue::UnitType::Pixels); |
| 428 | 510 |
| 429 // Explicitly set the height of the <div> containing the <img> so that it | 511 // Explicitly set the height of the <div> containing the <img> so that it |
| 430 // can display the full image without shrinking it, allowing a full-width | 512 // can display the full image without shrinking it, allowing a full-width |
| 431 // reading mode for normal-width-huge-height images. | 513 // reading mode for normal-width-huge-height images. |
| 514 float viewportAspectRatio = |
| 515 frame()->host()->visualViewport().size().aspectRatio(); |
| 432 int divHeight = std::max(imageSize.height().toInt(), | 516 int divHeight = std::max(imageSize.height().toInt(), |
| 433 (int)(divWidth / viewportAspectRatio)); | 517 static_cast<int>(divWidth / viewportAspectRatio)); |
| 434 m_divElement->setInlineStyleProperty(CSSPropertyHeight, divHeight, | 518 m_divElement->setInlineStyleProperty(CSSPropertyHeight, divHeight, |
| 435 CSSPrimitiveValue::UnitType::Pixels); | 519 CSSPrimitiveValue::UnitType::Pixels); |
| 436 return; | 520 return; |
| 437 } | 521 } |
| 438 | 522 |
| 439 bool fitsInWindow = imageFitsInWindow(); | 523 bool fitsInWindow = imageFitsInWindow(); |
| 440 | 524 |
| 441 // If the image has been explicitly zoomed in, restore the cursor if the image | 525 // If the image has been explicitly zoomed in, restore the cursor if the image |
| 442 // fits and set it to a zoom out cursor if the image doesn't fit | 526 // fits and set it to a zoom out cursor if the image doesn't fit |
| 443 if (!m_shouldShrinkImage) { | 527 if (!m_shouldShrinkImage) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 } | 572 } |
| 489 | 573 |
| 490 // -------- | 574 // -------- |
| 491 | 575 |
| 492 void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { | 576 void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { |
| 493 if (event->type() == EventTypeNames::resize) { | 577 if (event->type() == EventTypeNames::resize) { |
| 494 m_doc->windowSizeChanged(); | 578 m_doc->windowSizeChanged(); |
| 495 } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { | 579 } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { |
| 496 MouseEvent* mouseEvent = toMouseEvent(event); | 580 MouseEvent* mouseEvent = toMouseEvent(event); |
| 497 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); | 581 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); |
| 582 } else if ((event->type() == EventTypeNames::touchend || |
| 583 event->type() == EventTypeNames::touchcancel) && |
| 584 event->isTouchEvent()) { |
| 585 m_doc->updateImageStyle(); |
| 498 } | 586 } |
| 499 } | 587 } |
| 500 | 588 |
| 501 bool ImageEventListener::operator==(const EventListener& listener) const { | 589 bool ImageEventListener::operator==(const EventListener& listener) const { |
| 502 if (const ImageEventListener* imageEventListener = | 590 if (const ImageEventListener* imageEventListener = |
| 503 ImageEventListener::cast(&listener)) | 591 ImageEventListener::cast(&listener)) |
| 504 return m_doc == imageEventListener->m_doc; | 592 return m_doc == imageEventListener->m_doc; |
| 505 return false; | 593 return false; |
| 506 } | 594 } |
| 507 | 595 |
| 508 } // namespace blink | 596 } // namespace blink |
| OLD | NEW |