blob: 3468197b950e3c21a534dda1d6fba383f1ee0818 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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/modules/notifications/notification_resources_loader.h"
#include <cmath>
#include "base/optional.h"
#include "base/time/time.h"
#include "third_party/blink/public/common/notifications/notification_constants.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/mojom/notifications/notification.mojom-blink.h"
#include "third_party/blink/public/platform/web_size.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/threading.h"
namespace blink {
namespace {
// 99.9% of all images were fetched successfully in 90 seconds.
constexpr base::TimeDelta kImageFetchTimeout = base::TimeDelta::FromSeconds(90);
enum class NotificationIconType { kImage, kIcon, kBadge, kActionIcon };
gfx::Size GetIconDimensions(NotificationIconType type) {
switch (type) {
case NotificationIconType::kImage:
return {kNotificationMaxImageWidthPx, kNotificationMaxImageHeightPx};
case NotificationIconType::kIcon:
return {kNotificationMaxIconSizePx, kNotificationMaxIconSizePx};
case NotificationIconType::kBadge:
return {kNotificationMaxBadgeSizePx, kNotificationMaxBadgeSizePx};
case NotificationIconType::kActionIcon:
return {kNotificationMaxActionIconSizePx,
kNotificationMaxActionIconSizePx};
}
}
} // namespace
NotificationResourcesLoader::NotificationResourcesLoader(
CompletionCallback completion_callback)
: started_(false),
completion_callback_(std::move(completion_callback)),
pending_request_count_(0) {
DCHECK(completion_callback_);
}
NotificationResourcesLoader::~NotificationResourcesLoader() = default;
void NotificationResourcesLoader::Start(
ExecutionContext* context,
const mojom::blink::NotificationData& notification_data) {
DCHECK(!started_);
started_ = true;
wtf_size_t num_actions = notification_data.actions.has_value()
? notification_data.actions->size()
: 0;
pending_request_count_ = 3 /* image, icon, badge */ + num_actions;
// TODO(johnme): ensure image is not loaded when it will not be used.
// TODO(mvanouwerkerk): ensure no badge is loaded when it will not be used.
LoadIcon(context, notification_data.image,
GetIconDimensions(NotificationIconType::kImage),
WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
WrapWeakPersistent(this), WTF::Unretained(&image_)));
LoadIcon(context, notification_data.icon,
GetIconDimensions(NotificationIconType::kIcon),
WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
WrapWeakPersistent(this), WTF::Unretained(&icon_)));
LoadIcon(context, notification_data.badge,
GetIconDimensions(NotificationIconType::kBadge),
WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
WrapWeakPersistent(this), WTF::Unretained(&badge_)));
action_icons_.Grow(num_actions);
for (wtf_size_t i = 0; i < num_actions; i++) {
LoadIcon(context, notification_data.actions.value()[i]->icon,
GetIconDimensions(NotificationIconType::kActionIcon),
WTF::Bind(&NotificationResourcesLoader::DidLoadIcon,
WrapWeakPersistent(this),
WTF::Unretained(&action_icons_[i])));
}
}
mojom::blink::NotificationResourcesPtr
NotificationResourcesLoader::GetResources() const {
auto resources = mojom::blink::NotificationResources::New();
resources->image = image_;
resources->icon = icon_;
resources->badge = badge_;
resources->action_icons = action_icons_;
return resources;
}
void NotificationResourcesLoader::Stop() {
for (const auto& icon_loader : icon_loaders_)
icon_loader->Stop();
}
void NotificationResourcesLoader::Trace(Visitor* visitor) const {
visitor->Trace(icon_loaders_);
}
void NotificationResourcesLoader::LoadIcon(
ExecutionContext* context,
const KURL& url,
const gfx::Size& resize_dimensions,
ThreadedIconLoader::IconCallback icon_callback) {
if (url.IsNull() || url.IsEmpty() || !url.IsValid()) {
std::move(icon_callback).Run(SkBitmap(), -1.0);
return;
}
ResourceRequest resource_request(url);
resource_request.SetRequestContext(mojom::blink::RequestContextType::IMAGE);
resource_request.SetRequestDestination(
network::mojom::RequestDestination::kImage);
resource_request.SetPriority(ResourceLoadPriority::kMedium);
resource_request.SetTimeoutInterval(kImageFetchTimeout);
auto* icon_loader = MakeGarbageCollected<ThreadedIconLoader>();
icon_loaders_.push_back(icon_loader);
icon_loader->Start(context, resource_request, resize_dimensions,
std::move(icon_callback));
}
void NotificationResourcesLoader::DidLoadIcon(SkBitmap* out_icon,
SkBitmap icon,
double resize_scale) {
*out_icon = std::move(icon);
DidFinishRequest();
}
void NotificationResourcesLoader::DidFinishRequest() {
DCHECK_GT(pending_request_count_, 0);
pending_request_count_--;
if (!pending_request_count_) {
Stop();
std::move(completion_callback_).Run(this);
// The |this| pointer may have been deleted now.
}
}
} // namespace blink