blob: 5e77eadc870c8bbdfcf2d09f689a0828b6701610 [file] [log] [blame]
/*
* Copyright (C) 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/css/css_image_set_value.h"
#include <algorithm>
#include "third_party/blink/renderer/core/css/css_image_set_option_value.h"
#include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
#include "third_party/blink/renderer/core/style/style_image_set.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
CSSImageSetValue::CSSImageSetValue()
: CSSValueList(kImageSetClass, kCommaSeparator) {}
CSSImageSetValue::~CSSImageSetValue() = default;
const CSSImageSetOptionValue* CSSImageSetValue::GetBestOption(
const float device_scale_factor) {
// This method is implementing the selection logic described in the
// "CSS Images Module Level 4" spec:
// https://w3c.github.io/csswg-drafts/css-images-4/#image-set-notation
//
// Spec definition of image-set-option selection algorithm:
//
// "An image-set() function contains a list of one or more
// <image-set-option>s, and must select only one of them
// to determine what image it will represent:
//
// 1. First, remove any <image-set-option>s from the list that specify an
// unknown or unsupported MIME type in their type() value.
// 2. Second, remove any <image-set-option>s from the list that have the
// same <resolution> as a previous option in the list.
// 3. Finally, among the remaining <image-set-option>s, make a UA-specific
// choice of which to load, based on whatever criteria deemed relevant
// (such as the resolution of the display, connection speed, etc).
// 4. The image-set() function then represents the <image> of the chosen
// <image-set-option>."
if (options_.empty()) {
for (const auto& i : *this) {
auto* option = To<CSSImageSetOptionValue>(i.Get());
if (option->IsSupported()) {
options_.push_back(option);
}
}
if (options_.empty()) {
// No supported options were identified in the image-set.
// As an optimization in order to avoid having to iterate
// through the unsupported options on subsequent calls,
// nullptr is inserted in the options_ vector.
options_.push_back(nullptr);
} else {
std::stable_sort(options_.begin(), options_.end(),
[](auto& left, auto& right) {
return left->ComputedResolution() <
right->ComputedResolution();
});
auto* last = std::unique(options_.begin(), options_.end(),
[](auto& left, auto& right) {
return left->ComputedResolution() ==
right->ComputedResolution();
});
options_.erase(last, options_.end());
}
}
for (const auto& option : options_) {
if (option && option->ComputedResolution() >= device_scale_factor) {
return option.Get();
}
}
return options_.back().Get();
}
bool CSSImageSetValue::IsCachePending(const float device_scale_factor) const {
return !cached_image_ ||
!EqualResolutions(device_scale_factor, cached_device_scale_factor_);
}
StyleImage* CSSImageSetValue::CachedImage(
const float device_scale_factor) const {
DCHECK(!IsCachePending(device_scale_factor));
return cached_image_.Get();
}
StyleImage* CSSImageSetValue::CacheImage(StyleImage* style_image,
const float device_scale_factor) {
cached_image_ = MakeGarbageCollected<StyleImageSet>(style_image, this);
cached_device_scale_factor_ = device_scale_factor;
return cached_image_.Get();
}
String CSSImageSetValue::CustomCSSText() const {
StringBuilder result;
result.Append("image-set(");
for (wtf_size_t i = 0, length = this->length(); i < length; ++i) {
if (i > 0) {
result.Append(", ");
}
result.Append(Item(i).CssText());
}
result.Append(')');
return result.ReleaseString();
}
bool CSSImageSetValue::HasFailedOrCanceledSubresources() const {
if (!cached_image_) {
return false;
}
if (ImageResourceContent* cached_content = cached_image_->CachedImage()) {
return cached_content->LoadFailedOrCanceled();
}
return true;
}
void CSSImageSetValue::TraceAfterDispatch(blink::Visitor* visitor) const {
visitor->Trace(cached_image_);
visitor->Trace(options_);
CSSValueList::TraceAfterDispatch(visitor);
}
} // namespace blink