[go: nahoru, domu]

blob: 0690ccbdcd5673f4e9c7f80c61303ad6ceb26f4e [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/loader/threaded_icon_loader.h"
#include <algorithm>
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "skia/ext/image_operations.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_data.h"
#include "third_party/blink/public/web/web_image.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
#include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
#include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/scheduler/public/main_thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_gfx.h"
#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
namespace blink {
namespace {
void DecodeSVGOnMainThread(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
scoped_refptr<SegmentReader> data,
gfx::Size resize_dimensions,
CrossThreadOnceFunction<void(SkBitmap, double)> done_callback) {
DCHECK(IsMainThread());
blink::WebData buffer(
reinterpret_cast<const char*>(std::move(data->GetAsSkData()->bytes())),
data->size());
SkBitmap icon = blink::WebImage::DecodeSVG(buffer, resize_dimensions);
if (icon.drawsNothing()) {
PostCrossThreadTask(
*task_runner, FROM_HERE,
CrossThreadBindOnce(std::move(done_callback), SkBitmap(), -1.0));
return;
}
PostCrossThreadTask(
*task_runner, FROM_HERE,
CrossThreadBindOnce(std::move(done_callback), std::move(icon), 1.0));
}
void DecodeAndResizeImage(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
scoped_refptr<SegmentReader> data,
gfx::Size resize_dimensions,
CrossThreadOnceFunction<void(SkBitmap, double)> done_callback) {
auto notify_complete = [&](SkBitmap icon, double resize_scale) {
// This is needed so it can be moved cross-thread.
icon.setImmutable();
PostCrossThreadTask(*task_runner, FROM_HERE,
CrossThreadBindOnce(std::move(done_callback),
std::move(icon), resize_scale));
};
std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
std::move(data), /*data_complete=*/true,
ImageDecoder::kAlphaPremultiplied, ImageDecoder::kDefaultBitDepth,
ColorBehavior::kTransformToSRGB, Platform::GetMaxDecodedImageBytes());
if (!decoder) {
notify_complete(SkBitmap(), -1.0);
return;
}
ImageFrame* image_frame = decoder->DecodeFrameBufferAtIndex(0);
if (!image_frame) {
notify_complete(SkBitmap(), -1.0);
return;
}
SkBitmap decoded_icon = image_frame->Bitmap();
if (resize_dimensions.IsEmpty()) {
notify_complete(std::move(decoded_icon), 1.0);
return;
}
// If the icon is larger than |resize_dimensions| permits, we need to
// resize it as well. This can be done synchronously given that we're on a
// background thread already.
double scale = std::min(
static_cast<double>(resize_dimensions.width()) / decoded_icon.width(),
static_cast<double>(resize_dimensions.height()) / decoded_icon.height());
if (scale >= 1.0) {
notify_complete(std::move(decoded_icon), 1.0);
return;
}
int resized_width = std::clamp(static_cast<int>(scale * decoded_icon.width()),
1, resize_dimensions.width());
int resized_height =
std::clamp(static_cast<int>(scale * decoded_icon.height()), 1,
resize_dimensions.height());
// Use the RESIZE_GOOD quality allowing the implementation to pick an
// appropriate method for the resize. Can be increased to RESIZE_BETTER
// or RESIZE_BEST if the quality looks poor.
SkBitmap resized_icon = skia::ImageOperations::Resize(
decoded_icon, skia::ImageOperations::RESIZE_GOOD, resized_width,
resized_height);
if (resized_icon.isNull()) {
notify_complete(std::move(decoded_icon), 1.0);
return;
}
notify_complete(std::move(resized_icon), scale);
}
} // namespace
void ThreadedIconLoader::Start(
ExecutionContext* execution_context,
const ResourceRequestHead& resource_request,
const std::optional<gfx::Size>& resize_dimensions,
IconCallback callback) {
DCHECK(!stopped_);
DCHECK(resource_request.Url().IsValid());
DCHECK_EQ(resource_request.GetRequestContext(),
mojom::blink::RequestContextType::IMAGE);
DCHECK(!icon_callback_);
icon_callback_ = std::move(callback);
resize_dimensions_ = resize_dimensions;
ResourceLoaderOptions resource_loader_options(
execution_context->GetCurrentWorld());
threadable_loader_ = MakeGarbageCollected<ThreadableLoader>(
*execution_context, this, resource_loader_options);
threadable_loader_->SetTimeout(resource_request.TimeoutInterval());
threadable_loader_->Start(ResourceRequest(resource_request));
}
void ThreadedIconLoader::Stop() {
stopped_ = true;
if (threadable_loader_) {
threadable_loader_->Cancel();
threadable_loader_ = nullptr;
}
}
void ThreadedIconLoader::DidReceiveResponse(uint64_t,
const ResourceResponse& response) {
response_mime_type_ = response.MimeType();
}
void ThreadedIconLoader::DidReceiveData(const char* data, unsigned length) {
if (!data_)
data_ = SharedBuffer::Create();
data_->Append(data, length);
}
void ThreadedIconLoader::DidFinishLoading(uint64_t resource_identifier) {
if (stopped_)
return;
if (!data_) {
std::move(icon_callback_).Run(SkBitmap(), -1);
return;
}
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
threadable_loader_->GetTaskRunner();
if (response_mime_type_ == "image/svg+xml") {
PostCrossThreadTask(
*Thread::MainThread()->GetTaskRunner(MainThreadTaskRunnerRestricted()),
FROM_HERE,
CrossThreadBindOnce(
&DecodeSVGOnMainThread, std::move(task_runner),
SegmentReader::CreateFromSharedBuffer(std::move(data_)),
resize_dimensions_ ? *resize_dimensions_ : gfx::Size(),
CrossThreadBindOnce(&ThreadedIconLoader::OnBackgroundTaskComplete,
WrapCrossThreadWeakPersistent(this))));
return;
}
worker_pool::PostTask(
FROM_HERE,
CrossThreadBindOnce(
&DecodeAndResizeImage, std::move(task_runner),
SegmentReader::CreateFromSharedBuffer(std::move(data_)),
resize_dimensions_ ? *resize_dimensions_ : gfx::Size(),
CrossThreadBindOnce(&ThreadedIconLoader::OnBackgroundTaskComplete,
WrapCrossThreadWeakPersistent(this))));
}
void ThreadedIconLoader::OnBackgroundTaskComplete(SkBitmap icon,
double resize_scale) {
if (stopped_)
return;
std::move(icon_callback_).Run(std::move(icon), resize_scale);
}
void ThreadedIconLoader::DidFail(uint64_t, const ResourceError& error) {
if (stopped_)
return;
std::move(icon_callback_).Run(SkBitmap(), -1);
}
void ThreadedIconLoader::DidFailRedirectCheck(uint64_t) {
if (stopped_)
return;
std::move(icon_callback_).Run(SkBitmap(), -1);
}
void ThreadedIconLoader::Trace(Visitor* visitor) const {
visitor->Trace(threadable_loader_);
ThreadableLoaderClient::Trace(visitor);
}
} // namespace blink