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 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
165 String fileName = | 165 String fileName = |
166 decodeURLEscapeSequences(document()->url().lastPathComponent()); | 166 decodeURLEscapeSequences(document()->url().lastPathComponent()); |
167 if (fileName.isEmpty()) | 167 if (fileName.isEmpty()) |
168 fileName = document()->url().host(); | 168 fileName = document()->url().host(); |
169 document()->setTitle(imageTitle(fileName, size)); | 169 document()->setTitle(imageTitle(fileName, size)); |
170 if (isDetached()) | 170 if (isDetached()) |
171 return; | 171 return; |
172 } | 172 } |
173 | 173 |
174 document()->imageUpdated(); | 174 document()->imageUpdated(); |
175 document()->imageLoaded(); | |
175 } | 176 } |
176 | 177 |
177 if (!isDetached()) | 178 if (!isDetached()) |
178 document()->finishedParsing(); | 179 document()->finishedParsing(); |
179 } | 180 } |
180 | 181 |
181 // -------- | 182 // -------- |
182 | 183 |
183 ImageDocument::ImageDocument(const DocumentInit& initializer) | 184 ImageDocument::ImageDocument(const DocumentInit& initializer) |
184 : HTMLDocument(initializer, ImageDocumentClass), | 185 : HTMLDocument(initializer, ImageDocumentClass), |
185 m_divElement(nullptr), | 186 m_divElement(nullptr), |
186 m_imageElement(nullptr), | 187 m_imageElement(nullptr), |
187 m_imageSizeIsKnown(false), | 188 m_imageSizeIsKnown(false), |
188 m_didShrinkImage(false), | 189 m_didShrinkImage(false), |
189 m_shouldShrinkImage(shouldShrinkToFit()), | 190 m_shouldShrinkImage(shouldShrinkToFit()), |
191 m_imageIsLoaded(false), | |
192 m_checkerSizeInitialized(false), | |
193 m_divWidth(0), | |
190 m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport | 194 m_shrinkToFitMode(frame()->settings()->viewportEnabled() ? Viewport |
191 : Desktop) { | 195 : Desktop) { |
192 setCompatibilityMode(QuirksMode); | 196 setCompatibilityMode(QuirksMode); |
193 lockCompatibilityMode(); | 197 lockCompatibilityMode(); |
194 UseCounter::count(*this, UseCounter::ImageDocument); | 198 UseCounter::count(*this, UseCounter::ImageDocument); |
195 if (!isInMainFrame()) | 199 if (!isInMainFrame()) |
196 UseCounter::count(*this, UseCounter::ImageDocumentInFrame); | 200 UseCounter::count(*this, UseCounter::ImageDocumentInFrame); |
197 } | 201 } |
198 | 202 |
199 DocumentParser* ImageDocument::createParser() { | 203 DocumentParser* ImageDocument::createParser() { |
(...skipping 11 matching lines...) Expand all Loading... | |
211 HTMLHeadElement* head = HTMLHeadElement::create(*this); | 215 HTMLHeadElement* head = HTMLHeadElement::create(*this); |
212 HTMLMetaElement* meta = HTMLMetaElement::create(*this); | 216 HTMLMetaElement* meta = HTMLMetaElement::create(*this); |
213 meta->setAttribute(nameAttr, "viewport"); | 217 meta->setAttribute(nameAttr, "viewport"); |
214 meta->setAttribute(contentAttr, "width=device-width, minimum-scale=0.1"); | 218 meta->setAttribute(contentAttr, "width=device-width, minimum-scale=0.1"); |
215 head->appendChild(meta); | 219 head->appendChild(meta); |
216 | 220 |
217 HTMLBodyElement* body = HTMLBodyElement::create(*this); | 221 HTMLBodyElement* body = HTMLBodyElement::create(*this); |
218 | 222 |
219 if (shouldShrinkToFit()) { | 223 if (shouldShrinkToFit()) { |
220 // Display the image prominently centered in the frame. | 224 // Display the image prominently centered in the frame. |
221 body->setAttribute(styleAttr, "margin: 0px;"); | 225 body->setAttribute(styleAttr, "margin: 0px; background: #0e0e0e;"); |
222 | 226 |
223 // See w3c example on how to centering an element: | 227 // See w3c example on how to center an element: |
224 // https://www.w3.org/Style/Examples/007/center.en.html | 228 // https://www.w3.org/Style/Examples/007/center.en.html |
225 m_divElement = HTMLDivElement::create(*this); | 229 m_divElement = HTMLDivElement::create(*this); |
226 m_divElement->setAttribute(styleAttr, | 230 m_divElement->setAttribute(styleAttr, |
227 "display: flex;" | 231 "display: flex;" |
228 "flex-direction: column;" | 232 "flex-direction: column;" |
229 "justify-content: center;" | 233 "justify-content: center;" |
230 "align-items: center;" | 234 "align-items: center;" |
231 "min-height: min-content;" | 235 "min-height: min-content;" |
232 "min-width: min-content;" | 236 "min-width: min-content;" |
233 "height: 100%;" | 237 "height: 100%;" |
234 "width: 100%;"); | 238 "width: 100%;"); |
235 HTMLContentElement* content = HTMLContentElement::create(*this); | 239 HTMLContentElement* content = HTMLContentElement::create(*this); |
236 m_divElement->appendChild(content); | 240 m_divElement->appendChild(content); |
237 | 241 |
238 ShadowRoot& shadowRoot = body->ensureUserAgentShadowRoot(); | 242 ShadowRoot& shadowRoot = body->ensureUserAgentShadowRoot(); |
239 shadowRoot.appendChild(m_divElement); | 243 shadowRoot.appendChild(m_divElement); |
240 } else { | 244 } else { |
241 body->setAttribute(styleAttr, "margin: 0px;"); | 245 body->setAttribute(styleAttr, "margin: 0px;"); |
242 } | 246 } |
243 | 247 |
244 willInsertBody(); | 248 willInsertBody(); |
245 | 249 |
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); | 250 m_imageElement = HTMLImageElement::create(*this); |
251 m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); | 251 updateImageStyle(); |
252 m_imageElement->setLoadingImageDocument(); | 252 m_imageElement->setLoadingImageDocument(); |
253 m_imageElement->setSrc(url().getString()); | 253 m_imageElement->setSrc(url().getString()); |
254 body->appendChild(m_imageElement.get()); | 254 body->appendChild(m_imageElement.get()); |
255 if (loader() && m_imageElement->cachedImage()) | 255 if (loader() && m_imageElement->cachedImage()) |
256 m_imageElement->cachedImage()->responseReceived(loader()->response(), | 256 m_imageElement->cachedImage()->responseReceived(loader()->response(), |
257 nullptr); | 257 nullptr); |
258 | 258 |
259 if (shouldShrinkToFit()) { | 259 if (shouldShrinkToFit()) { |
260 // Add event listeners | 260 // Add event listeners |
261 EventListener* listener = ImageEventListener::create(this); | 261 EventListener* listener = ImageEventListener::create(this); |
262 if (LocalDOMWindow* domWindow = this->domWindow()) | 262 if (LocalDOMWindow* domWindow = this->domWindow()) |
263 domWindow->addEventListener("resize", listener, false); | 263 domWindow->addEventListener(EventTypeNames::resize, listener, false); |
264 if (m_shrinkToFitMode == Desktop) | 264 |
265 m_imageElement->addEventListener("click", listener, false); | 265 if (m_shrinkToFitMode == Desktop) { |
266 m_imageElement->addEventListener(EventTypeNames::click, listener, false); | |
267 } else if (m_shrinkToFitMode == Viewport) { | |
pdr.
2016/10/28 21:20:48
Because many desktops have touchscreens these days
gone
2016/10/28 21:36:20
The touch listeners applied only to viewport mode
pdr.
2016/10/28 22:16:50
ah, sgtm.
| |
268 m_imageElement->addEventListener(EventTypeNames::touchend, listener, | |
269 false); | |
270 m_imageElement->addEventListener(EventTypeNames::touchcancel, listener, | |
271 false); | |
272 } | |
266 } | 273 } |
267 | 274 |
268 rootElement->appendChild(head); | 275 rootElement->appendChild(head); |
269 rootElement->appendChild(body); | 276 rootElement->appendChild(body); |
270 } | 277 } |
271 | 278 |
272 float ImageDocument::scale() const { | 279 float ImageDocument::scale() const { |
273 DCHECK_EQ(m_shrinkToFitMode, Desktop); | 280 DCHECK_EQ(m_shrinkToFitMode, Desktop); |
274 if (!m_imageElement || m_imageElement->document() != this) | 281 if (!m_imageElement || m_imageElement->document() != this) |
275 return 1.0f; | 282 return 1.0f; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
339 float scrollX = | 346 float scrollX = |
340 imageX / scale - static_cast<float>(frame()->view()->width()) / 2; | 347 imageX / scale - static_cast<float>(frame()->view()->width()) / 2; |
341 float scrollY = | 348 float scrollY = |
342 imageY / scale - static_cast<float>(frame()->view()->height()) / 2; | 349 imageY / scale - static_cast<float>(frame()->view()->height()) / 2; |
343 | 350 |
344 frame()->view()->layoutViewportScrollableArea()->setScrollOffset( | 351 frame()->view()->layoutViewportScrollableArea()->setScrollOffset( |
345 ScrollOffset(scrollX, scrollY), ProgrammaticScroll); | 352 ScrollOffset(scrollX, scrollY), ProgrammaticScroll); |
346 } | 353 } |
347 } | 354 } |
348 | 355 |
356 void ImageDocument::imageLoaded() { | |
357 m_imageIsLoaded = true; | |
358 updateImageStyle(); | |
359 } | |
360 | |
361 void ImageDocument::updateImageStyle() { | |
362 StringBuilder imageStyle; | |
363 imageStyle.append("-webkit-user-select: none;"); | |
364 if (shouldShrinkToFit()) { | |
365 if (m_shrinkToFitMode == Viewport) { | |
366 imageStyle.append("max-width: 100%;"); | |
367 } | |
368 | |
369 // Once the image has fully loaded, it is displayed atop a checkerboard to | |
370 // show transparency more faithfully. The pattern is generated via CSS. | |
371 if (m_imageIsLoaded) { | |
372 // The base square size is set to 10 because it rounds nicely for both the | |
373 // minimum scale (0.1) and maximum scale (5.0). | |
374 double checkerSize = 10; | |
375 | |
376 if (m_shrinkToFitMode == Viewport) { | |
377 // To ensure the checker pattern is visible for large images in | |
378 // viewports, the checker size must be dynamically adjusted to account | |
379 // for how much the page is currently being scaled. | |
380 // | |
381 // Unfortunately, the page scale is not properly initialized when this | |
pdr.
2016/10/28 21:20:48
This workaround is unfortunate. Can you think of a
gone
2016/10/28 21:36:20
Yeah, I'm not happy with it either, which is why I
pdr.
2016/10/28 22:16:50
Any idea where 0.367 comes from? Could be worth fi
gone
2016/10/28 23:42:22
Did some more experimentation about found that the
| |
382 // function is called the first time. This workaround determines the | |
383 // initial scale by comparing the size of the image's container against | |
384 // the viewport's size (see ImageDocument::windowSizeChanged). | |
385 double scale = | |
386 m_checkerSizeInitialized | |
387 ? frame()->host()->visualViewport().scale() | |
388 : double(frame()->host()->visualViewport().size().width()) / | |
389 m_divWidth; | |
390 checkerSize = std::max(1.0, checkerSize / scale); | |
391 m_checkerSizeInitialized = true; | |
392 } | |
393 | |
394 int roundedSize = static_cast<int>(round(checkerSize)); | |
395 int tileSize = roundedSize * 2; | |
396 | |
397 imageStyle.append("background-position: 0px 0px, "); | |
398 imageStyle.append(AtomicString::number(roundedSize)); | |
399 imageStyle.append("px "); | |
400 imageStyle.append(AtomicString::number(roundedSize)); | |
401 imageStyle.append("px;"); | |
402 | |
403 imageStyle.append("background-size: "); | |
404 imageStyle.append(AtomicString::number(tileSize)); | |
405 imageStyle.append("px "); | |
406 imageStyle.append(AtomicString::number(tileSize)); | |
407 imageStyle.append("px;"); | |
408 | |
409 imageStyle.append( | |
410 "background-color: white;" | |
411 "background-image:" | |
412 "linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, " | |
413 "#eee 75%, #eee 100%)," | |
414 "linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, " | |
415 "#eee 75%, #eee 100%);"); | |
416 } | |
417 } | |
418 | |
419 m_imageElement->setAttribute(styleAttr, imageStyle.toAtomicString()); | |
420 } | |
421 | |
349 void ImageDocument::imageUpdated() { | 422 void ImageDocument::imageUpdated() { |
350 DCHECK(m_imageElement); | 423 DCHECK(m_imageElement); |
351 | 424 |
352 if (m_imageSizeIsKnown) | 425 if (m_imageSizeIsKnown) |
353 return; | 426 return; |
354 | 427 |
355 updateStyleAndLayoutTree(); | 428 updateStyleAndLayoutTree(); |
356 if (!m_imageElement->cachedImage() || | 429 if (!m_imageElement->cachedImage() || |
357 m_imageElement->cachedImage() | 430 m_imageElement->cachedImage() |
358 ->imageSize(LayoutObject::shouldRespectImageOrientation( | 431 ->imageSize(LayoutObject::shouldRespectImageOrientation( |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
415 m_imageElement->layoutObject()), | 488 m_imageElement->layoutObject()), |
416 1.f); | 489 1.f); |
417 int viewportWidth = frame()->host()->visualViewport().size().width(); | 490 int viewportWidth = frame()->host()->visualViewport().size().width(); |
418 int viewportHeight = frame()->host()->visualViewport().size().height(); | 491 int viewportHeight = frame()->host()->visualViewport().size().height(); |
419 float viewportAspectRatio = (float)viewportWidth / viewportHeight; | 492 float viewportAspectRatio = (float)viewportWidth / viewportHeight; |
420 | 493 |
421 // For huge images, minimum-scale=0.1 is still too big on small screens. | 494 // 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 | 495 // Set the <div> width so that the image will shrink to fit the width of the |
423 // screen when the scale is minimum. | 496 // screen when the scale is minimum. |
424 int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); | 497 int maxWidth = std::min(imageSize.width().toInt(), viewportWidth * 10); |
425 int divWidth = std::max(viewportWidth, maxWidth); | 498 m_divWidth = std::max(viewportWidth, maxWidth); |
426 m_divElement->setInlineStyleProperty(CSSPropertyWidth, divWidth, | 499 m_divElement->setInlineStyleProperty(CSSPropertyWidth, m_divWidth, |
427 CSSPrimitiveValue::UnitType::Pixels); | 500 CSSPrimitiveValue::UnitType::Pixels); |
428 | 501 |
429 // Explicitly set the height of the <div> containing the <img> so that it | 502 // 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 | 503 // can display the full image without shrinking it, allowing a full-width |
431 // reading mode for normal-width-huge-height images. | 504 // reading mode for normal-width-huge-height images. |
432 int divHeight = std::max(imageSize.height().toInt(), | 505 int divHeight = |
433 (int)(divWidth / viewportAspectRatio)); | 506 std::max(imageSize.height().toInt(), |
507 static_cast<int>(m_divWidth / viewportAspectRatio)); | |
434 m_divElement->setInlineStyleProperty(CSSPropertyHeight, divHeight, | 508 m_divElement->setInlineStyleProperty(CSSPropertyHeight, divHeight, |
435 CSSPrimitiveValue::UnitType::Pixels); | 509 CSSPrimitiveValue::UnitType::Pixels); |
510 updateImageStyle(); | |
436 return; | 511 return; |
437 } | 512 } |
438 | 513 |
439 bool fitsInWindow = imageFitsInWindow(); | 514 bool fitsInWindow = imageFitsInWindow(); |
440 | 515 |
441 // If the image has been explicitly zoomed in, restore the cursor if the image | 516 // 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 | 517 // fits and set it to a zoom out cursor if the image doesn't fit |
443 if (!m_shouldShrinkImage) { | 518 if (!m_shouldShrinkImage) { |
444 if (fitsInWindow) | 519 if (fitsInWindow) |
445 m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); | 520 m_imageElement->removeInlineStyleProperty(CSSPropertyCursor); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
488 } | 563 } |
489 | 564 |
490 // -------- | 565 // -------- |
491 | 566 |
492 void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { | 567 void ImageEventListener::handleEvent(ExecutionContext*, Event* event) { |
493 if (event->type() == EventTypeNames::resize) { | 568 if (event->type() == EventTypeNames::resize) { |
494 m_doc->windowSizeChanged(); | 569 m_doc->windowSizeChanged(); |
495 } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { | 570 } else if (event->type() == EventTypeNames::click && event->isMouseEvent()) { |
496 MouseEvent* mouseEvent = toMouseEvent(event); | 571 MouseEvent* mouseEvent = toMouseEvent(event); |
497 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); | 572 m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); |
573 } else if (event->isTouchEvent()) { | |
574 m_doc->updateImageStyle(); | |
498 } | 575 } |
499 } | 576 } |
500 | 577 |
501 bool ImageEventListener::operator==(const EventListener& listener) const { | 578 bool ImageEventListener::operator==(const EventListener& listener) const { |
502 if (const ImageEventListener* imageEventListener = | 579 if (const ImageEventListener* imageEventListener = |
503 ImageEventListener::cast(&listener)) | 580 ImageEventListener::cast(&listener)) |
504 return m_doc == imageEventListener->m_doc; | 581 return m_doc == imageEventListener->m_doc; |
505 return false; | 582 return false; |
506 } | 583 } |
507 | 584 |
508 } // namespace blink | 585 } // namespace blink |
OLD | NEW |