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 20 matching lines...) Expand all Loading... |
31 #include "core/events/MouseEvent.h" | 31 #include "core/events/MouseEvent.h" |
32 #include "core/fetch/ImageResource.h" | 32 #include "core/fetch/ImageResource.h" |
33 #include "core/frame/FrameHost.h" | 33 #include "core/frame/FrameHost.h" |
34 #include "core/frame/FrameView.h" | 34 #include "core/frame/FrameView.h" |
35 #include "core/frame/LocalDOMWindow.h" | 35 #include "core/frame/LocalDOMWindow.h" |
36 #include "core/frame/LocalFrame.h" | 36 #include "core/frame/LocalFrame.h" |
37 #include "core/frame/Settings.h" | 37 #include "core/frame/Settings.h" |
38 #include "core/frame/UseCounter.h" | 38 #include "core/frame/UseCounter.h" |
39 #include "core/frame/VisualViewport.h" | 39 #include "core/frame/VisualViewport.h" |
40 #include "core/html/HTMLBodyElement.h" | 40 #include "core/html/HTMLBodyElement.h" |
| 41 #include "core/html/HTMLContentElement.h" |
| 42 #include "core/html/HTMLDivElement.h" |
41 #include "core/html/HTMLHeadElement.h" | 43 #include "core/html/HTMLHeadElement.h" |
42 #include "core/html/HTMLHtmlElement.h" | 44 #include "core/html/HTMLHtmlElement.h" |
43 #include "core/html/HTMLImageElement.h" | 45 #include "core/html/HTMLImageElement.h" |
44 #include "core/html/HTMLMetaElement.h" | 46 #include "core/html/HTMLMetaElement.h" |
45 #include "core/layout/LayoutObject.h" | 47 #include "core/layout/LayoutObject.h" |
46 #include "core/loader/DocumentLoader.h" | 48 #include "core/loader/DocumentLoader.h" |
47 #include "core/loader/FrameLoader.h" | 49 #include "core/loader/FrameLoader.h" |
48 #include "core/loader/FrameLoaderClient.h" | 50 #include "core/loader/FrameLoaderClient.h" |
49 #include "platform/HostWindow.h" | 51 #include "platform/HostWindow.h" |
50 #include "wtf/text/StringBuilder.h" | 52 #include "wtf/text/StringBuilder.h" |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 } | 175 } |
174 | 176 |
175 if (!isDetached()) | 177 if (!isDetached()) |
176 document()->finishedParsing(); | 178 document()->finishedParsing(); |
177 } | 179 } |
178 | 180 |
179 // -------- | 181 // -------- |
180 | 182 |
181 ImageDocument::ImageDocument(const DocumentInit& initializer) | 183 ImageDocument::ImageDocument(const DocumentInit& initializer) |
182 : HTMLDocument(initializer, ImageDocumentClass), | 184 : HTMLDocument(initializer, ImageDocumentClass), |
| 185 m_divElement(nullptr), |
183 m_imageElement(nullptr), | 186 m_imageElement(nullptr), |
184 m_imageSizeIsKnown(false), | 187 m_imageSizeIsKnown(false), |
185 m_didShrinkImage(false), | 188 m_didShrinkImage(false), |
186 m_shouldShrinkImage(shouldShrinkToFit()), | 189 m_shouldShrinkImage(shouldShrinkToFit()), |
187 m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport | 190 m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport |
188 : Desktop) { | 191 : Desktop) { |
189 setCompatibilityMode(QuirksMode); | 192 setCompatibilityMode(QuirksMode); |
190 lockCompatibilityMode(); | 193 lockCompatibilityMode(); |
191 UseCounter::count(*this, UseCounter::ImageDocument); | 194 UseCounter::count(*this, UseCounter::ImageDocument); |
192 if (!isInMainFrame()) | 195 if (!isInMainFrame()) |
(...skipping 12 matching lines...) Expand all Loading... |
205 if (isStopped()) | 208 if (isStopped()) |
206 return; // runScriptsAtDocumentElementAvailable can detach the frame. | 209 return; // runScriptsAtDocumentElementAvailable can detach the frame. |
207 | 210 |
208 HTMLHeadElement* head = HTMLHeadElement::create(*this); | 211 HTMLHeadElement* head = HTMLHeadElement::create(*this); |
209 HTMLMetaElement* meta = HTMLMetaElement::create(*this); | 212 HTMLMetaElement* meta = HTMLMetaElement::create(*this); |
210 meta->setAttribute(nameAttr, "viewport"); | 213 meta->setAttribute(nameAttr, "viewport"); |
211 meta->setAttribute(contentAttr, "width=device-width, minimum-scale=0.1"); | 214 meta->setAttribute(contentAttr, "width=device-width, minimum-scale=0.1"); |
212 head->appendChild(meta); | 215 head->appendChild(meta); |
213 | 216 |
214 HTMLBodyElement* body = HTMLBodyElement::create(*this); | 217 HTMLBodyElement* body = HTMLBodyElement::create(*this); |
215 body->setAttribute(styleAttr, "margin: 0px;"); | 218 |
| 219 if (shouldShrinkToFit()) { |
| 220 // Display the image prominently centered in the frame. |
| 221 body->setAttribute(styleAttr, "margin: 0px;"); |
| 222 |
| 223 // See w3c example on how to centering an element: |
| 224 // https://www.w3.org/Style/Examples/007/center.en.html |
| 225 m_divElement = HTMLDivElement::create(*this); |
| 226 m_divElement->setAttribute(styleAttr, |
| 227 "display: flex;" |
| 228 "flex-direction: column;" |
| 229 "justify-content: center;" |
| 230 "align-items: center;" |
| 231 "min-height: min-content;" |
| 232 "min-width: min-content;" |
| 233 "height: 100%;" |
| 234 "width: 100%;"); |
| 235 HTMLContentElement* content = HTMLContentElement::create(*this); |
| 236 m_divElement->appendChild(content); |
| 237 |
| 238 ShadowRoot& shadowRoot = body->ensureUserAgentShadowRoot(); |
| 239 shadowRoot.appendChild(m_divElement); |
| 240 } else { |
| 241 body->setAttribute(styleAttr, "margin: 0px;"); |
| 242 } |
216 | 243 |
217 willInsertBody(); | 244 willInsertBody(); |
218 | 245 |
| 246 StringBuilder imageStyle; |
| 247 imageStyle.append("-webkit-user-select: none;"); |
| 248 if (shouldShrinkToFit() && m_shrinkToFitMode == Viewport) |
| 249 imageStyle.append("max-width: 100%"); |
219 m_imageElement = HTMLImageElement::create(*this); | 250 m_imageElement = HTMLImageElement::create(*this); |
220 m_imageElement->setAttribute(styleAttr, "-webkit-user-select: none"); | 251 m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); |
221 m_imageElement->setLoadingImageDocument(); | 252 m_imageElement->setLoadingImageDocument(); |
222 m_imageElement->setSrc(url().getString()); | 253 m_imageElement->setSrc(url().getString()); |
223 body->appendChild(m_imageElement.get()); | 254 body->appendChild(m_imageElement.get()); |
224 if (loader() && m_imageElement->cachedImage()) | 255 if (loader() && m_imageElement->cachedImage()) |
225 m_imageElement->cachedImage()->responseReceived(loader()->response(), | 256 m_imageElement->cachedImage()->responseReceived(loader()->response(), |
226 nullptr); | 257 nullptr); |
227 | 258 |
228 if (shouldShrinkToFit()) { | 259 if (shouldShrinkToFit()) { |
229 // Add event listeners | 260 // Add event listeners |
230 EventListener* listener = ImageEventListener::create(this); | 261 EventListener* listener = ImageEventListener::create(this); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
287 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 318 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
288 | 319 |
289 if (!m_imageSizeIsKnown || imageFitsInWindow()) | 320 if (!m_imageSizeIsKnown || imageFitsInWindow()) |
290 return; | 321 return; |
291 | 322 |
292 m_shouldShrinkImage = !m_shouldShrinkImage; | 323 m_shouldShrinkImage = !m_shouldShrinkImage; |
293 | 324 |
294 if (m_shouldShrinkImage) { | 325 if (m_shouldShrinkImage) { |
295 windowSizeChanged(); | 326 windowSizeChanged(); |
296 } else { | 327 } else { |
| 328 // Adjust the coordinates to account for the fact that the image was |
| 329 // centered on the screen. |
| 330 float imageX = x - m_imageElement->offsetLeft(); |
| 331 float imageY = y - m_imageElement->offsetTop(); |
| 332 |
297 restoreImageSize(); | 333 restoreImageSize(); |
298 | 334 |
299 updateStyleAndLayout(); | 335 updateStyleAndLayout(); |
300 | 336 |
301 double scale = this->scale(); | 337 double scale = this->scale(); |
302 | 338 |
303 float scrollX = | 339 float scrollX = |
304 x / scale - static_cast<float>(frame()->view()->width()) / 2; | 340 imageX / scale - static_cast<float>(frame()->view()->width()) / 2; |
305 float scrollY = | 341 float scrollY = |
306 y / scale - static_cast<float>(frame()->view()->height()) / 2; | 342 imageY / scale - static_cast<float>(frame()->view()->height()) / 2; |
307 | 343 |
308 frame()->view()->layoutViewportScrollableArea()->setScrollOffset( | 344 frame()->view()->layoutViewportScrollableArea()->setScrollOffset( |
309 ScrollOffset(scrollX, scrollY), ProgrammaticScroll); | 345 ScrollOffset(scrollX, scrollY), ProgrammaticScroll); |
310 } | 346 } |
311 } | 347 } |
312 | 348 |
313 void ImageDocument::imageUpdated() { | 349 void ImageDocument::imageUpdated() { |
314 DCHECK(m_imageElement); | 350 DCHECK(m_imageElement); |
315 | 351 |
316 if (m_imageSizeIsKnown) | 352 if (m_imageSizeIsKnown) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
360 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 396 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
361 return this->scale() >= 1; | 397 return this->scale() >= 1; |
362 } | 398 } |
363 | 399 |
364 void ImageDocument::windowSizeChanged() { | 400 void ImageDocument::windowSizeChanged() { |
365 if (!m_imageElement || !m_imageSizeIsKnown || | 401 if (!m_imageElement || !m_imageSizeIsKnown || |
366 m_imageElement->document() != this) | 402 m_imageElement->document() != this) |
367 return; | 403 return; |
368 | 404 |
369 if (m_shrinkToFitMode == Viewport) { | 405 if (m_shrinkToFitMode == Viewport) { |
| 406 // Zooming in and out of an image being displayed within a viewport is done |
| 407 // by changing the page scale factor of the page instead of changing the |
| 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 |
370 // For huge images, minimum-scale=0.1 is still too big on small screens. | 421 // For huge images, minimum-scale=0.1 is still too big on small screens. |
371 // Set max-width so that the image will shrink to fit the width of the | 422 // Set the <div> width so that the image will shrink to fit the width of the |
372 // screen when the scale is minimum. Don't shrink height to fit because we | 423 // screen when the scale is minimum. |
373 // use width=device-width in viewport meta tag, and expect a full-width | 424 int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); |
| 425 int divWidth = std::max(viewportWidth, maxWidth); |
| 426 m_divElement->setInlineStyleProperty(CSSPropertyWidth, divWidth, |
| 427 CSSPrimitiveValue::UnitType::Pixels); |
| 428 |
| 429 // 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 |
374 // reading mode for normal-width-huge-height images. | 431 // reading mode for normal-width-huge-height images. |
375 int viewportWidth = frame()->host()->visualViewport().size().width(); | 432 int divHeight = std::max(imageSize.height().toInt(), |
376 m_imageElement->setInlineStyleProperty(CSSPropertyMaxWidth, | 433 (int)(divWidth / viewportAspectRatio)); |
377 viewportWidth * 10, | 434 m_divElement->setInlineStyleProperty(CSSPropertyHeight, divHeight, |
378 CSSPrimitiveValue::UnitType::Pixels); | 435 CSSPrimitiveValue::UnitType::Pixels); |
379 return; | 436 return; |
380 } | 437 } |
381 | 438 |
382 bool fitsInWindow = imageFitsInWindow(); | 439 bool fitsInWindow = imageFitsInWindow(); |
383 | 440 |
384 // If the image has been explicitly zoomed in, restore the cursor if the image | 441 // If the image has been explicitly zoomed in, restore the cursor if the image |
385 // fits and set it to a zoom out cursor if the image doesn't fit | 442 // fits and set it to a zoom out cursor if the image doesn't fit |
386 if (!m_shouldShrinkImage) { | 443 if (!m_shouldShrinkImage) { |
387 if (fitsInWindow) | 444 if (fitsInWindow) |
388 m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); | 445 m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); |
(...skipping 29 matching lines...) Expand all Loading... |
418 } | 475 } |
419 | 476 |
420 return m_imageElement->cachedImage(); | 477 return m_imageElement->cachedImage(); |
421 } | 478 } |
422 | 479 |
423 bool ImageDocument::shouldShrinkToFit() const { | 480 bool ImageDocument::shouldShrinkToFit() const { |
424 return frame()->isMainFrame(); | 481 return frame()->isMainFrame(); |
425 } | 482 } |
426 | 483 |
427 DEFINE_TRACE(ImageDocument) { | 484 DEFINE_TRACE(ImageDocument) { |
| 485 visitor->trace(m_divElement); |
428 visitor->trace(m_imageElement); | 486 visitor->trace(m_imageElement); |
429 HTMLDocument::trace(visitor); | 487 HTMLDocument::trace(visitor); |
430 } | 488 } |
431 | 489 |
432 // -------- | 490 // -------- |
433 | 491 |
434 void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { | 492 void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { |
435 if (event->type() == EventTypeNames::resize) { | 493 if (event->type() == EventTypeNames::resize) { |
436 m_doc->windowSizeChanged(); | 494 m_doc->windowSizeChanged(); |
437 } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { | 495 } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { |
438 MouseEvent* mouseEvent = toMouseEvent(event); | 496 MouseEvent* mouseEvent = toMouseEvent(event); |
439 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); | 497 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); |
440 } | 498 } |
441 } | 499 } |
442 | 500 |
443 bool ImageEventListener::operator==(const EventListener& listener) const { | 501 bool ImageEventListener::operator==(const EventListener& listener) const { |
444 if (const ImageEventListener* imageEventListener = | 502 if (const ImageEventListener* imageEventListener = |
445 ImageEventListener::cast(&listener)) | 503 ImageEventListener::cast(&listener)) |
446 return m_doc == imageEventListener->m_doc; | 504 return m_doc == imageEventListener->m_doc; |
447 return false; | 505 return false; |
448 } | 506 } |
449 | 507 |
450 } // namespace blink | 508 } // namespace blink |
OLD | NEW |