| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "core/fetch/ImageResourceContent.h" | |
| 6 | |
| 7 #include "core/fetch/ImageResource.h" | |
| 8 #include "core/fetch/ImageResourceInfo.h" | |
| 9 #include "core/fetch/ImageResourceObserver.h" | |
| 10 #include "core/svg/graphics/SVGImage.h" | |
| 11 #include "platform/Histogram.h" | |
| 12 #include "platform/RuntimeEnabledFeatures.h" | |
| 13 #include "platform/SharedBuffer.h" | |
| 14 #include "platform/geometry/IntSize.h" | |
| 15 #include "platform/graphics/BitmapImage.h" | |
| 16 #include "platform/graphics/PlaceholderImage.h" | |
| 17 #include "platform/tracing/TraceEvent.h" | |
| 18 #include "wtf/StdLibExtras.h" | |
| 19 #include "wtf/Vector.h" | |
| 20 #include <memory> | |
| 21 #include <v8.h> | |
| 22 | |
| 23 namespace blink { | |
| 24 namespace { | |
| 25 class NullImageResourceInfo final | |
| 26 : public GarbageCollectedFinalized<NullImageResourceInfo>, | |
| 27 public ImageResourceInfo { | |
| 28 USING_GARBAGE_COLLECTED_MIXIN(NullImageResourceInfo); | |
| 29 | |
| 30 public: | |
| 31 NullImageResourceInfo() {} | |
| 32 | |
| 33 DEFINE_INLINE_VIRTUAL_TRACE() { ImageResourceInfo::trace(visitor); } | |
| 34 | |
| 35 private: | |
| 36 const KURL& url() const override { return m_url; } | |
| 37 bool isSchedulingReload() const override { return false; } | |
| 38 bool hasDevicePixelRatioHeaderValue() const override { return false; } | |
| 39 float devicePixelRatioHeaderValue() const override { return 1.0; } | |
| 40 const ResourceResponse& response() const override { return m_response; } | |
| 41 ResourceStatus getStatus() const override { return ResourceStatus::Cached; } | |
| 42 bool isPlaceholder() const override { return false; } | |
| 43 bool isCacheValidator() const override { return false; } | |
| 44 bool schedulingReloadOrShouldReloadBrokenPlaceholder() const override { | |
| 45 return false; | |
| 46 } | |
| 47 bool isAccessAllowed( | |
| 48 SecurityOrigin*, | |
| 49 DoesCurrentFrameHaveSingleSecurityOrigin) const override { | |
| 50 return true; | |
| 51 } | |
| 52 bool hasCacheControlNoStoreHeader() const override { return false; } | |
| 53 const ResourceError& resourceError() const override { return m_error; } | |
| 54 | |
| 55 void decodeError(bool allDataReceived) override {} | |
| 56 void setDecodedSize(size_t) override {} | |
| 57 void willAddClientOrObserver() override {} | |
| 58 void didRemoveClientOrObserver() override {} | |
| 59 void emulateLoadStartedForInspector( | |
| 60 ResourceFetcher*, | |
| 61 const KURL&, | |
| 62 const AtomicString& initiatorName) override {} | |
| 63 | |
| 64 const KURL m_url; | |
| 65 const ResourceResponse m_response; | |
| 66 const ResourceError m_error; | |
| 67 }; | |
| 68 | |
| 69 } // namespace | |
| 70 | |
| 71 ImageResourceContent::ImageResourceContent(PassRefPtr<blink::Image> image) | |
| 72 : m_image(image), m_isRefetchableDataFromDiskCache(true) { | |
| 73 DEFINE_STATIC_LOCAL(NullImageResourceInfo, nullInfo, | |
| 74 (new NullImageResourceInfo())); | |
| 75 m_info = &nullInfo; | |
| 76 } | |
| 77 | |
| 78 ImageResourceContent* ImageResourceContent::fetch(FetchRequest& request, | |
| 79 ResourceFetcher* fetcher) { | |
| 80 // TODO(hiroshige): Remove direct references to ImageResource by making | |
| 81 // the dependencies around ImageResource and ImageResourceContent cleaner. | |
| 82 ImageResource* resource = ImageResource::fetch(request, fetcher); | |
| 83 if (!resource) | |
| 84 return nullptr; | |
| 85 return resource->getContent(); | |
| 86 } | |
| 87 | |
| 88 void ImageResourceContent::setImageResourceInfo(ImageResourceInfo* info) { | |
| 89 m_info = info; | |
| 90 } | |
| 91 | |
| 92 DEFINE_TRACE(ImageResourceContent) { | |
| 93 visitor->trace(m_info); | |
| 94 ImageObserver::trace(visitor); | |
| 95 } | |
| 96 | |
| 97 void ImageResourceContent::markObserverFinished( | |
| 98 ImageResourceObserver* observer) { | |
| 99 auto it = m_observers.find(observer); | |
| 100 if (it == m_observers.end()) | |
| 101 return; | |
| 102 m_observers.remove(it); | |
| 103 m_finishedObservers.add(observer); | |
| 104 } | |
| 105 | |
| 106 void ImageResourceContent::addObserver(ImageResourceObserver* observer) { | |
| 107 m_info->willAddClientOrObserver(); | |
| 108 | |
| 109 m_observers.add(observer); | |
| 110 | |
| 111 if (m_info->isCacheValidator()) | |
| 112 return; | |
| 113 | |
| 114 if (m_image && !m_image->isNull()) { | |
| 115 observer->imageChanged(this); | |
| 116 } | |
| 117 | |
| 118 if (isLoaded() && m_observers.contains(observer) && | |
| 119 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { | |
| 120 markObserverFinished(observer); | |
| 121 observer->imageNotifyFinished(this); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 void ImageResourceContent::removeObserver(ImageResourceObserver* observer) { | |
| 126 DCHECK(observer); | |
| 127 | |
| 128 auto it = m_observers.find(observer); | |
| 129 if (it != m_observers.end()) { | |
| 130 m_observers.remove(it); | |
| 131 } else { | |
| 132 it = m_finishedObservers.find(observer); | |
| 133 DCHECK(it != m_finishedObservers.end()); | |
| 134 m_finishedObservers.remove(it); | |
| 135 } | |
| 136 m_info->didRemoveClientOrObserver(); | |
| 137 } | |
| 138 | |
| 139 static void priorityFromObserver(const ImageResourceObserver* observer, | |
| 140 ResourcePriority& priority) { | |
| 141 ResourcePriority nextPriority = observer->computeResourcePriority(); | |
| 142 if (nextPriority.visibility == ResourcePriority::NotVisible) | |
| 143 return; | |
| 144 priority.visibility = ResourcePriority::Visible; | |
| 145 priority.intraPriorityValue += nextPriority.intraPriorityValue; | |
| 146 } | |
| 147 | |
| 148 ResourcePriority ImageResourceContent::priorityFromObservers() const { | |
| 149 ResourcePriority priority; | |
| 150 | |
| 151 for (const auto& it : m_finishedObservers) | |
| 152 priorityFromObserver(it.key, priority); | |
| 153 for (const auto& it : m_observers) | |
| 154 priorityFromObserver(it.key, priority); | |
| 155 | |
| 156 return priority; | |
| 157 } | |
| 158 | |
| 159 void ImageResourceContent::destroyDecodedData() { | |
| 160 if (!m_image) | |
| 161 return; | |
| 162 CHECK(!errorOccurred()); | |
| 163 m_image->destroyDecodedData(); | |
| 164 } | |
| 165 | |
| 166 void ImageResourceContent::doResetAnimation() { | |
| 167 if (m_image) | |
| 168 m_image->resetAnimation(); | |
| 169 } | |
| 170 | |
| 171 PassRefPtr<const SharedBuffer> ImageResourceContent::resourceBuffer() const { | |
| 172 if (m_image) | |
| 173 return m_image->data(); | |
| 174 return nullptr; | |
| 175 } | |
| 176 | |
| 177 bool ImageResourceContent::shouldUpdateImageImmediately() const { | |
| 178 // If we don't have the size available yet, then update immediately since | |
| 179 // we need to know the image size as soon as possible. Likewise for | |
| 180 // animated images, update right away since we shouldn't throttle animated | |
| 181 // images. | |
| 182 return m_sizeAvailable == Image::SizeUnavailable || | |
| 183 (m_image && m_image->maybeAnimated()); | |
| 184 } | |
| 185 | |
| 186 std::pair<blink::Image*, float> ImageResourceContent::brokenImage( | |
| 187 float deviceScaleFactor) { | |
| 188 if (deviceScaleFactor >= 2) { | |
| 189 DEFINE_STATIC_REF(blink::Image, brokenImageHiRes, | |
| 190 (blink::Image::loadPlatformResource("missingImage@2x"))); | |
| 191 return std::make_pair(brokenImageHiRes, 2); | |
| 192 } | |
| 193 | |
| 194 DEFINE_STATIC_REF(blink::Image, brokenImageLoRes, | |
| 195 (blink::Image::loadPlatformResource("missingImage"))); | |
| 196 return std::make_pair(brokenImageLoRes, 1); | |
| 197 } | |
| 198 | |
| 199 blink::Image* ImageResourceContent::getImage() { | |
| 200 if (errorOccurred()) { | |
| 201 // Returning the 1x broken image is non-ideal, but we cannot reliably access | |
| 202 // the appropriate deviceScaleFactor from here. It is critical that callers | |
| 203 // use ImageResourceContent::brokenImage() when they need the real, | |
| 204 // deviceScaleFactor-appropriate broken image icon. | |
| 205 return brokenImage(1).first; | |
| 206 } | |
| 207 | |
| 208 if (m_image) | |
| 209 return m_image.get(); | |
| 210 | |
| 211 return blink::Image::nullImage(); | |
| 212 } | |
| 213 | |
| 214 bool ImageResourceContent::usesImageContainerSize() const { | |
| 215 if (m_image) | |
| 216 return m_image->usesContainerSize(); | |
| 217 | |
| 218 return false; | |
| 219 } | |
| 220 | |
| 221 bool ImageResourceContent::imageHasRelativeSize() const { | |
| 222 if (m_image) | |
| 223 return m_image->hasRelativeSize(); | |
| 224 | |
| 225 return false; | |
| 226 } | |
| 227 | |
| 228 LayoutSize ImageResourceContent::imageSize( | |
| 229 RespectImageOrientationEnum shouldRespectImageOrientation, | |
| 230 float multiplier, | |
| 231 SizeType sizeType) { | |
| 232 if (!m_image) | |
| 233 return LayoutSize(); | |
| 234 | |
| 235 LayoutSize size; | |
| 236 | |
| 237 if (m_image->isBitmapImage() && | |
| 238 shouldRespectImageOrientation == RespectImageOrientation) { | |
| 239 size = | |
| 240 LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation()); | |
| 241 } else { | |
| 242 size = LayoutSize(m_image->size()); | |
| 243 } | |
| 244 | |
| 245 if (sizeType == IntrinsicCorrectedToDPR && hasDevicePixelRatioHeaderValue() && | |
| 246 devicePixelRatioHeaderValue() > 0) | |
| 247 multiplier = 1 / devicePixelRatioHeaderValue(); | |
| 248 | |
| 249 if (multiplier == 1 || m_image->hasRelativeSize()) | |
| 250 return size; | |
| 251 | |
| 252 // Don't let images that have a width/height >= 1 shrink below 1 when zoomed. | |
| 253 LayoutSize minimumSize( | |
| 254 size.width() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit(), | |
| 255 LayoutUnit(size.height() > LayoutUnit() ? LayoutUnit(1) : LayoutUnit())); | |
| 256 size.scale(multiplier); | |
| 257 size.clampToMinimumSize(minimumSize); | |
| 258 return size; | |
| 259 } | |
| 260 | |
| 261 void ImageResourceContent::notifyObservers( | |
| 262 NotifyFinishOption notifyingFinishOption, | |
| 263 const IntRect* changeRect) { | |
| 264 for (auto* observer : m_finishedObservers.asVector()) { | |
| 265 if (m_finishedObservers.contains(observer)) | |
| 266 observer->imageChanged(this, changeRect); | |
| 267 } | |
| 268 for (auto* observer : m_observers.asVector()) { | |
| 269 if (m_observers.contains(observer)) { | |
| 270 observer->imageChanged(this, changeRect); | |
| 271 if (notifyingFinishOption == ShouldNotifyFinish && | |
| 272 m_observers.contains(observer) && | |
| 273 !m_info->schedulingReloadOrShouldReloadBrokenPlaceholder()) { | |
| 274 markObserverFinished(observer); | |
| 275 observer->imageNotifyFinished(this); | |
| 276 } | |
| 277 } | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 PassRefPtr<Image> ImageResourceContent::createImage() { | |
| 282 if (m_info->response().mimeType() == "image/svg+xml") | |
| 283 return SVGImage::create(this); | |
| 284 return BitmapImage::create(this); | |
| 285 } | |
| 286 | |
| 287 void ImageResourceContent::clearImage() { | |
| 288 if (!m_image) | |
| 289 return; | |
| 290 int64_t length = m_image->data() ? m_image->data()->size() : 0; | |
| 291 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(-length); | |
| 292 | |
| 293 // If our Image has an observer, it's always us so we need to clear the back | |
| 294 // pointer before dropping our reference. | |
| 295 m_image->clearImageObserver(); | |
| 296 m_image.clear(); | |
| 297 m_sizeAvailable = Image::SizeUnavailable; | |
| 298 } | |
| 299 | |
| 300 void ImageResourceContent::clearImageAndNotifyObservers( | |
| 301 NotifyFinishOption notifyingFinishOption) { | |
| 302 clearImage(); | |
| 303 notifyObservers(notifyingFinishOption); | |
| 304 } | |
| 305 | |
| 306 void ImageResourceContent::updateImage(PassRefPtr<SharedBuffer> data, | |
| 307 ClearImageOption clearImageOption, | |
| 308 bool allDataReceived) { | |
| 309 TRACE_EVENT0("blink", "ImageResourceContent::updateImage"); | |
| 310 | |
| 311 if (clearImageOption == ImageResourceContent::ClearExistingImage) { | |
| 312 clearImage(); | |
| 313 } | |
| 314 | |
| 315 // Have the image update its data from its internal buffer. It will not do | |
| 316 // anything now, but will delay decoding until queried for info (like size or | |
| 317 // specific image frames). | |
| 318 if (data) { | |
| 319 if (!m_image) | |
| 320 m_image = createImage(); | |
| 321 DCHECK(m_image); | |
| 322 m_sizeAvailable = m_image->setData(std::move(data), allDataReceived); | |
| 323 } | |
| 324 | |
| 325 // Go ahead and tell our observers to try to draw if we have either received | |
| 326 // all the data or the size is known. Each chunk from the network causes | |
| 327 // observers to repaint, which will force that chunk to decode. | |
| 328 if (m_sizeAvailable == Image::SizeUnavailable && !allDataReceived) | |
| 329 return; | |
| 330 | |
| 331 if (m_info->isPlaceholder() && allDataReceived && m_image && | |
| 332 !m_image->isNull()) { | |
| 333 if (m_sizeAvailable == Image::SizeAvailable) { | |
| 334 // TODO(sclittle): Show the original image if the response consists of the | |
| 335 // entire image, such as if the entire image response body is smaller than | |
| 336 // the requested range. | |
| 337 IntSize dimensions = m_image->size(); | |
| 338 | |
| 339 clearImage(); | |
| 340 m_image = PlaceholderImage::create(this, dimensions); | |
| 341 } else { | |
| 342 // Clear the image so that it gets treated like a decoding error, since | |
| 343 // the attempt to build a placeholder image failed. | |
| 344 clearImage(); | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 if (!m_image || m_image->isNull()) { | |
| 349 clearImage(); | |
| 350 m_info->decodeError(allDataReceived); | |
| 351 } | |
| 352 | |
| 353 // It would be nice to only redraw the decoded band of the image, but with the | |
| 354 // current design (decoding delayed until painting) that seems hard. | |
| 355 notifyObservers(allDataReceived ? ShouldNotifyFinish : DoNotNotifyFinish); | |
| 356 } | |
| 357 | |
| 358 void ImageResourceContent::decodedSizeChangedTo(const blink::Image* image, | |
| 359 size_t newSize) { | |
| 360 if (!image || image != m_image) | |
| 361 return; | |
| 362 | |
| 363 m_info->setDecodedSize(newSize); | |
| 364 } | |
| 365 | |
| 366 bool ImageResourceContent::shouldPauseAnimation(const blink::Image* image) { | |
| 367 if (!image || image != m_image) | |
| 368 return false; | |
| 369 | |
| 370 for (const auto& it : m_finishedObservers) | |
| 371 if (it.key->willRenderImage()) | |
| 372 return false; | |
| 373 | |
| 374 for (const auto& it : m_observers) | |
| 375 if (it.key->willRenderImage()) | |
| 376 return false; | |
| 377 | |
| 378 return true; | |
| 379 } | |
| 380 | |
| 381 void ImageResourceContent::animationAdvanced(const blink::Image* image) { | |
| 382 if (!image || image != m_image) | |
| 383 return; | |
| 384 notifyObservers(DoNotNotifyFinish); | |
| 385 } | |
| 386 | |
| 387 void ImageResourceContent::updateImageAnimationPolicy() { | |
| 388 if (!m_image) | |
| 389 return; | |
| 390 | |
| 391 ImageAnimationPolicy newPolicy = ImageAnimationPolicyAllowed; | |
| 392 for (const auto& it : m_finishedObservers) { | |
| 393 if (it.key->getImageAnimationPolicy(newPolicy)) | |
| 394 break; | |
| 395 } | |
| 396 for (const auto& it : m_observers) { | |
| 397 if (it.key->getImageAnimationPolicy(newPolicy)) | |
| 398 break; | |
| 399 } | |
| 400 | |
| 401 if (m_image->animationPolicy() != newPolicy) { | |
| 402 m_image->resetAnimation(); | |
| 403 m_image->setAnimationPolicy(newPolicy); | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 void ImageResourceContent::changedInRect(const blink::Image* image, | |
| 408 const IntRect& rect) { | |
| 409 if (!image || image != m_image) | |
| 410 return; | |
| 411 notifyObservers(DoNotNotifyFinish, &rect); | |
| 412 } | |
| 413 | |
| 414 bool ImageResourceContent::isAccessAllowed(SecurityOrigin* securityOrigin) { | |
| 415 return m_info->isAccessAllowed( | |
| 416 securityOrigin, getImage()->currentFrameHasSingleSecurityOrigin() | |
| 417 ? ImageResourceInfo::HasSingleSecurityOrigin | |
| 418 : ImageResourceInfo::HasMultipleSecurityOrigin); | |
| 419 } | |
| 420 | |
| 421 void ImageResourceContent::emulateLoadStartedForInspector( | |
| 422 ResourceFetcher* fetcher, | |
| 423 const KURL& url, | |
| 424 const AtomicString& initiatorName) { | |
| 425 m_info->emulateLoadStartedForInspector(fetcher, url, initiatorName); | |
| 426 } | |
| 427 | |
| 428 // TODO(hiroshige): Consider removing the following methods, or stoping | |
| 429 // redirecting to ImageResource. | |
| 430 bool ImageResourceContent::isLoaded() const { | |
| 431 return getStatus() > ResourceStatus::Pending; | |
| 432 } | |
| 433 | |
| 434 bool ImageResourceContent::isLoading() const { | |
| 435 return getStatus() == ResourceStatus::Pending; | |
| 436 } | |
| 437 | |
| 438 bool ImageResourceContent::errorOccurred() const { | |
| 439 return getStatus() == ResourceStatus::LoadError || | |
| 440 getStatus() == ResourceStatus::DecodeError; | |
| 441 } | |
| 442 | |
| 443 bool ImageResourceContent::loadFailedOrCanceled() const { | |
| 444 return getStatus() == ResourceStatus::LoadError; | |
| 445 } | |
| 446 | |
| 447 ResourceStatus ImageResourceContent::getStatus() const { | |
| 448 return m_info->getStatus(); | |
| 449 } | |
| 450 | |
| 451 const KURL& ImageResourceContent::url() const { | |
| 452 return m_info->url(); | |
| 453 } | |
| 454 | |
| 455 bool ImageResourceContent::hasCacheControlNoStoreHeader() const { | |
| 456 return m_info->hasCacheControlNoStoreHeader(); | |
| 457 } | |
| 458 | |
| 459 float ImageResourceContent::devicePixelRatioHeaderValue() const { | |
| 460 return m_info->devicePixelRatioHeaderValue(); | |
| 461 } | |
| 462 | |
| 463 bool ImageResourceContent::hasDevicePixelRatioHeaderValue() const { | |
| 464 return m_info->hasDevicePixelRatioHeaderValue(); | |
| 465 } | |
| 466 | |
| 467 const ResourceResponse& ImageResourceContent::response() const { | |
| 468 return m_info->response(); | |
| 469 } | |
| 470 | |
| 471 const ResourceError& ImageResourceContent::resourceError() const { | |
| 472 return m_info->resourceError(); | |
| 473 } | |
| 474 | |
| 475 } // namespace blink | |
| OLD | NEW |