blob: 37dac6e1f0363fbc510a9206452fb58f30a9269a [file] [log] [blame]
/*
Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "core/loader/resource/ImageResource.h"
#include <stdint.h>
#include <v8.h>
#include <memory>
#include "core/loader/resource/ImageResourceContent.h"
#include "core/loader/resource/ImageResourceInfo.h"
#include "platform/Histogram.h"
#include "platform/InstanceCounters.h"
#include "platform/SharedBuffer.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/loader/fetch/FetchParameters.h"
#include "platform/loader/fetch/MemoryCache.h"
#include "platform/loader/fetch/ResourceClient.h"
#include "platform/loader/fetch/ResourceFetcher.h"
#include "platform/loader/fetch/ResourceLoader.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/loader/fetch/ResourceLoadingLog.h"
#include "platform/loader/fetch/fetch_initiator_type_names.h"
#include "platform/network/HTTPParsers.h"
#include "platform/runtime_enabled_features.h"
#include "platform/weborigin/KURL.h"
#include "platform/weborigin/SecurityViolationReportingPolicy.h"
#include "platform/wtf/StdLibExtras.h"
#include "platform/wtf/Time.h"
#include "public/platform/Platform.h"
#include "v8/include/v8.h"
namespace blink {
namespace {
// The amount of time to wait before informing the clients that the image has
// been updated (in seconds). This effectively throttles invalidations that
// result from new data arriving for this image.
constexpr double kFlushDelaySeconds = 1.;
bool HasServerLoFiResponseHeaders(const ResourceResponse& response) {
return response.HttpHeaderField("chrome-proxy-content-transform")
.Contains("empty-image") ||
// Check for the legacy Server Lo-Fi response headers, since it's
// possible that an old Lo-Fi image could be served from the cache.
response.HttpHeaderField("chrome-proxy").Contains("q=low");
}
} // namespace
class ImageResource::ImageResourceInfoImpl final
: public GarbageCollectedFinalized<ImageResourceInfoImpl>,
public ImageResourceInfo {
USING_GARBAGE_COLLECTED_MIXIN(ImageResourceInfoImpl);
public:
ImageResourceInfoImpl(ImageResource* resource) : resource_(resource) {
DCHECK(resource_);
}
void Trace(blink::Visitor* visitor) override {
visitor->Trace(resource_);
ImageResourceInfo::Trace(visitor);
}
private:
const KURL& Url() const override { return resource_->Url(); }
bool IsSchedulingReload() const override {
return resource_->is_scheduling_reload_;
}
bool HasDevicePixelRatioHeaderValue() const override {
return resource_->has_device_pixel_ratio_header_value_;
}
float DevicePixelRatioHeaderValue() const override {
return resource_->device_pixel_ratio_header_value_;
}
const ResourceResponse& GetResponse() const override {
return resource_->GetResponse();
}
bool ShouldShowPlaceholder() const override {
return resource_->ShouldShowPlaceholder();
}
bool IsCacheValidator() const override {
return resource_->IsCacheValidator();
}
bool SchedulingReloadOrShouldReloadBrokenPlaceholder() const override {
return resource_->is_scheduling_reload_ ||
resource_->ShouldReloadBrokenPlaceholder();
}
bool IsAccessAllowed(
const SecurityOrigin* security_origin,
DoesCurrentFrameHaveSingleSecurityOrigin
does_current_frame_has_single_security_origin) const override {
return resource_->IsAccessAllowed(
security_origin, does_current_frame_has_single_security_origin);
}
bool HasCacheControlNoStoreHeader() const override {
return resource_->HasCacheControlNoStoreHeader();
}
Optional<ResourceError> GetResourceError() const override {
if (resource_->LoadFailedOrCanceled())
return resource_->GetResourceError();
return WTF::nullopt;
}
void SetDecodedSize(size_t size) override { resource_->SetDecodedSize(size); }
void WillAddClientOrObserver() override {
resource_->WillAddClientOrObserver();
}
void DidRemoveClientOrObserver() override {
resource_->DidRemoveClientOrObserver();
}
void EmulateLoadStartedForInspector(
ResourceFetcher* fetcher,
const KURL& url,
const AtomicString& initiator_name) override {
fetcher->EmulateLoadStartedForInspector(resource_.Get(), url,
WebURLRequest::kRequestContextImage,
initiator_name);
}
const Member<ImageResource> resource_;
};
class ImageResource::ImageResourceFactory : public NonTextResourceFactory {
STACK_ALLOCATED();
public:
ImageResourceFactory(const FetchParameters& fetch_params)
: NonTextResourceFactory(Resource::kImage),
fetch_params_(&fetch_params) {}
Resource* Create(const ResourceRequest& request,
const ResourceLoaderOptions& options) const override {
return new ImageResource(request, options,
ImageResourceContent::CreateNotStarted(),
fetch_params_->GetPlaceholderImageRequestType() ==
FetchParameters::kAllowPlaceholder);
}
private:
// Weak, unowned pointer. Must outlive |this|.
const FetchParameters* fetch_params_;
};
ImageResource* ImageResource::Fetch(FetchParameters& params,
ResourceFetcher* fetcher) {
if (params.GetResourceRequest().GetRequestContext() ==
WebURLRequest::kRequestContextUnspecified) {
params.SetRequestContext(WebURLRequest::kRequestContextImage);
}
ImageResource* resource = ToImageResource(
fetcher->RequestResource(params, ImageResourceFactory(params), nullptr));
// If the fetch originated from user agent CSS we should mark it as a user
// agent resource.
if (params.Options().initiator_info.name == FetchInitiatorTypeNames::uacss)
resource->FlagAsUserAgentResource();
return resource;
}
bool ImageResource::CanReuse(const FetchParameters& params) const {
// If the image is a placeholder, but this fetch doesn't allow a
// placeholder, then do not reuse this resource.
if (params.GetPlaceholderImageRequestType() !=
FetchParameters::kAllowPlaceholder &&
placeholder_option_ != PlaceholderOption::kDoNotReloadPlaceholder)
return false;
return Resource::CanReuse(params);
}
bool ImageResource::CanUseCacheValidator() const {
// Disable revalidation while ImageResourceContent is still waiting for
// SVG load completion.
// TODO(hiroshige): Clean up revalidation-related dependencies.
if (!GetContent()->IsLoaded())
return false;
return Resource::CanUseCacheValidator();
}
ImageResource* ImageResource::Create(const ResourceRequest& request) {
ResourceLoaderOptions options;
return new ImageResource(request, options,
ImageResourceContent::CreateNotStarted(), false);
}
ImageResource* ImageResource::CreateForTest(const KURL& url) {
ResourceRequest request(url);
return Create(request);
}
ImageResource::ImageResource(const ResourceRequest& resource_request,
const ResourceLoaderOptions& options,
ImageResourceContent* content,
bool is_placeholder)
: Resource(resource_request, kImage, options),
content_(content),
device_pixel_ratio_header_value_(1.0),
has_device_pixel_ratio_header_value_(false),
is_scheduling_reload_(false),
placeholder_option_(
is_placeholder ? PlaceholderOption::kShowAndReloadPlaceholderAlways
: PlaceholderOption::kDoNotReloadPlaceholder),
flush_timer_(this, &ImageResource::FlushImageIfNeeded) {
DCHECK(GetContent());
RESOURCE_LOADING_DVLOG(1) << "new ImageResource(ResourceRequest) " << this;
GetContent()->SetImageResourceInfo(new ImageResourceInfoImpl(this));
}
ImageResource::~ImageResource() {
RESOURCE_LOADING_DVLOG(1) << "~ImageResource " << this;
if (is_referenced_from_ua_stylesheet_)
InstanceCounters::DecrementCounter(InstanceCounters::kUACSSResourceCounter);
}
void ImageResource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail,
WebProcessMemoryDump* memory_dump) const {
Resource::OnMemoryDump(level_of_detail, memory_dump);
const String name = GetMemoryDumpName() + "/image_content";
auto* dump = memory_dump->CreateMemoryAllocatorDump(name);
size_t encoded_size =
content_->HasImage() ? content_->GetImage()->Data()->size() : 0;
dump->AddScalar("size", "bytes", encoded_size);
}
void ImageResource::Trace(blink::Visitor* visitor) {
visitor->Trace(multipart_parser_);
visitor->Trace(content_);
Resource::Trace(visitor);
MultipartImageResourceParser::Client::Trace(visitor);
}
void ImageResource::NotifyFinished() {
// Don't notify clients of completion if this ImageResource is
// about to be reloaded.
if (is_scheduling_reload_ || ShouldReloadBrokenPlaceholder())
return;
Resource::NotifyFinished();
}
bool ImageResource::HasClientsOrObservers() const {
return Resource::HasClientsOrObservers() || GetContent()->HasObservers();
}
void ImageResource::DidAddClient(ResourceClient* client) {
DCHECK((multipart_parser_ && IsLoading()) || !Data() ||
GetContent()->HasImage());
// Don't notify observers and clients of completion if this ImageResource is
// about to be reloaded.
if (is_scheduling_reload_ || ShouldReloadBrokenPlaceholder())
return;
Resource::DidAddClient(client);
}
void ImageResource::DestroyDecodedDataForFailedRevalidation() {
// Clears the image, as we must create a new image for the failed
// revalidation response.
UpdateImage(nullptr, ImageResourceContent::kClearAndUpdateImage, false);
SetDecodedSize(0);
}
void ImageResource::DestroyDecodedDataIfPossible() {
GetContent()->DestroyDecodedData();
if (GetContent()->HasImage() && !IsUnusedPreload() &&
GetContent()->IsRefetchableDataFromDiskCache()) {
UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer.EstimatedDroppableEncodedSize",
EncodedSize() / 1024);
}
}
void ImageResource::AllClientsAndObserversRemoved() {
// After ErrorOccurred() is set true in Resource::FinishAsError() before
// the subsequent UpdateImage() in ImageResource::FinishAsError(),
// HasImage() is true and ErrorOccurred() is true.
// |is_during_finish_as_error_| is introduced to allow such cases.
// crbug.com/701723
// TODO(hiroshige): Make the CHECK condition cleaner.
CHECK(is_during_finish_as_error_ || !GetContent()->HasImage() ||
!ErrorOccurred());
// If possible, delay the resetting until back at the event loop. Doing so
// after a conservative GC prevents resetAnimation() from upsetting ongoing
// animation updates (crbug.com/613709)
if (!ThreadHeap::WillObjectBeLazilySwept(this)) {
Platform::Current()->CurrentThread()->GetWebTaskRunner()->PostTask(
FROM_HERE, WTF::Bind(&ImageResourceContent::DoResetAnimation,
WrapWeakPersistent(GetContent())));
} else {
GetContent()->DoResetAnimation();
}
if (multipart_parser_)
multipart_parser_->Cancel();
Resource::AllClientsAndObserversRemoved();
}
scoped_refptr<const SharedBuffer> ImageResource::ResourceBuffer() const {
if (Data())
return Data();
return GetContent()->ResourceBuffer();
}
void ImageResource::AppendData(const char* data, size_t length) {
v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(length);
if (multipart_parser_) {
multipart_parser_->AppendData(data, length);
} else {
Resource::AppendData(data, length);
// Update the image immediately if needed.
if (GetContent()->ShouldUpdateImageImmediately()) {
UpdateImage(Data(), ImageResourceContent::kUpdateImage, false);
return;
}
// For other cases, only update at |kFlushDelaySeconds| intervals. This
// throttles how frequently we update |m_image| and how frequently we
// inform the clients which causes an invalidation of this image. In other
// words, we only invalidate this image every |kFlushDelaySeconds| seconds
// while loading.
if (!flush_timer_.IsActive()) {
double now = WTF::CurrentTimeTicksInSeconds();
if (!last_flush_time_)
last_flush_time_ = now;
DCHECK_LE(last_flush_time_, now);
double flush_delay = last_flush_time_ - now + kFlushDelaySeconds;
if (flush_delay < 0.)
flush_delay = 0.;
flush_timer_.StartOneShot(flush_delay, FROM_HERE);
}
}
}
void ImageResource::FlushImageIfNeeded(TimerBase*) {
// We might have already loaded the image fully, in which case we don't need
// to call |updateImage()|.
if (IsLoading()) {
last_flush_time_ = WTF::CurrentTimeTicksInSeconds();
UpdateImage(Data(), ImageResourceContent::kUpdateImage, false);
}
}
void ImageResource::DecodeError(bool all_data_received) {
size_t size = EncodedSize();
ClearData();
SetEncodedSize(0);
if (!ErrorOccurred())
SetStatus(ResourceStatus::kDecodeError);
bool is_multipart = !!multipart_parser_;
// Finishes loading if needed, and notifies observers.
if (!all_data_received && Loader()) {
// Observers are notified via ImageResource::finish().
// TODO(hiroshige): Do not call didFinishLoading() directly.
Loader()->DidFinishLoading(CurrentTimeTicksInSeconds(), size, size, size,
false);
} else {
auto result = GetContent()->UpdateImage(
nullptr, GetStatus(),
ImageResourceContent::kClearImageAndNotifyObservers, all_data_received,
is_multipart);
DCHECK_EQ(result, ImageResourceContent::UpdateImageResult::kNoDecodeError);
}
GetMemoryCache()->Remove(this);
}
void ImageResource::UpdateImageAndClearBuffer() {
UpdateImage(Data(), ImageResourceContent::kClearAndUpdateImage, true);
ClearData();
}
void ImageResource::NotifyStartLoad() {
CHECK_EQ(GetStatus(), ResourceStatus::kPending);
GetContent()->NotifyStartLoad();
}
void ImageResource::Finish(double load_finish_time,
base::SingleThreadTaskRunner* task_runner) {
if (multipart_parser_) {
multipart_parser_->Finish();
if (Data())
UpdateImageAndClearBuffer();
} else {
UpdateImage(Data(), ImageResourceContent::kUpdateImage, true);
// As encoded image data can be created from m_image (see
// ImageResource::resourceBuffer(), we don't have to keep m_data. Let's
// clear this. As for the lifetimes of m_image and m_data, see this
// document:
// https://docs.google.com/document/d/1v0yTAZ6wkqX2U_M6BNIGUJpM1s0TIw1VsqpxoL7aciY/edit?usp=sharing
ClearData();
}
Resource::Finish(load_finish_time, task_runner);
}
void ImageResource::FinishAsError(const ResourceError& error,
base::SingleThreadTaskRunner* task_runner) {
if (multipart_parser_)
multipart_parser_->Cancel();
// TODO(hiroshige): Move setEncodedSize() call to Resource::error() if it
// is really needed, or remove it otherwise.
SetEncodedSize(0);
is_during_finish_as_error_ = true;
Resource::FinishAsError(error, task_runner);
is_during_finish_as_error_ = false;
UpdateImage(nullptr, ImageResourceContent::kClearImageAndNotifyObservers,
true);
}
// Determines if |response| likely contains the entire resource for the purposes
// of determining whether or not to show a placeholder, e.g. if the server
// responded with a full 200 response or if the full image is smaller than the
// requested range.
static bool IsEntireResource(const ResourceResponse& response) {
if (response.HttpStatusCode() != 206)
return true;
int64_t first_byte_position = -1, last_byte_position = -1,
instance_length = -1;
return ParseContentRangeHeaderFor206(
response.HttpHeaderField("Content-Range"), &first_byte_position,
&last_byte_position, &instance_length) &&
first_byte_position == 0 && last_byte_position + 1 == instance_length;
}
void ImageResource::ResponseReceived(
const ResourceResponse& response,
std::unique_ptr<WebDataConsumerHandle> handle) {
DCHECK(!handle);
DCHECK(!multipart_parser_);
// If there's no boundary, just handle the request normally.
if (response.IsMultipart() && !response.MultipartBoundary().IsEmpty()) {
multipart_parser_ = new MultipartImageResourceParser(
response, response.MultipartBoundary(), this);
}
// Notify the base class that a response has been received. Note that after
// this call, |GetResponse()| will represent the full effective
// ResourceResponse, while |response| might just be a revalidation response
// (e.g. a 304) with a partial set of updated headers that were folded into
// the cached response.
Resource::ResponseReceived(response, std::move(handle));
device_pixel_ratio_header_value_ =
GetResponse()
.HttpHeaderField(HTTPNames::Content_DPR)
.ToFloat(&has_device_pixel_ratio_header_value_);
if (!has_device_pixel_ratio_header_value_ ||
device_pixel_ratio_header_value_ <= 0.0) {
device_pixel_ratio_header_value_ = 1.0;
has_device_pixel_ratio_header_value_ = false;
}
if (placeholder_option_ ==
PlaceholderOption::kShowAndReloadPlaceholderAlways &&
IsEntireResource(GetResponse())) {
if (GetResponse().HttpStatusCode() < 400 ||
GetResponse().HttpStatusCode() >= 600) {
// Don't treat a complete and broken image as a placeholder if the
// response code is something other than a 4xx or 5xx error.
// This is done to prevent reissuing the request in cases like
// "204 No Content" responses to tracking requests triggered by <img>
// tags, and <img> tags used to preload non-image resources.
placeholder_option_ = PlaceholderOption::kDoNotReloadPlaceholder;
} else {
placeholder_option_ = PlaceholderOption::kReloadPlaceholderOnDecodeError;
}
}
if (HasServerLoFiResponseHeaders(GetResponse())) {
// Ensure that the PreviewsState bit for Server Lo-Fi is set iff Chrome
// received the appropriate Server Lo-Fi response headers for this image.
//
// Normally, the |kServerLoFiOn| bit should already be set if Server Lo-Fi
// response headers are coming back, but it's possible for legacy Lo-Fi
// images to be served from the cache even if Chrome isn't in Lo-Fi mode.
// This also serves as a nice last line of defence to ensure that Server
// Lo-Fi images can be reloaded to show the original even if e.g. a server
// bug causes Lo-Fi images to be sent when they aren't expected.
SetPreviewsState(GetResourceRequest().GetPreviewsState() |
WebURLRequest::kServerLoFiOn);
} else if (GetResourceRequest().GetPreviewsState() &
WebURLRequest::kServerLoFiOn) {
// If Chrome expects a Lo-Fi response, but the server decided to send the
// full image, then clear the Server Lo-Fi Previews state bit.
WebURLRequest::PreviewsState new_previews_state =
GetResourceRequest().GetPreviewsState();
new_previews_state &= ~WebURLRequest::kServerLoFiOn;
if (new_previews_state == WebURLRequest::kPreviewsUnspecified)
new_previews_state = WebURLRequest::kPreviewsOff;
SetPreviewsState(new_previews_state);
}
}
bool ImageResource::ShouldShowPlaceholder() const {
if (RuntimeEnabledFeatures::ClientPlaceholdersForServerLoFiEnabled() &&
(GetResourceRequest().GetPreviewsState() &
WebURLRequest::kServerLoFiOn)) {
// If the runtime feature is enabled, show Client Lo-Fi placeholder images
// in place of Server Lo-Fi responses. This is done so that all Lo-Fi images
// have a consistent appearance.
return true;
}
switch (placeholder_option_) {
case PlaceholderOption::kShowAndReloadPlaceholderAlways:
case PlaceholderOption::kShowAndDoNotReloadPlaceholder:
return true;
case PlaceholderOption::kReloadPlaceholderOnDecodeError:
case PlaceholderOption::kDoNotReloadPlaceholder:
return false;
}
NOTREACHED();
return false;
}
bool ImageResource::ShouldReloadBrokenPlaceholder() const {
switch (placeholder_option_) {
case PlaceholderOption::kShowAndReloadPlaceholderAlways:
return ErrorOccurred();
case PlaceholderOption::kReloadPlaceholderOnDecodeError:
return GetStatus() == ResourceStatus::kDecodeError;
case PlaceholderOption::kShowAndDoNotReloadPlaceholder:
case PlaceholderOption::kDoNotReloadPlaceholder:
return false;
}
NOTREACHED();
return false;
}
void ImageResource::ReloadIfLoFiOrPlaceholderImage(
ResourceFetcher* fetcher,
ReloadLoFiOrPlaceholderPolicy policy) {
if (policy == kReloadIfNeeded && !ShouldReloadBrokenPlaceholder())
return;
// If the image is loaded, then the |PreviewsState::kServerLoFiOn| bit should
// be set iff the image has Server Lo-Fi response headers.
DCHECK(!IsLoaded() ||
HasServerLoFiResponseHeaders(GetResponse()) ==
static_cast<bool>(GetResourceRequest().GetPreviewsState() &
WebURLRequest::kServerLoFiOn));
if (placeholder_option_ == PlaceholderOption::kDoNotReloadPlaceholder &&
!(GetResourceRequest().GetPreviewsState() & WebURLRequest::kServerLoFiOn))
return;
// Prevent clients and observers from being notified of completion while the
// reload is being scheduled, so that e.g. canceling an existing load in
// progress doesn't cause clients and observers to be notified of completion
// prematurely.
DCHECK(!is_scheduling_reload_);
is_scheduling_reload_ = true;
SetCachePolicyBypassingCache();
// The reloaded image should not use any previews transformations.
WebURLRequest::PreviewsState previews_state_for_reload =
WebURLRequest::kPreviewsNoTransform;
WebURLRequest::PreviewsState old_previews_state =
GetResourceRequest().GetPreviewsState();
if (policy == kReloadIfNeeded && (GetResourceRequest().GetPreviewsState() &
WebURLRequest::kClientLoFiOn)) {
// If the image attempted to use Client LoFi, but encountered a decoding
// error and is being automatically reloaded, then also set the appropriate
// PreviewsState bit for that. This allows the embedder to count the
// bandwidth used for this reload against the data savings of the initial
// response.
previews_state_for_reload |= WebURLRequest::kClientLoFiAutoReload;
}
SetPreviewsState(previews_state_for_reload);
if (placeholder_option_ != PlaceholderOption::kDoNotReloadPlaceholder)
ClearRangeRequestHeader();
if (old_previews_state & WebURLRequest::kClientLoFiOn &&
policy != kReloadAlways) {
placeholder_option_ = PlaceholderOption::kShowAndDoNotReloadPlaceholder;
} else {
placeholder_option_ = PlaceholderOption::kDoNotReloadPlaceholder;
}
if (IsLoading()) {
Loader()->Cancel();
// Canceling the loader causes error() to be called, which in turn calls
// clear() and notifyObservers(), so there's no need to call these again
// here.
} else {
ClearData();
SetEncodedSize(0);
UpdateImage(nullptr, ImageResourceContent::kClearImageAndNotifyObservers,
false);
}
SetStatus(ResourceStatus::kNotStarted);
DCHECK(is_scheduling_reload_);
is_scheduling_reload_ = false;
fetcher->StartLoad(this);
}
void ImageResource::OnePartInMultipartReceived(
const ResourceResponse& response) {
DCHECK(multipart_parser_);
SetResponse(response);
if (multipart_parsing_state_ == MultipartParsingState::kWaitingForFirstPart) {
// We have nothing to do because we don't have any data.
multipart_parsing_state_ = MultipartParsingState::kParsingFirstPart;
return;
}
UpdateImageAndClearBuffer();
if (multipart_parsing_state_ == MultipartParsingState::kParsingFirstPart) {
multipart_parsing_state_ = MultipartParsingState::kFinishedParsingFirstPart;
// Notify finished when the first part ends.
if (!ErrorOccurred())
SetStatus(ResourceStatus::kCached);
// We notify clients and observers of finish in checkNotify() and
// updateImageAndClearBuffer(), respectively, and they will not be
// notified again in Resource::finish()/error().
NotifyFinished();
if (Loader())
Loader()->DidFinishLoadingFirstPartInMultipart();
}
}
void ImageResource::MultipartDataReceived(const char* bytes, size_t size) {
DCHECK(multipart_parser_);
Resource::AppendData(bytes, size);
}
bool ImageResource::IsAccessAllowed(
const SecurityOrigin* security_origin,
ImageResourceInfo::DoesCurrentFrameHaveSingleSecurityOrigin
does_current_frame_has_single_security_origin) const {
if (GetResponse().WasFetchedViaServiceWorker())
return GetCORSStatus() != CORSStatus::kServiceWorkerOpaque;
if (does_current_frame_has_single_security_origin !=
ImageResourceInfo::kHasSingleSecurityOrigin)
return false;
DCHECK(security_origin);
if (PassesAccessControlCheck(*security_origin))
return true;
return !security_origin->TaintsCanvas(GetResponse().Url());
}
ImageResourceContent* ImageResource::GetContent() {
return content_;
}
const ImageResourceContent* ImageResource::GetContent() const {
return content_;
}
ResourcePriority ImageResource::PriorityFromObservers() {
return GetContent()->PriorityFromObservers();
}
void ImageResource::UpdateImage(
scoped_refptr<SharedBuffer> shared_buffer,
ImageResourceContent::UpdateImageOption update_image_option,
bool all_data_received) {
bool is_multipart = !!multipart_parser_;
auto result = GetContent()->UpdateImage(std::move(shared_buffer), GetStatus(),
update_image_option,
all_data_received, is_multipart);
if (result == ImageResourceContent::UpdateImageResult::kShouldDecodeError) {
// In case of decode error, we call imageNotifyFinished() iff we don't
// initiate reloading:
// [(a): when this is in the middle of loading, or (b): otherwise]
// 1. The updateImage() call above doesn't call notifyObservers().
// 2. notifyObservers(ShouldNotifyFinish) is called
// (a) via updateImage() called in ImageResource::finish()
// called via didFinishLoading() called in decodeError(), or
// (b) via updateImage() called in decodeError().
// imageNotifyFinished() is called here iff we will not initiate
// reloading in Step 3 due to notifyObservers()'s
// schedulingReloadOrShouldReloadBrokenPlaceholder() check.
// 3. reloadIfLoFiOrPlaceholderImage() is called via ResourceFetcher
// (a) via didFinishLoading() called in decodeError(), or
// (b) after returning ImageResource::updateImage().
DecodeError(all_data_received);
}
}
void ImageResource::FlagAsUserAgentResource() {
if (is_referenced_from_ua_stylesheet_)
return;
InstanceCounters::IncrementCounter(InstanceCounters::kUACSSResourceCounter);
is_referenced_from_ua_stylesheet_ = true;
}
} // namespace blink