Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(704)

Side by Side Diff: third_party/WebKit/Source/core/fetch/ImageResource.cpp

Issue 2558033002: Loading: move ImageResource and related classes to core/loader/resource (Closed)
Patch Set: rebase and format Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6 Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22 */
23
24 #include "core/fetch/ImageResource.h"
25
26 #include "core/fetch/ImageResourceContent.h"
27 #include "core/fetch/ImageResourceInfo.h"
28 #include "core/fetch/MemoryCache.h"
29 #include "core/fetch/ResourceClient.h"
30 #include "core/fetch/ResourceFetcher.h"
31 #include "core/fetch/ResourceLoader.h"
32 #include "core/fetch/ResourceLoadingLog.h"
33 #include "platform/Histogram.h"
34 #include "platform/RuntimeEnabledFeatures.h"
35 #include "platform/SharedBuffer.h"
36 #include "platform/tracing/TraceEvent.h"
37 #include "public/platform/Platform.h"
38 #include "wtf/CurrentTime.h"
39 #include "wtf/StdLibExtras.h"
40 #include <memory>
41 #include <v8.h>
42
43 namespace blink {
44 namespace {
45 // The amount of time to wait before informing the clients that the image has
46 // been updated (in seconds). This effectively throttles invalidations that
47 // result from new data arriving for this image.
48 constexpr double kFlushDelaySeconds = 1.;
49 } // namespace
50
51 class ImageResource::ImageResourceInfoImpl final
52 : public GarbageCollectedFinalized<ImageResourceInfoImpl>,
53 public ImageResourceInfo {
54 USING_GARBAGE_COLLECTED_MIXIN(ImageResourceInfoImpl);
55
56 public:
57 ImageResourceInfoImpl(ImageResource* resource) : m_resource(resource) {
58 DCHECK(m_resource);
59 }
60 DEFINE_INLINE_VIRTUAL_TRACE() {
61 visitor->trace(m_resource);
62 ImageResourceInfo::trace(visitor);
63 }
64
65 private:
66 const KURL& url() const override { return m_resource->url(); }
67 bool isSchedulingReload() const override {
68 return m_resource->m_isSchedulingReload;
69 }
70 bool hasDevicePixelRatioHeaderValue() const override {
71 return m_resource->m_hasDevicePixelRatioHeaderValue;
72 }
73 float devicePixelRatioHeaderValue() const override {
74 return m_resource->m_devicePixelRatioHeaderValue;
75 }
76 const ResourceResponse& response() const override {
77 return m_resource->response();
78 }
79 Resource::Status getStatus() const override {
80 return m_resource->getStatus();
81 }
82 bool isPlaceholder() const override { return m_resource->isPlaceholder(); }
83 bool isCacheValidator() const override {
84 return m_resource->isCacheValidator();
85 }
86 bool schedulingReloadOrShouldReloadBrokenPlaceholder() const override {
87 return m_resource->m_isSchedulingReload ||
88 m_resource->shouldReloadBrokenPlaceholder();
89 }
90 bool isAccessAllowed(
91 SecurityOrigin* securityOrigin,
92 DoesCurrentFrameHaveSingleSecurityOrigin
93 doesCurrentFrameHasSingleSecurityOrigin) const override {
94 return m_resource->isAccessAllowed(securityOrigin,
95 doesCurrentFrameHasSingleSecurityOrigin);
96 }
97 bool hasCacheControlNoStoreHeader() const override {
98 return m_resource->hasCacheControlNoStoreHeader();
99 }
100 const ResourceError& resourceError() const override {
101 return m_resource->resourceError();
102 }
103
104 void decodeError(bool allDataReceived) override {
105 m_resource->decodeError(allDataReceived);
106 }
107 void setDecodedSize(size_t size) override {
108 m_resource->setDecodedSize(size);
109 }
110 void willAddClientOrObserver() override {
111 m_resource->willAddClientOrObserver(Resource::MarkAsReferenced);
112 }
113 void didRemoveClientOrObserver() override {
114 m_resource->didRemoveClientOrObserver();
115 }
116 void emulateLoadStartedForInspector(
117 ResourceFetcher* fetcher,
118 const KURL& url,
119 const AtomicString& initiatorName) override {
120 fetcher->emulateLoadStartedForInspector(m_resource.get(), url,
121 WebURLRequest::RequestContextImage,
122 initiatorName);
123 }
124
125 const Member<ImageResource> m_resource;
126 };
127
128 class ImageResource::ImageResourceFactory : public ResourceFactory {
129 STACK_ALLOCATED();
130
131 public:
132 ImageResourceFactory(const FetchRequest& fetchRequest)
133 : ResourceFactory(Resource::Image), m_fetchRequest(&fetchRequest) {}
134
135 Resource* create(const ResourceRequest& request,
136 const ResourceLoaderOptions& options,
137 const String&) const override {
138 return new ImageResource(request, options, ImageResourceContent::create(),
139 m_fetchRequest->placeholderImageRequestType() ==
140 FetchRequest::AllowPlaceholder);
141 }
142
143 private:
144 // Weak, unowned pointer. Must outlive |this|.
145 const FetchRequest* m_fetchRequest;
146 };
147
148 ImageResource* ImageResource::fetch(FetchRequest& request,
149 ResourceFetcher* fetcher) {
150 if (request.resourceRequest().requestContext() ==
151 WebURLRequest::RequestContextUnspecified) {
152 request.mutableResourceRequest().setRequestContext(
153 WebURLRequest::RequestContextImage);
154 }
155 if (fetcher->context().pageDismissalEventBeingDispatched()) {
156 KURL requestURL = request.resourceRequest().url();
157 if (requestURL.isValid()) {
158 ResourceRequestBlockedReason blockReason = fetcher->context().canRequest(
159 Resource::Image, request.resourceRequest(), requestURL,
160 request.options(), request.forPreload(),
161 request.getOriginRestriction());
162 if (blockReason == ResourceRequestBlockedReason::None)
163 fetcher->context().sendImagePing(requestURL);
164 }
165 return nullptr;
166 }
167
168 ImageResource* resource = toImageResource(
169 fetcher->requestResource(request, ImageResourceFactory(request)));
170 if (resource &&
171 request.placeholderImageRequestType() != FetchRequest::AllowPlaceholder &&
172 resource->m_isPlaceholder) {
173 // If the image is a placeholder, but this fetch doesn't allow a
174 // placeholder, then load the original image. Note that the cache is not
175 // bypassed here - it should be fine to use a cached copy if possible.
176 resource->reloadIfLoFiOrPlaceholderImage(
177 fetcher, kReloadAlwaysWithExistingCachePolicy);
178 }
179 return resource;
180 }
181
182 ImageResource* ImageResource::create(const ResourceRequest& request) {
183 return new ImageResource(request, ResourceLoaderOptions(),
184 ImageResourceContent::create(), false);
185 }
186
187 ImageResource::ImageResource(const ResourceRequest& resourceRequest,
188 const ResourceLoaderOptions& options,
189 ImageResourceContent* content,
190 bool isPlaceholder)
191 : Resource(resourceRequest, Image, options),
192 m_content(content),
193 m_devicePixelRatioHeaderValue(1.0),
194 m_hasDevicePixelRatioHeaderValue(false),
195 m_isSchedulingReload(false),
196 m_isPlaceholder(isPlaceholder),
197 m_flushTimer(this, &ImageResource::flushImageIfNeeded) {
198 DCHECK(getContent());
199 RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this;
200 getContent()->setImageResourceInfo(new ImageResourceInfoImpl(this));
201 }
202
203 ImageResource::~ImageResource() {
204 RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this;
205 }
206
207 DEFINE_TRACE(ImageResource) {
208 visitor->trace(m_multipartParser);
209 visitor->trace(m_content);
210 Resource::trace(visitor);
211 MultipartImageResourceParser::Client::trace(visitor);
212 }
213
214 void ImageResource::checkNotify() {
215 // Don't notify clients of completion if this ImageResource is
216 // about to be reloaded.
217 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder())
218 return;
219
220 Resource::checkNotify();
221 }
222
223 bool ImageResource::hasClientsOrObservers() const {
224 return Resource::hasClientsOrObservers() || getContent()->hasObservers();
225 }
226
227 void ImageResource::didAddClient(ResourceClient* client) {
228 DCHECK((m_multipartParser && isLoading()) || !data() ||
229 getContent()->hasImage());
230
231 // Don't notify observers and clients of completion if this ImageResource is
232 // about to be reloaded.
233 if (m_isSchedulingReload || shouldReloadBrokenPlaceholder())
234 return;
235
236 Resource::didAddClient(client);
237 }
238
239 void ImageResource::destroyDecodedDataForFailedRevalidation() {
240 getContent()->clearImage();
241 setDecodedSize(0);
242 }
243
244 void ImageResource::destroyDecodedDataIfPossible() {
245 getContent()->destroyDecodedData();
246 if (getContent()->hasImage() && !isPreloaded() &&
247 getContent()->isRefetchableDataFromDiskCache()) {
248 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer.EstimatedDroppableEncodedSize",
249 encodedSize() / 1024);
250 }
251 }
252
253 void ImageResource::allClientsAndObserversRemoved() {
254 CHECK(!getContent()->hasImage() || !errorOccurred());
255 // If possible, delay the resetting until back at the event loop. Doing so
256 // after a conservative GC prevents resetAnimation() from upsetting ongoing
257 // animation updates (crbug.com/613709)
258 if (!ThreadHeap::willObjectBeLazilySwept(this)) {
259 Platform::current()->currentThread()->getWebTaskRunner()->postTask(
260 BLINK_FROM_HERE, WTF::bind(&ImageResourceContent::doResetAnimation,
261 wrapWeakPersistent(getContent())));
262 } else {
263 getContent()->doResetAnimation();
264 }
265 if (m_multipartParser)
266 m_multipartParser->cancel();
267 Resource::allClientsAndObserversRemoved();
268 }
269
270 PassRefPtr<const SharedBuffer> ImageResource::resourceBuffer() const {
271 if (data())
272 return data();
273 return getContent()->resourceBuffer();
274 }
275
276 void ImageResource::appendData(const char* data, size_t length) {
277 v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length);
278 if (m_multipartParser) {
279 m_multipartParser->appendData(data, length);
280 } else {
281 Resource::appendData(data, length);
282
283 // Update the image immediately if needed.
284 if (getContent()->shouldUpdateImageImmediately()) {
285 getContent()->updateImage(this->data(),
286 ImageResourceContent::KeepExistingImage, false);
287 return;
288 }
289
290 // For other cases, only update at |kFlushDelaySeconds| intervals. This
291 // throttles how frequently we update |m_image| and how frequently we
292 // inform the clients which causes an invalidation of this image. In other
293 // words, we only invalidate this image every |kFlushDelaySeconds| seconds
294 // while loading.
295 if (!m_flushTimer.isActive()) {
296 double now = WTF::monotonicallyIncreasingTime();
297 if (!m_lastFlushTime)
298 m_lastFlushTime = now;
299
300 DCHECK_LE(m_lastFlushTime, now);
301 double flushDelay = m_lastFlushTime - now + kFlushDelaySeconds;
302 if (flushDelay < 0.)
303 flushDelay = 0.;
304 m_flushTimer.startOneShot(flushDelay, BLINK_FROM_HERE);
305 }
306 }
307 }
308
309 void ImageResource::flushImageIfNeeded(TimerBase*) {
310 // We might have already loaded the image fully, in which case we don't need
311 // to call |updateImage()|.
312 if (isLoading()) {
313 m_lastFlushTime = WTF::monotonicallyIncreasingTime();
314 getContent()->updateImage(this->data(),
315 ImageResourceContent::KeepExistingImage, false);
316 }
317 }
318
319 bool ImageResource::willPaintBrokenImage() const {
320 return errorOccurred();
321 }
322
323 void ImageResource::decodeError(bool allDataReceived) {
324 size_t size = encodedSize();
325
326 clearData();
327 setEncodedSize(0);
328 if (!errorOccurred())
329 setStatus(DecodeError);
330
331 if (!allDataReceived && loader()) {
332 // TODO(hiroshige): Do not call didFinishLoading() directly.
333 loader()->didFinishLoading(monotonicallyIncreasingTime(), size, size);
334 }
335
336 memoryCache()->remove(this);
337 }
338
339 void ImageResource::updateImageAndClearBuffer() {
340 getContent()->updateImage(data(), ImageResourceContent::ClearExistingImage,
341 true);
342 clearData();
343 }
344
345 void ImageResource::finish(double loadFinishTime) {
346 if (m_multipartParser) {
347 m_multipartParser->finish();
348 if (data())
349 updateImageAndClearBuffer();
350 } else {
351 getContent()->updateImage(data(), ImageResourceContent::KeepExistingImage,
352 true);
353 // As encoded image data can be created from m_image (see
354 // ImageResource::resourceBuffer(), we don't have to keep m_data. Let's
355 // clear this. As for the lifetimes of m_image and m_data, see this
356 // document:
357 // https://docs.google.com/document/d/1v0yTAZ6wkqX2U_M6BNIGUJpM1s0TIw1Vsqpxo L7aciY/edit?usp=sharing
358 clearData();
359 }
360 Resource::finish(loadFinishTime);
361 }
362
363 void ImageResource::error(const ResourceError& error) {
364 if (m_multipartParser)
365 m_multipartParser->cancel();
366 // TODO(hiroshige): Move setEncodedSize() call to Resource::error() if it
367 // is really needed, or remove it otherwise.
368 setEncodedSize(0);
369 Resource::error(error);
370 getContent()->clearImageAndNotifyObservers(
371 ImageResourceContent::ShouldNotifyFinish);
372 }
373
374 void ImageResource::responseReceived(
375 const ResourceResponse& response,
376 std::unique_ptr<WebDataConsumerHandle> handle) {
377 DCHECK(!handle);
378 DCHECK(!m_multipartParser);
379 // If there's no boundary, just handle the request normally.
380 if (response.isMultipart() && !response.multipartBoundary().isEmpty()) {
381 m_multipartParser = new MultipartImageResourceParser(
382 response, response.multipartBoundary(), this);
383 }
384 Resource::responseReceived(response, std::move(handle));
385 if (RuntimeEnabledFeatures::clientHintsEnabled()) {
386 m_devicePixelRatioHeaderValue =
387 this->response()
388 .httpHeaderField(HTTPNames::Content_DPR)
389 .toFloat(&m_hasDevicePixelRatioHeaderValue);
390 if (!m_hasDevicePixelRatioHeaderValue ||
391 m_devicePixelRatioHeaderValue <= 0.0) {
392 m_devicePixelRatioHeaderValue = 1.0;
393 m_hasDevicePixelRatioHeaderValue = false;
394 }
395 }
396 }
397
398 static bool isLoFiImage(const ImageResource& resource) {
399 if (resource.resourceRequest().loFiState() != WebURLRequest::LoFiOn)
400 return false;
401 return !resource.isLoaded() ||
402 resource.response()
403 .httpHeaderField("chrome-proxy-content-transform")
404 .contains("empty-image");
405 }
406
407 void ImageResource::reloadIfLoFiOrPlaceholderImage(
408 ResourceFetcher* fetcher,
409 ReloadLoFiOrPlaceholderPolicy policy) {
410 if (policy == kReloadIfNeeded && !shouldReloadBrokenPlaceholder())
411 return;
412
413 if (!m_isPlaceholder && !isLoFiImage(*this))
414 return;
415
416 // Prevent clients and observers from being notified of completion while the
417 // reload is being scheduled, so that e.g. canceling an existing load in
418 // progress doesn't cause clients and observers to be notified of completion
419 // prematurely.
420 DCHECK(!m_isSchedulingReload);
421 m_isSchedulingReload = true;
422
423 if (policy != kReloadAlwaysWithExistingCachePolicy)
424 setCachePolicyBypassingCache();
425 setLoFiStateOff();
426
427 if (m_isPlaceholder) {
428 m_isPlaceholder = false;
429 clearRangeRequestHeader();
430 }
431
432 if (isLoading()) {
433 loader()->cancel();
434 // Canceling the loader causes error() to be called, which in turn calls
435 // clear() and notifyObservers(), so there's no need to call these again
436 // here.
437 } else {
438 clearData();
439 setEncodedSize(0);
440 getContent()->clearImageAndNotifyObservers(
441 ImageResourceContent::DoNotNotifyFinish);
442 }
443
444 setStatus(NotStarted);
445
446 DCHECK(m_isSchedulingReload);
447 m_isSchedulingReload = false;
448
449 fetcher->startLoad(this);
450 }
451
452 void ImageResource::onePartInMultipartReceived(
453 const ResourceResponse& response) {
454 DCHECK(m_multipartParser);
455
456 setResponse(response);
457 if (m_multipartParsingState == MultipartParsingState::WaitingForFirstPart) {
458 // We have nothing to do because we don't have any data.
459 m_multipartParsingState = MultipartParsingState::ParsingFirstPart;
460 return;
461 }
462 updateImageAndClearBuffer();
463
464 if (m_multipartParsingState == MultipartParsingState::ParsingFirstPart) {
465 m_multipartParsingState = MultipartParsingState::FinishedParsingFirstPart;
466 // Notify finished when the first part ends.
467 if (!errorOccurred())
468 setStatus(Cached);
469 // We notify clients and observers of finish in checkNotify() and
470 // updateImageAndClearBuffer(), respectively, and they will not be
471 // notified again in Resource::finish()/error().
472 checkNotify();
473 if (loader())
474 loader()->didFinishLoadingFirstPartInMultipart();
475 }
476 }
477
478 void ImageResource::multipartDataReceived(const char* bytes, size_t size) {
479 DCHECK(m_multipartParser);
480 Resource::appendData(bytes, size);
481 }
482
483 bool ImageResource::isAccessAllowed(
484 SecurityOrigin* securityOrigin,
485 ImageResourceInfo::DoesCurrentFrameHaveSingleSecurityOrigin
486 doesCurrentFrameHasSingleSecurityOrigin) const {
487 if (response().wasFetchedViaServiceWorker()) {
488 return response().serviceWorkerResponseType() !=
489 WebServiceWorkerResponseTypeOpaque;
490 }
491 if (doesCurrentFrameHasSingleSecurityOrigin !=
492 ImageResourceInfo::HasSingleSecurityOrigin)
493 return false;
494 if (passesAccessControlCheck(securityOrigin))
495 return true;
496 return !securityOrigin->taintsCanvas(response().url());
497 }
498
499 ImageResourceContent* ImageResource::getContent() {
500 return m_content;
501 }
502
503 const ImageResourceContent* ImageResource::getContent() const {
504 return m_content;
505 }
506
507 ResourcePriority ImageResource::priorityFromObservers() {
508 return getContent()->priorityFromObservers();
509 }
510
511 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698