blob: b6b50766041331c7a9ee78846b1af508c42b7a95 [file] [log] [blame]
/*
* Copyright (C) 2012-2013 Intel Corporation. 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 THE COPYRIGHT HOLDER "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 THE COPYRIGHT HOLDER 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 "core/css/resolver/ViewportStyleResolver.h"
#include "core/CSSValueKeywords.h"
#include "core/css/CSSDefaultStyleSheets.h"
#include "core/css/CSSPrimitiveValueMappings.h"
#include "core/css/CSSPropertyValueSet.h"
#include "core/css/CSSStyleSheet.h"
#include "core/css/CSSToLengthConversionData.h"
#include "core/css/DocumentStyleSheetCollection.h"
#include "core/css/MediaValuesInitialViewport.h"
#include "core/css/StyleRule.h"
#include "core/css/StyleRuleImport.h"
#include "core/css/StyleSheetContents.h"
#include "core/dom/Document.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/dom/ViewportDescription.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/Settings.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
namespace blink {
ViewportStyleResolver::ViewportStyleResolver(Document& document)
: document_(document) {
DCHECK(document.GetFrame());
initial_viewport_medium_ = new MediaQueryEvaluator(
MediaValuesInitialViewport::Create(*document.GetFrame()));
}
void ViewportStyleResolver::Reset() {
viewport_dependent_media_query_results_.clear();
device_dependent_media_query_results_.clear();
property_set_ = nullptr;
has_author_style_ = false;
has_viewport_units_ = false;
needs_update_ = kNoUpdate;
}
void ViewportStyleResolver::CollectViewportRulesFromUASheets() {
CSSDefaultStyleSheets& default_style_sheets =
CSSDefaultStyleSheets::Instance();
WebViewportStyle viewport_style =
document_->GetSettings() ? document_->GetSettings()->GetViewportStyle()
: WebViewportStyle::kDefault;
StyleSheetContents* viewport_contents = nullptr;
switch (viewport_style) {
case WebViewportStyle::kDefault:
break;
case WebViewportStyle::kMobile:
viewport_contents = default_style_sheets.EnsureMobileViewportStyleSheet();
break;
case WebViewportStyle::kTelevision:
viewport_contents =
default_style_sheets.EnsureTelevisionViewportStyleSheet();
break;
}
if (viewport_contents)
CollectViewportChildRules(viewport_contents->ChildRules(),
kUserAgentOrigin);
if (document_->IsMobileDocument()) {
CollectViewportChildRules(
default_style_sheets.EnsureXHTMLMobileProfileStyleSheet()->ChildRules(),
kUserAgentOrigin);
}
DCHECK(!default_style_sheets.DefaultStyleSheet()->HasViewportRule());
}
void ViewportStyleResolver::CollectViewportChildRules(
const HeapVector<Member<StyleRuleBase>>& rules,
Origin origin) {
for (auto& rule : rules) {
if (rule->IsViewportRule()) {
AddViewportRule(*ToStyleRuleViewport(rule), origin);
} else if (rule->IsMediaRule()) {
StyleRuleMedia* media_rule = ToStyleRuleMedia(rule);
if (!media_rule->MediaQueries() ||
initial_viewport_medium_->Eval(
*media_rule->MediaQueries(),
&viewport_dependent_media_query_results_,
&device_dependent_media_query_results_))
CollectViewportChildRules(media_rule->ChildRules(), origin);
} else if (rule->IsSupportsRule()) {
StyleRuleSupports* supports_rule = ToStyleRuleSupports(rule);
if (supports_rule->ConditionIsSupported())
CollectViewportChildRules(supports_rule->ChildRules(), origin);
}
}
}
void ViewportStyleResolver::CollectViewportRulesFromImports(
StyleSheetContents& contents) {
for (const auto& import_rule : contents.ImportRules()) {
if (!import_rule->GetStyleSheet())
continue;
if (!import_rule->GetStyleSheet()->HasViewportRule())
continue;
if (import_rule->MediaQueries() &&
initial_viewport_medium_->Eval(*import_rule->MediaQueries(),
&viewport_dependent_media_query_results_,
&device_dependent_media_query_results_))
CollectViewportRulesFromAuthorSheetContents(
*import_rule->GetStyleSheet());
}
}
void ViewportStyleResolver::CollectViewportRulesFromAuthorSheetContents(
StyleSheetContents& contents) {
CollectViewportRulesFromImports(contents);
if (contents.HasViewportRule())
CollectViewportChildRules(contents.ChildRules(), kAuthorOrigin);
}
void ViewportStyleResolver::CollectViewportRulesFromAuthorSheet(
const CSSStyleSheet& sheet) {
DCHECK(sheet.Contents());
StyleSheetContents& contents = *sheet.Contents();
if (!contents.HasViewportRule() && contents.ImportRules().IsEmpty())
return;
if (sheet.MediaQueries() &&
!initial_viewport_medium_->Eval(*sheet.MediaQueries(),
&viewport_dependent_media_query_results_,
&device_dependent_media_query_results_))
return;
CollectViewportRulesFromAuthorSheetContents(contents);
}
void ViewportStyleResolver::AddViewportRule(StyleRuleViewport& viewport_rule,
Origin origin) {
CSSPropertyValueSet& property_set = viewport_rule.MutableProperties();
unsigned property_count = property_set.PropertyCount();
if (!property_count)
return;
if (origin == kAuthorOrigin)
has_author_style_ = true;
if (!property_set_) {
property_set_ = property_set.MutableCopy();
return;
}
// We cannot use mergeAndOverrideOnConflict() here because it doesn't
// respect the !important declaration (but addRespectingCascade() does).
for (unsigned i = 0; i < property_count; ++i) {
CSSPropertyValueSet::PropertyReference property =
property_set.PropertyAt(i);
property_set_->AddRespectingCascade(
CSSPropertyValue(property.PropertyMetadata(), property.Value()));
}
}
void ViewportStyleResolver::Resolve() {
if (!property_set_) {
document_->SetViewportDescription(
ViewportDescription(ViewportDescription::kUserAgentStyleSheet));
return;
}
ViewportDescription description(
has_author_style_ ? ViewportDescription::kAuthorStyleSheet
: ViewportDescription::kUserAgentStyleSheet);
description.user_zoom = ViewportArgumentValue(CSSPropertyUserZoom);
description.zoom = ViewportArgumentValue(CSSPropertyZoom);
description.min_zoom = ViewportArgumentValue(CSSPropertyMinZoom);
description.max_zoom = ViewportArgumentValue(CSSPropertyMaxZoom);
description.min_width = ViewportLengthValue(CSSPropertyMinWidth);
description.max_width = ViewportLengthValue(CSSPropertyMaxWidth);
description.min_height = ViewportLengthValue(CSSPropertyMinHeight);
description.max_height = ViewportLengthValue(CSSPropertyMaxHeight);
description.orientation = ViewportArgumentValue(CSSPropertyOrientation);
document_->SetViewportDescription(description);
}
float ViewportStyleResolver::ViewportArgumentValue(CSSPropertyID id) const {
float default_value = ViewportDescription::kValueAuto;
// UserZoom default value is CSSValueZoom, which maps to true, meaning that
// yes, it is user scalable. When the value is set to CSSValueFixed, we
// return false.
if (id == CSSPropertyUserZoom)
default_value = 1;
const CSSValue* value = property_set_->GetPropertyCSSValue(id);
if (!value || !(value->IsPrimitiveValue() || value->IsIdentifierValue()))
return default_value;
if (value->IsIdentifierValue()) {
switch (ToCSSIdentifierValue(value)->GetValueID()) {
case CSSValueAuto:
return default_value;
case CSSValueLandscape:
return ViewportDescription::kValueLandscape;
case CSSValuePortrait:
return ViewportDescription::kValuePortrait;
case CSSValueZoom:
return default_value;
case CSSValueInternalExtendToZoom:
return ViewportDescription::kValueExtendToZoom;
case CSSValueFixed:
return 0;
default:
return default_value;
}
}
const CSSPrimitiveValue* primitive_value = ToCSSPrimitiveValue(value);
if (primitive_value->IsNumber() || primitive_value->IsPx())
return primitive_value->GetFloatValue();
if (primitive_value->IsFontRelativeLength())
return primitive_value->GetFloatValue() *
document_->GetComputedStyle()->GetFontDescription().ComputedSize();
if (primitive_value->IsPercentage()) {
float percent_value = primitive_value->GetFloatValue() / 100.0f;
switch (id) {
case CSSPropertyMaxZoom:
case CSSPropertyMinZoom:
case CSSPropertyZoom:
return percent_value;
default:
NOTREACHED();
break;
}
}
NOTREACHED();
return default_value;
}
Length ViewportStyleResolver::ViewportLengthValue(CSSPropertyID id) {
DCHECK(id == CSSPropertyMaxHeight || id == CSSPropertyMinHeight ||
id == CSSPropertyMaxWidth || id == CSSPropertyMinWidth);
const CSSValue* value = property_set_->GetPropertyCSSValue(id);
if (!value || !(value->IsPrimitiveValue() || value->IsIdentifierValue()))
return Length(); // auto
if (value->IsIdentifierValue()) {
CSSValueID value_id = ToCSSIdentifierValue(value)->GetValueID();
if (value_id == CSSValueInternalExtendToZoom)
return Length(kExtendToZoom);
if (value_id == CSSValueAuto)
return Length(kAuto);
}
const CSSPrimitiveValue* primitive_value = ToCSSPrimitiveValue(value);
ComputedStyle* document_style = document_->MutableComputedStyle();
// If we have viewport units the conversion will mark the document style as
// having viewport units.
bool document_style_has_viewport_units = document_style->HasViewportUnits();
document_style->SetHasViewportUnits(false);
LocalFrameView* view = document_->GetFrame()->View();
DCHECK(view);
CSSToLengthConversionData::FontSizes font_sizes(document_style,
document_style);
CSSToLengthConversionData::ViewportSize viewport_size(
view->InitialViewportWidth(), view->InitialViewportHeight());
Length result = primitive_value->ConvertToLength(CSSToLengthConversionData(
document_style, font_sizes, viewport_size, 1.0f));
if (document_style->HasViewportUnits())
has_viewport_units_ = true;
document_style->SetHasViewportUnits(document_style_has_viewport_units);
if (result.IsFixed() && document_->GetPage()) {
float scaled_value =
document_->GetPage()->GetChromeClient().WindowToViewportScalar(
result.GetFloatValue());
result.SetValue(scaled_value);
}
return result;
}
void ViewportStyleResolver::InitialViewportChanged() {
if (needs_update_ == kCollectRules)
return;
if (has_viewport_units_)
needs_update_ = kResolve;
auto& results = viewport_dependent_media_query_results_;
for (unsigned i = 0; i < results.size(); i++) {
if (initial_viewport_medium_->Eval(results[i].Expression()) !=
results[i].Result()) {
needs_update_ = kCollectRules;
break;
}
}
if (needs_update_ == kNoUpdate)
return;
document_->ScheduleLayoutTreeUpdateIfNeeded();
}
void ViewportStyleResolver::SetNeedsCollectRules() {
needs_update_ = kCollectRules;
document_->ScheduleLayoutTreeUpdateIfNeeded();
}
void ViewportStyleResolver::UpdateViewport(
DocumentStyleSheetCollection& collection) {
if (needs_update_ == kNoUpdate)
return;
if (needs_update_ == kCollectRules) {
Reset();
CollectViewportRulesFromUASheets();
if (RuntimeEnabledFeatures::CSSViewportEnabled())
collection.CollectViewportRules(*this);
}
Resolve();
needs_update_ = kNoUpdate;
}
void ViewportStyleResolver::Trace(blink::Visitor* visitor) {
visitor->Trace(document_);
visitor->Trace(property_set_);
visitor->Trace(initial_viewport_medium_);
}
} // namespace blink