| OLD | NEW |
| 1 /* | 1 /* |
| 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) |
| 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) |
| 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) |
| 5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) | 5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) |
| 6 Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. | 6 Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
| 7 | 7 |
| 8 This library is free software; you can redistribute it and/or | 8 This library is free software; you can redistribute it and/or |
| 9 modify it under the terms of the GNU Library General Public | 9 modify it under the terms of the GNU Library General Public |
| 10 License as published by the Free Software Foundation; either | 10 License as published by the Free Software Foundation; either |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 */ | 22 */ |
| 23 | 23 |
| 24 #include "core/fetch/ImageResource.h" | 24 #include "core/fetch/ImageResource.h" |
| 25 | 25 |
| 26 #include "core/fetch/ImageResourceObserver.h" | 26 #include "core/fetch/ImageResourceObserver.h" |
| 27 #include "core/fetch/MemoryCache.h" | 27 #include "core/fetch/MemoryCache.h" |
| 28 #include "core/fetch/ResourceClient.h" | 28 #include "core/fetch/ResourceClient.h" |
| 29 #include "core/fetch/ResourceFetcher.h" | 29 #include "core/fetch/ResourceFetcher.h" |
| 30 #include "core/fetch/ResourceLoader.h" | 30 #include "core/fetch/ResourceLoader.h" |
| 31 #include "core/fetch/ResourceLoadingLog.h" | 31 #include "core/fetch/ResourceLoadingLog.h" |
| 32 #include "core/fetch/SubstituteData.h" |
| 32 #include "core/svg/graphics/SVGImage.h" | 33 #include "core/svg/graphics/SVGImage.h" |
| 33 #include "platform/RuntimeEnabledFeatures.h" | 34 #include "platform/RuntimeEnabledFeatures.h" |
| 34 #include "platform/SharedBuffer.h" | 35 #include "platform/SharedBuffer.h" |
| 35 #include "platform/TraceEvent.h" | 36 #include "platform/TraceEvent.h" |
| 37 #include "platform/geometry/IntSize.h" |
| 36 #include "platform/graphics/BitmapImage.h" | 38 #include "platform/graphics/BitmapImage.h" |
| 37 #include "public/platform/Platform.h" | 39 #include "public/platform/Platform.h" |
| 38 #include "public/platform/WebCachePolicy.h" | 40 #include "public/platform/WebCachePolicy.h" |
| 39 #include "wtf/CurrentTime.h" | 41 #include "wtf/CurrentTime.h" |
| 40 #include "wtf/HashCountedSet.h" | 42 #include "wtf/HashCountedSet.h" |
| 41 #include "wtf/StdLibExtras.h" | 43 #include "wtf/StdLibExtras.h" |
| 42 #include "wtf/Vector.h" | 44 #include "wtf/Vector.h" |
| 45 #include "wtf/text/StringToNumber.h" |
| 46 #include <cstdio> |
| 43 #include <memory> | 47 #include <memory> |
| 44 #include <v8.h> | 48 #include <v8.h> |
| 45 | 49 |
| 46 namespace blink { | 50 namespace blink { |
| 47 | 51 |
| 48 ImageResource* ImageResource::fetch(FetchRequest& request, ResourceFetcher* fetc
her) | 52 // Helper class that manages the state involved with attempting to load a |
| 53 // placeholder for an image. |
| 54 class ImageResource::PlaceholderLoaderJob : public GarbageCollectedFinalized<Pla
ceholderLoaderJob> { |
| 55 public: |
| 56 explicit PlaceholderLoaderJob(ResourceFetcher*); |
| 57 ~PlaceholderLoaderJob(); |
| 58 |
| 59 DECLARE_TRACE(); |
| 60 |
| 61 void onImageFetchComplete(ImageResource*); |
| 62 |
| 63 private: |
| 64 enum class State { |
| 65 Range = 0, |
| 66 CacheOnly, |
| 67 Placeholder, |
| 68 BypassCacheFull |
| 69 }; |
| 70 |
| 71 void finish(ImageResource* image) |
| 72 { |
| 73 DCHECK_EQ(image->m_placeholderLoaderJob.get(), this); |
| 74 image->m_placeholderLoaderJob = nullptr; |
| 75 } |
| 76 |
| 77 void onRangeFetchComplete(ImageResource*); |
| 78 void doCacheOnlyFetch(ImageResource*); |
| 79 void onCacheOnlyFetchComplete(ImageResource*); |
| 80 void loadPlaceholderOrBypassCache(ImageResource*); |
| 81 void doPlaceholderFetch(ImageResource*, const IntSize&); |
| 82 void doBypassCacheFullFetch(ImageResource*); |
| 83 |
| 84 State m_state; |
| 85 Member<ResourceFetcher> m_fetcher; |
| 86 IntSize m_dimensionsFromRange; |
| 87 }; |
| 88 |
| 89 ImageResource::PlaceholderLoaderJob::PlaceholderLoaderJob(ResourceFetcher* fetch
er) |
| 90 : m_state(State::Range) |
| 91 , m_fetcher(fetcher) |
| 92 { |
| 93 DCHECK(fetcher); |
| 94 } |
| 95 |
| 96 ImageResource::PlaceholderLoaderJob::~PlaceholderLoaderJob() {} |
| 97 |
| 98 DEFINE_TRACE(ImageResource::PlaceholderLoaderJob) |
| 99 { |
| 100 visitor->trace(m_fetcher); |
| 101 } |
| 102 |
| 103 void ImageResource::PlaceholderLoaderJob::onImageFetchComplete(ImageResource* im
age) |
| 104 { |
| 105 DCHECK(image); |
| 106 |
| 107 switch (m_state) { |
| 108 case State::Range: |
| 109 onRangeFetchComplete(image); |
| 110 break; |
| 111 case State::CacheOnly: |
| 112 onCacheOnlyFetchComplete(image); |
| 113 break; |
| 114 default: |
| 115 finish(image); |
| 116 break; |
| 117 } |
| 118 } |
| 119 |
| 120 // Parses the values from a Content-Range response header of the form |
| 121 // "bytes %u-%u/%u". Returns true iff parsing is successful. |
| 122 static bool parseContentRangeHeader(const String& contentRange, uint64_t* rangeF
irst, uint64_t* rangeLast, uint64_t* totalSize) |
| 123 { |
| 124 // Example Content-Range header: "bytes 0-2047/16366". |
| 125 if (!contentRange.startsWith("bytes ")) |
| 126 return false; |
| 127 size_t dashPos = contentRange.find('-', arraysize("bytes ") - 1); |
| 128 if (dashPos == kNotFound) |
| 129 return false; |
| 130 size_t slashPos = contentRange.find('/', dashPos + 1); |
| 131 if (slashPos == kNotFound) |
| 132 return false; |
| 133 |
| 134 const size_t rangeFirstPos = arraysize("bytes ") - 1, rangeLastPos = dashPos
+ 1, totalSizePos = slashPos + 1; |
| 135 bool rangeFirstOk = false, rangeLastOk = false, totalSizeOk = false; |
| 136 if (contentRange.is8Bit()) { |
| 137 *rangeFirst = charactersToUInt64Strict(contentRange.characters8() + rang
eFirstPos, dashPos - rangeFirstPos, &rangeFirstOk); |
| 138 *rangeLast = charactersToUInt64Strict(contentRange.characters8() + range
LastPos, slashPos - rangeLastPos, &rangeLastOk); |
| 139 *totalSize = charactersToUInt64Strict(contentRange.characters8() + total
SizePos, contentRange.length() - totalSizePos, &totalSizeOk); |
| 140 } else { |
| 141 *rangeFirst = charactersToUInt64Strict(contentRange.characters16() + ran
geFirstPos, dashPos - rangeFirstPos, &rangeFirstOk); |
| 142 *rangeLast = charactersToUInt64Strict(contentRange.characters16() + rang
eLastPos, slashPos - rangeLastPos, &rangeLastOk); |
| 143 *totalSize = charactersToUInt64Strict(contentRange.characters16() + tota
lSizePos, contentRange.length() - totalSizePos, &totalSizeOk); |
| 144 } |
| 145 return rangeFirstOk && rangeLastOk && totalSizeOk; |
| 146 } |
| 147 |
| 148 // Returns true if |response| likely represents the entire resource instead of |
| 149 // just a partial range. |
| 150 static bool hasEntireResource(const ResourceResponse& response) |
| 151 { |
| 152 if (response.httpStatusCode() != 206) |
| 153 return true; |
| 154 |
| 155 const String& contentRangeHeader = response.httpHeaderField("content-range")
; |
| 156 if (contentRangeHeader.isNull()) { |
| 157 // If there's no Content-Range header, then assume that the response is |
| 158 // the entire resource. |
| 159 return true; |
| 160 } |
| 161 |
| 162 uint64_t rangeFirst, rangeLast, totalSize; |
| 163 if (!parseContentRangeHeader(contentRangeHeader, &rangeFirst, &rangeLast, &t
otalSize)) |
| 164 return false; |
| 165 |
| 166 // Check if the returned range is the entire resource. |
| 167 return rangeFirst == 0 && rangeLast + 1 == totalSize; |
| 168 } |
| 169 |
| 170 void ImageResource::PlaceholderLoaderJob::onRangeFetchComplete(ImageResource* im
age) |
| 171 { |
| 172 DCHECK_EQ(State::Range, m_state); |
| 173 |
| 174 // If the response consists of the entire image, e.g. if the image was |
| 175 // smaller than the requested range or if the server responded with a 200 |
| 176 // response for the full image, then just use the full response to show the |
| 177 // full image. |
| 178 // Also avoid re-fetching requests that led to 204 responses, since those |
| 179 // typically indicate that it was for a tracking request, and there's likely |
| 180 // no point in attempting to re-fetch the image. |
| 181 if ((image->response().httpStatusCode() == 204 || !image->willPaintBrokenIma
ge()) && hasEntireResource(image->response())) { |
| 182 image->setIsPlaceholder(false); |
| 183 finish(image); |
| 184 return; |
| 185 } |
| 186 |
| 187 if (image->hasImage()) |
| 188 m_dimensionsFromRange = image->getImage()->size(); |
| 189 |
| 190 if (image->response().wasCached() && image->response().httpStatusCode() == 2
06 && image->resourceRequest().getCachePolicy() != WebCachePolicy::BypassingCach
e) { |
| 191 // If the range response came from the cache, then that means that it |
| 192 // was fresh in the cache and there's a chance that the entire image |
| 193 // response is fresh and in the cache, so attempt to fetch the entire |
| 194 // image from the cache. |
| 195 doCacheOnlyFetch(image); |
| 196 return; |
| 197 } |
| 198 loadPlaceholderOrBypassCache(image); |
| 199 } |
| 200 |
| 201 void ImageResource::PlaceholderLoaderJob::doCacheOnlyFetch(ImageResource* image) |
| 202 { |
| 203 m_state = State::CacheOnly; |
| 204 |
| 205 ResourceRequest request = image->getOriginalResourceRequest(); |
| 206 request.setCachePolicy(WebCachePolicy::ReturnCacheDataDontLoad); |
| 207 image->reload(m_fetcher.get(), request, SubstituteData()); |
| 208 } |
| 209 |
| 210 void ImageResource::PlaceholderLoaderJob::onCacheOnlyFetchComplete(ImageResource
* image) |
| 211 { |
| 212 DCHECK_EQ(State::CacheOnly, m_state); |
| 213 |
| 214 if (!image->willPaintBrokenImage() && hasEntireResource(image->response()))
{ |
| 215 image->setIsPlaceholder(false); |
| 216 finish(image); |
| 217 return; |
| 218 } |
| 219 loadPlaceholderOrBypassCache(image); |
| 220 } |
| 221 |
| 222 void ImageResource::PlaceholderLoaderJob::loadPlaceholderOrBypassCache(ImageReso
urce* image) |
| 223 { |
| 224 if (!m_dimensionsFromRange.isEmpty()) { |
| 225 doPlaceholderFetch(image, m_dimensionsFromRange); |
| 226 return; |
| 227 } |
| 228 if (image->getOriginalResourceRequest().getCachePolicy() != WebCachePolicy::
ReturnCacheDataDontLoad) { |
| 229 doBypassCacheFullFetch(image); |
| 230 return; |
| 231 } |
| 232 // Return the broken image since there's nothing else that can be done here. |
| 233 image->setIsPlaceholder(false); |
| 234 finish(image); |
| 235 } |
| 236 |
| 237 static Vector<char> generatePlaceholderBox(const IntSize& dimensions) |
| 238 { |
| 239 DCHECK(!dimensions.isEmpty()); |
| 240 |
| 241 static const char kFormat[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" stan
dalone=\"no\"?>\n" |
| 242 "<svg xmlns=\"http://www.w3.org/2000/svg\" wid
th=\"%d\" height=\"%d\">\n" |
| 243 "<rect width=\"100%%\" height=\"100%%\" style=
\"fill:rgba(127,127,127,0.4);stroke-width:%d;stroke:black\" />\n" |
| 244 "</svg>"; |
| 245 // Don't add the black border to tiny images, to avoid coloring the entire i
mage black. |
| 246 int strokeWidth = dimensions.width() >= 10 && dimensions.height() >= 10 ? 2
: 0; |
| 247 |
| 248 int expectedResult = std::snprintf(nullptr, 0, kFormat, dimensions.width(),
dimensions.height(), strokeWidth); |
| 249 DCHECK_LE(0, expectedResult); |
| 250 Vector<char> out(safeCast<size_t>(expectedResult) + 1 /* for trailing '\0' *
/); |
| 251 int result = std::snprintf(out.data(), out.size(), kFormat, dimensions.width
(), dimensions.height(), strokeWidth); |
| 252 DCHECK_EQ(expectedResult, result); |
| 253 DCHECK_EQ('\0', out.last()); |
| 254 out.removeLast(); // Cut off the trailing '\0'. |
| 255 return out; |
| 256 } |
| 257 |
| 258 void ImageResource::PlaceholderLoaderJob::doPlaceholderFetch(ImageResource* imag
e, const IntSize& dimensions) |
| 259 { |
| 260 DCHECK(!dimensions.isEmpty()); |
| 261 m_state = State::Placeholder; |
| 262 |
| 263 Vector<char> svgBox = generatePlaceholderBox(dimensions); |
| 264 SubstituteData substituteData(SharedBuffer::adoptVector(svgBox), "image/svg+
xml", "utf-8", KURL(), LoadNormally); |
| 265 DCHECK(image->isPlaceholder()); |
| 266 image->reload(m_fetcher.get(), image->getOriginalResourceRequest(), substitu
teData); |
| 267 } |
| 268 |
| 269 void ImageResource::PlaceholderLoaderJob::doBypassCacheFullFetch(ImageResource*
image) |
| 270 { |
| 271 m_state = State::BypassCacheFull; |
| 272 |
| 273 // This reload is done bypassing the cache for several reasons: |
| 274 // |
| 275 // (1) CacheOnly fetches fail for sparse entries in the disk cache even if |
| 276 // all the ranges of the full resource are available separately, so |
| 277 // bypassing the cache here ensures that the full response is cached |
| 278 // non-sparsely, so future fetches of the same image will be properly |
| 279 // satisfied by the full cached response, reducing the cost of future |
| 280 // fetches of this same image. |
| 281 // |
| 282 // (2) It's possible that the origin server doesn't understand ranges, |
| 283 // and it returned a broken range, so bypassing the cache for this reload |
| 284 // will hopefully return the full unbroken image instead of attempting to |
| 285 // fetch the latter range of the image and combine that with the existing |
| 286 // potentially broken range. |
| 287 // |
| 288 // TODO(sclittle): Investigate if the issue with (1) can be fixed, i.e. make |
| 289 // the disk cache support sparse cache entries together with |
| 290 // WebCachePolicy::ReturnCacheDataDontLoad. |
| 291 ResourceRequest request = image->getOriginalResourceRequest(); |
| 292 request.setCachePolicy(WebCachePolicy::BypassingCache); |
| 293 image->setIsPlaceholder(false); |
| 294 image->reload(m_fetcher.get(), request, SubstituteData()); |
| 295 } |
| 296 |
| 297 static void modifyRequestToFetchPlaceholderRange(ResourceRequest* request) |
| 298 { |
| 299 DCHECK_EQ(nullAtom, request->httpHeaderField("range")); |
| 300 |
| 301 // Fetch the first few bytes of the image. This number is tuned to both (a) |
| 302 // likely capture the entire image for small images and (b) likely contain t
he |
| 303 // dimensions for larger images. |
| 304 // TODO(sclittle): Calculate the optimal value for this number. |
| 305 request->setHTTPHeaderField("range", "bytes=0-2047"); |
| 306 } |
| 307 |
| 308 static void unmodifyRequestToFetchPlaceholderRange(ResourceRequest* request) |
| 309 { |
| 310 DCHECK_EQ("bytes=0-2047", request->httpHeaderField("range")); |
| 311 request->clearHTTPHeaderField("range"); |
| 312 } |
| 313 |
| 314 Resource* ImageResource::ImageResourceFactory::create(const ResourceRequest& req
uest, const ResourceLoaderOptions& options, const String&) const |
| 315 { |
| 316 if (m_isPlaceholder) { |
| 317 ResourceRequest originalRequest = request; |
| 318 unmodifyRequestToFetchPlaceholderRange(&originalRequest); |
| 319 return new ImageResource(request, options, originalRequest, m_isPlacehol
der); |
| 320 } |
| 321 return new ImageResource(request, options, request, m_isPlaceholder); |
| 322 } |
| 323 |
| 324 ImageResource* ImageResource::fetch(FetchRequest& request, ResourceFetcher* fetc
her, PlaceholderRequestType placeholderRequestType) |
| 49 { | 325 { |
| 50 if (request.resourceRequest().requestContext() == WebURLRequest::RequestCont
extUnspecified) | 326 if (request.resourceRequest().requestContext() == WebURLRequest::RequestCont
extUnspecified) |
| 51 request.mutableResourceRequest().setRequestContext(WebURLRequest::Reques
tContextImage); | 327 request.mutableResourceRequest().setRequestContext(WebURLRequest::Reques
tContextImage); |
| 52 if (fetcher->context().pageDismissalEventBeingDispatched()) { | 328 if (fetcher->context().pageDismissalEventBeingDispatched()) { |
| 53 KURL requestURL = request.resourceRequest().url(); | 329 KURL requestURL = request.resourceRequest().url(); |
| 54 if (requestURL.isValid() && fetcher->context().canRequest(Resource::Imag
e, request.resourceRequest(), requestURL, request.options(), request.forPreload(
), request.getOriginRestriction())) | 330 if (requestURL.isValid() && fetcher->context().canRequest(Resource::Imag
e, request.resourceRequest(), requestURL, request.options(), request.forPreload(
), request.getOriginRestriction())) |
| 55 fetcher->context().sendImagePing(requestURL); | 331 fetcher->context().sendImagePing(requestURL); |
| 56 return nullptr; | 332 return nullptr; |
| 57 } | 333 } |
| 58 | 334 |
| 59 return toImageResource(fetcher->requestResource(request, ImageResourceFactor
y())); | 335 bool attemptPlaceholder = placeholderRequestType == PlaceholderRequestType::
AllowPlaceholder && request.url().protocolIsInHTTPFamily() |
| 336 // Only issue range requests for GET requests, since that way it should |
| 337 // be safe to re-issue the request without side effects. |
| 338 && request.resourceRequest().httpMethod() == "GET" |
| 339 // If the request already has a range request header, then don't |
| 340 // overwrite that range header, and just attempt to fetch the image |
| 341 // normally without generating a placeholder. |
| 342 && request.resourceRequest().httpHeaderField("range").isNull(); |
| 343 |
| 344 if (attemptPlaceholder) |
| 345 modifyRequestToFetchPlaceholderRange(&request.mutableResourceRequest()); |
| 346 ImageResource* image = toImageResource(fetcher->requestResource(request, Ima
geResourceFactory(attemptPlaceholder))); |
| 347 |
| 348 // Check if the image is loading a placeholder already, otherwise add a Plac
eholderLoaderJob. |
| 349 if (attemptPlaceholder && (image->isLoading() || image->stillNeedsLoad()) &&
image->isPlaceholder() && !image->m_placeholderLoaderJob) |
| 350 image->m_placeholderLoaderJob = new PlaceholderLoaderJob(fetcher); |
| 351 |
| 352 // If this image shouldn't be a placeholder but it is, then reload it as not
a placeholder. |
| 353 if (!attemptPlaceholder && image->isPlaceholder()) { |
| 354 image->m_placeholderLoaderJob = nullptr; |
| 355 // Note that the cache is not bypassed for this reload - it should be fi
ne |
| 356 // to use a cached copy if it exists. |
| 357 image->setIsPlaceholder(false); |
| 358 image->reload(fetcher, request.resourceRequest(), SubstituteData()); |
| 359 } |
| 360 return image; |
| 60 } | 361 } |
| 61 | 362 |
| 62 ImageResource::ImageResource(const ResourceRequest& resourceRequest, const Resou
rceLoaderOptions& options) | 363 ImageResource::ImageResource(const ResourceRequest& resourceRequest, const Resou
rceLoaderOptions& options, const ResourceRequest& originalRequest, bool isPlaceh
older) |
| 63 : Resource(resourceRequest, Image, options) | 364 : Resource(resourceRequest, Image, options) |
| 64 , m_devicePixelRatioHeaderValue(1.0) | 365 , m_devicePixelRatioHeaderValue(1.0) |
| 65 , m_image(nullptr) | 366 , m_image(nullptr) |
| 66 , m_hasDevicePixelRatioHeaderValue(false) | 367 , m_hasDevicePixelRatioHeaderValue(false) |
| 368 , m_originalRequest(originalRequest) |
| 369 , m_isPlaceholder(isPlaceholder) |
| 370 , m_isSchedulingReload(false) |
| 67 { | 371 { |
| 68 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; | 372 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this; |
| 69 } | 373 } |
| 70 | 374 |
| 71 ImageResource::ImageResource(blink::Image* image, const ResourceLoaderOptions& o
ptions) | 375 ImageResource::ImageResource(blink::Image* image, const ResourceLoaderOptions& o
ptions) |
| 72 : Resource(ResourceRequest(""), Image, options) | 376 : Resource(ResourceRequest(""), Image, options) |
| 73 , m_devicePixelRatioHeaderValue(1.0) | 377 , m_devicePixelRatioHeaderValue(1.0) |
| 74 , m_image(image) | 378 , m_image(image) |
| 75 , m_hasDevicePixelRatioHeaderValue(false) | 379 , m_hasDevicePixelRatioHeaderValue(false) |
| 380 , m_originalRequest(resourceRequest()) |
| 381 , m_isPlaceholder(false) |
| 382 , m_isSchedulingReload(false) |
| 76 { | 383 { |
| 77 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this; | 384 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(Image) " << this; |
| 78 setStatus(Cached); | 385 setStatus(Cached); |
| 79 } | 386 } |
| 80 | 387 |
| 81 ImageResource::~ImageResource() | 388 ImageResource::~ImageResource() |
| 82 { | 389 { |
| 83 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; | 390 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this; |
| 84 clearImage(); | 391 clearImage(); |
| 85 } | 392 } |
| 86 | 393 |
| 87 DEFINE_TRACE(ImageResource) | 394 DEFINE_TRACE(ImageResource) |
| 88 { | 395 { |
| 89 visitor->trace(m_multipartParser); | 396 visitor->trace(m_multipartParser); |
| 397 visitor->trace(m_placeholderLoaderJob); |
| 90 Resource::trace(visitor); | 398 Resource::trace(visitor); |
| 91 ImageObserver::trace(visitor); | 399 ImageObserver::trace(visitor); |
| 92 MultipartImageResourceParser::Client::trace(visitor); | 400 MultipartImageResourceParser::Client::trace(visitor); |
| 93 } | 401 } |
| 94 | 402 |
| 95 void ImageResource::checkNotify() | 403 void ImageResource::checkNotify() |
| 96 { | 404 { |
| 405 if (m_isSchedulingReload) |
| 406 return; |
| 407 |
| 408 if (m_placeholderLoaderJob) |
| 409 m_placeholderLoaderJob->onImageFetchComplete(this); |
| 410 |
| 411 if (m_isSchedulingReload || m_placeholderLoaderJob) |
| 412 return; |
| 413 |
| 97 notifyObserversInternal(MarkFinishedOption::ShouldMarkFinished); | 414 notifyObserversInternal(MarkFinishedOption::ShouldMarkFinished); |
| 98 Resource::checkNotify(); | 415 Resource::checkNotify(); |
| 99 } | 416 } |
| 100 | 417 |
| 101 void ImageResource::notifyObserversInternal(MarkFinishedOption markFinishedOptio
n) | 418 void ImageResource::notifyObserversInternal(MarkFinishedOption markFinishedOptio
n) |
| 102 { | 419 { |
| 103 if (isLoading()) | 420 if (isLoading()) |
| 104 return; | 421 return; |
| 105 | 422 |
| 106 for (auto* observer : m_observers.asVector()) { | 423 for (auto* observer : m_observers.asVector()) { |
| 107 if (m_observers.contains(observer)) { | 424 if (m_observers.contains(observer)) { |
| 108 if (markFinishedOption == MarkFinishedOption::ShouldMarkFinished) | 425 if (markFinishedOption == MarkFinishedOption::ShouldMarkFinished) |
| 109 markObserverFinished(observer); | 426 markObserverFinished(observer); |
| 110 observer->imageNotifyFinished(this); | 427 observer->imageNotifyFinished(this); |
| 111 } | 428 } |
| 112 } | 429 } |
| 113 } | 430 } |
| 114 | 431 |
| 115 void ImageResource::markObserverFinished(ImageResourceObserver* observer) | 432 void ImageResource::markObserverFinished(ImageResourceObserver* observer) |
| 116 { | 433 { |
| 434 if (m_isSchedulingReload || m_placeholderLoaderJob) |
| 435 return; |
| 436 |
| 117 if (m_observers.contains(observer)) { | 437 if (m_observers.contains(observer)) { |
| 118 m_finishedObservers.add(observer); | 438 m_finishedObservers.add(observer); |
| 119 m_observers.remove(observer); | 439 m_observers.remove(observer); |
| 120 } | 440 } |
| 121 } | 441 } |
| 122 | 442 |
| 123 void ImageResource::didAddClient(ResourceClient* client) | 443 void ImageResource::didAddClient(ResourceClient* client) |
| 124 { | 444 { |
| 125 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); | 445 DCHECK((m_multipartParser && isLoading()) || !data() || m_image); |
| 126 Resource::didAddClient(client); | 446 Resource::didAddClient(client); |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 | 658 |
| 339 void ImageResource::clear() | 659 void ImageResource::clear() |
| 340 { | 660 { |
| 341 clearImage(); | 661 clearImage(); |
| 342 clearData(); | 662 clearData(); |
| 343 setEncodedSize(0); | 663 setEncodedSize(0); |
| 344 } | 664 } |
| 345 | 665 |
| 346 inline void ImageResource::createImage() | 666 inline void ImageResource::createImage() |
| 347 { | 667 { |
| 668 bool wantSVGImage = response().mimeType() == "image/svg+xml"; |
| 669 // It's possible for the existing |m_image| to be a bitmap image while the |
| 670 // response is now an SVG image, or vice versa, when an image is reloaded. |
| 671 // In these cases, clear and delete the existing image so that a new image |
| 672 // of the correct type can be created and used instead. |
| 673 if (m_image && m_image->isSVGImage() != wantSVGImage) |
| 674 clearImage(); |
| 675 |
| 348 // Create the image if it doesn't yet exist. | 676 // Create the image if it doesn't yet exist. |
| 349 if (m_image) | 677 if (m_image) |
| 350 return; | 678 return; |
| 351 | 679 |
| 352 if (response().mimeType() == "image/svg+xml") { | 680 if (wantSVGImage) { |
| 353 m_image = SVGImage::create(this); | 681 m_image = SVGImage::create(this); |
| 354 } else { | 682 } else { |
| 355 m_image = BitmapImage::create(this); | 683 m_image = BitmapImage::create(this); |
| 356 } | 684 } |
| 357 } | 685 } |
| 358 | 686 |
| 359 inline void ImageResource::clearImage() | 687 inline void ImageResource::clearImage() |
| 360 { | 688 { |
| 361 if (!m_image) | 689 if (!m_image) |
| 362 return; | 690 return; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 392 // to decode. | 720 // to decode. |
| 393 if (sizeAvailable == Image::SizeUnavailable && !allDataReceived) | 721 if (sizeAvailable == Image::SizeUnavailable && !allDataReceived) |
| 394 return; | 722 return; |
| 395 if (!m_image || m_image->isNull()) { | 723 if (!m_image || m_image->isNull()) { |
| 396 size_t size = encodedSize(); | 724 size_t size = encodedSize(); |
| 397 clear(); | 725 clear(); |
| 398 if (!errorOccurred()) | 726 if (!errorOccurred()) |
| 399 setStatus(DecodeError); | 727 setStatus(DecodeError); |
| 400 if (!allDataReceived && loader()) | 728 if (!allDataReceived && loader()) |
| 401 loader()->didFinishLoading(nullptr, monotonicallyIncreasingTime(), s
ize); | 729 loader()->didFinishLoading(nullptr, monotonicallyIncreasingTime(), s
ize); |
| 402 memoryCache()->remove(this); | 730 // It's possible that the error could have been resolved by the |
| 731 // didFinishLoading() call, so check again that the image is still in an |
| 732 // error state before proceeding. |
| 733 if (errorOccurred()) |
| 734 memoryCache()->remove(this); |
| 403 } | 735 } |
| 404 | 736 |
| 405 // It would be nice to only redraw the decoded band of the image, but with t
he current design | 737 // It would be nice to only redraw the decoded band of the image, but with t
he current design |
| 406 // (decoding delayed until painting) that seems hard. | 738 // (decoding delayed until painting) that seems hard. |
| 407 notifyObservers(); | 739 notifyObservers(); |
| 408 } | 740 } |
| 409 | 741 |
| 410 void ImageResource::updateImageAndClearBuffer() | 742 void ImageResource::updateImageAndClearBuffer() |
| 411 { | 743 { |
| 412 clearImage(); | 744 clearImage(); |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 } | 859 } |
| 528 | 860 |
| 529 if (m_image->animationPolicy() != newPolicy) { | 861 if (m_image->animationPolicy() != newPolicy) { |
| 530 m_image->resetAnimation(); | 862 m_image->resetAnimation(); |
| 531 m_image->setAnimationPolicy(newPolicy); | 863 m_image->setAnimationPolicy(newPolicy); |
| 532 } | 864 } |
| 533 } | 865 } |
| 534 | 866 |
| 535 void ImageResource::reloadIfLoFi(ResourceFetcher* fetcher) | 867 void ImageResource::reloadIfLoFi(ResourceFetcher* fetcher) |
| 536 { | 868 { |
| 537 if (resourceRequest().loFiState() != WebURLRequest::LoFiOn) | 869 if (!m_isPlaceholder && (resourceRequest().loFiState() != WebURLRequest::LoF
iOn || (isLoaded() && !response().httpHeaderField("chrome-proxy").contains("q=lo
w")))) |
| 538 return; | 870 return; |
| 539 if (isLoaded() && !response().httpHeaderField("chrome-proxy").contains("q=lo
w")) | 871 |
| 540 return; | 872 ResourceRequest reloadRequest = m_originalRequest; |
| 541 setCachePolicyBypassingCache(); | 873 reloadRequest.setCachePolicy(WebCachePolicy::BypassingCache); |
| 542 setLoFiStateOff(); | 874 reloadRequest.setLoFiState(WebURLRequest::LoFiOff); |
| 875 m_isPlaceholder = false; |
| 876 m_placeholderLoaderJob = nullptr; |
| 877 |
| 878 reload(fetcher, reloadRequest, SubstituteData()); |
| 879 } |
| 880 |
| 881 static void loadStaticResponse(ImageResource* resource, const SubstituteData& su
bstituteData) |
| 882 { |
| 883 DCHECK(resource); |
| 884 DCHECK(substituteData.isValid()); |
| 885 |
| 886 // This code was adapted from ResourceFetcher::resourceForStaticData(). |
| 887 ResourceResponse response(resource->resourceRequest().url(), substituteData.
mimeType(), substituteData.content()->size(), substituteData.textEncoding(), Str
ing()); |
| 888 response.setHTTPStatusCode(200); |
| 889 response.setHTTPStatusText("OK"); |
| 890 |
| 891 resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad())
; |
| 892 resource->responseReceived(response, nullptr); |
| 893 resource->setDataBufferingPolicy(BufferData); |
| 894 resource->setResourceBuffer(PassRefPtr<SharedBuffer>(substituteData.content(
))); |
| 895 resource->finish(); |
| 896 } |
| 897 |
| 898 void ImageResource::reload(ResourceFetcher* fetcher, const ResourceRequest& requ
est, const SubstituteData& substituteData) |
| 899 { |
| 900 DCHECK(!m_isSchedulingReload); |
| 901 m_isSchedulingReload = true; |
| 902 |
| 543 if (isLoading()) | 903 if (isLoading()) |
| 544 loader()->cancel(); | 904 loader()->cancel(); |
| 905 clearError(); |
| 545 clear(); | 906 clear(); |
| 546 notifyObservers(); | |
| 547 | 907 |
| 908 if (!substituteData.isValid()) { |
| 909 // If the reload will be done asynchronously, notify observers to update |
| 910 // the image's appearance while waiting for the async reload to |
| 911 // complete. |
| 912 notifyObservers(); |
| 913 } |
| 548 setStatus(NotStarted); | 914 setStatus(NotStarted); |
| 915 |
| 916 DCHECK(m_isSchedulingReload); |
| 917 m_isSchedulingReload = false; |
| 918 |
| 919 setResourceRequest(request); |
| 920 |
| 921 if (substituteData.isValid()) { |
| 922 loadStaticResponse(this, substituteData); |
| 923 return; |
| 924 } |
| 549 fetcher->startLoad(this); | 925 fetcher->startLoad(this); |
| 550 } | 926 } |
| 551 | 927 |
| 552 void ImageResource::changedInRect(const blink::Image* image, const IntRect& rect
) | 928 void ImageResource::changedInRect(const blink::Image* image, const IntRect& rect
) |
| 553 { | 929 { |
| 554 if (!image || image != m_image) | 930 if (!image || image != m_image) |
| 555 return; | 931 return; |
| 556 notifyObservers(&rect); | 932 notifyObservers(&rect); |
| 557 } | 933 } |
| 558 | 934 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 if (response().wasFetchedViaServiceWorker()) | 969 if (response().wasFetchedViaServiceWorker()) |
| 594 return response().serviceWorkerResponseType() != WebServiceWorkerRespons
eTypeOpaque; | 970 return response().serviceWorkerResponseType() != WebServiceWorkerRespons
eTypeOpaque; |
| 595 if (!getImage()->currentFrameHasSingleSecurityOrigin()) | 971 if (!getImage()->currentFrameHasSingleSecurityOrigin()) |
| 596 return false; | 972 return false; |
| 597 if (passesAccessControlCheck(securityOrigin)) | 973 if (passesAccessControlCheck(securityOrigin)) |
| 598 return true; | 974 return true; |
| 599 return !securityOrigin->taintsCanvas(response().url()); | 975 return !securityOrigin->taintsCanvas(response().url()); |
| 600 } | 976 } |
| 601 | 977 |
| 602 } // namespace blink | 978 } // namespace blink |
| OLD | NEW |