blob: aab5a680243cabe7a948fd1a2e85e924d784a5d2 [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/CSSStyleSheet.h"
#include "core/css/CSSToLengthConversionData.h"
#include "core/css/MediaValuesInitialViewport.h"
#include "core/css/StylePropertySet.h"
#include "core/css/StyleRule.h"
#include "core/css/StyleRuleImport.h"
#include "core/css/StyleSheetContents.h"
#include "core/dom/Document.h"
#include "core/dom/DocumentStyleSheetCollection.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/dom/ViewportDescription.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/layout/api/LayoutViewItem.h"
namespace blink {
ViewportStyleResolver::ViewportStyleResolver(Document& document)
: m_document(document) {
DCHECK(document.frame());
m_initialViewportMedium = new MediaQueryEvaluator(
MediaValuesInitialViewport::create(*document.frame()));
}
void ViewportStyleResolver::reset() {
m_viewportDependentMediaQueryResults.clear();
m_deviceDependentMediaQueryResults.clear();
m_propertySet = nullptr;
m_hasAuthorStyle = false;
m_hasViewportUnits = false;
m_needsUpdate = NoUpdate;
}
void ViewportStyleResolver::collectViewportRulesFromUASheets() {
CSSDefaultStyleSheets& defaultStyleSheets = CSSDefaultStyleSheets::instance();
WebViewportStyle viewportStyle = m_document->settings()
? m_document->settings()->viewportStyle()
: WebViewportStyle::Default;
StyleSheetContents* viewportContents = nullptr;
switch (viewportStyle) {
case WebViewportStyle::Default:
break;
case WebViewportStyle::Mobile:
viewportContents = defaultStyleSheets.ensureMobileViewportStyleSheet();
break;
case WebViewportStyle::Television:
viewportContents =
defaultStyleSheets.ensureTelevisionViewportStyleSheet();
break;
}
if (viewportContents)
collectViewportChildRules(viewportContents->childRules(), UserAgentOrigin);
if (m_document->isMobileDocument()) {
collectViewportChildRules(
defaultStyleSheets.ensureXHTMLMobileProfileStyleSheet()->childRules(),
UserAgentOrigin);
}
DCHECK(!defaultStyleSheets.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* mediaRule = toStyleRuleMedia(rule);
if (!mediaRule->mediaQueries() ||
m_initialViewportMedium->eval(mediaRule->mediaQueries(),
&m_viewportDependentMediaQueryResults,
&m_deviceDependentMediaQueryResults))
collectViewportChildRules(mediaRule->childRules(), origin);
} else if (rule->isSupportsRule()) {
StyleRuleSupports* supportsRule = toStyleRuleSupports(rule);
if (supportsRule->conditionIsSupported())
collectViewportChildRules(supportsRule->childRules(), origin);
}
}
}
void ViewportStyleResolver::collectViewportRulesFromImports(
StyleSheetContents& contents) {
for (const auto& importRule : contents.importRules()) {
if (!importRule->styleSheet())
continue;
if (!importRule->styleSheet()->hasViewportRule())
continue;
if (importRule->mediaQueries() &&
m_initialViewportMedium->eval(importRule->mediaQueries(),
&m_viewportDependentMediaQueryResults,
&m_deviceDependentMediaQueryResults))
collectViewportRulesFromAuthorSheetContents(*importRule->styleSheet());
}
}
void ViewportStyleResolver::collectViewportRulesFromAuthorSheetContents(
StyleSheetContents& contents) {
collectViewportRulesFromImports(contents);
if (contents.hasViewportRule())
collectViewportChildRules(contents.childRules(), AuthorOrigin);
}
void ViewportStyleResolver::collectViewportRulesFromAuthorSheet(
const CSSStyleSheet& sheet) {
DCHECK(sheet.contents());
StyleSheetContents& contents = *sheet.contents();
if (!contents.hasViewportRule() && contents.importRules().isEmpty())
return;
if (sheet.mediaQueries() &&
!m_initialViewportMedium->eval(sheet.mediaQueries(),
&m_viewportDependentMediaQueryResults,
&m_deviceDependentMediaQueryResults))
return;
collectViewportRulesFromAuthorSheetContents(contents);
}
void ViewportStyleResolver::addViewportRule(StyleRuleViewport& viewportRule,
Origin origin) {
StylePropertySet& propertySet = viewportRule.mutableProperties();
unsigned propertyCount = propertySet.propertyCount();
if (!propertyCount)
return;
if (origin == AuthorOrigin)
m_hasAuthorStyle = true;
if (!m_propertySet) {
m_propertySet = propertySet.mutableCopy();
return;
}
// We cannot use mergeAndOverrideOnConflict() here because it doesn't
// respect the !important declaration (but addRespectingCascade() does).
for (unsigned i = 0; i < propertyCount; ++i)
m_propertySet->addRespectingCascade(
propertySet.propertyAt(i).toCSSProperty());
}
void ViewportStyleResolver::resolve() {
if (!m_propertySet) {
m_document->setViewportDescription(
ViewportDescription(ViewportDescription::UserAgentStyleSheet));
return;
}
ViewportDescription description(
m_hasAuthorStyle ? ViewportDescription::AuthorStyleSheet
: ViewportDescription::UserAgentStyleSheet);
description.userZoom = viewportArgumentValue(CSSPropertyUserZoom);
description.zoom = viewportArgumentValue(CSSPropertyZoom);
description.minZoom = viewportArgumentValue(CSSPropertyMinZoom);
description.maxZoom = viewportArgumentValue(CSSPropertyMaxZoom);
description.minWidth = viewportLengthValue(CSSPropertyMinWidth);
description.maxWidth = viewportLengthValue(CSSPropertyMaxWidth);
description.minHeight = viewportLengthValue(CSSPropertyMinHeight);
description.maxHeight = viewportLengthValue(CSSPropertyMaxHeight);
description.orientation = viewportArgumentValue(CSSPropertyOrientation);
m_document->setViewportDescription(description);
}
float ViewportStyleResolver::viewportArgumentValue(CSSPropertyID id) const {
float defaultValue = ViewportDescription::ValueAuto;
// 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)
defaultValue = 1;
const CSSValue* value = m_propertySet->getPropertyCSSValue(id);
if (!value || !(value->isPrimitiveValue() || value->isIdentifierValue()))
return defaultValue;
if (value->isIdentifierValue()) {
switch (toCSSIdentifierValue(value)->getValueID()) {
case CSSValueAuto:
return defaultValue;
case CSSValueLandscape:
return ViewportDescription::ValueLandscape;
case CSSValuePortrait:
return ViewportDescription::ValuePortrait;
case CSSValueZoom:
return defaultValue;
case CSSValueInternalExtendToZoom:
return ViewportDescription::ValueExtendToZoom;
case CSSValueFixed:
return 0;
default:
return defaultValue;
}
}
const CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
if (primitiveValue->isNumber() || primitiveValue->isPx())
return primitiveValue->getFloatValue();
if (primitiveValue->isFontRelativeLength())
return primitiveValue->getFloatValue() *
m_document->computedStyle()->getFontDescription().computedSize();
if (primitiveValue->isPercentage()) {
float percentValue = primitiveValue->getFloatValue() / 100.0f;
switch (id) {
case CSSPropertyMaxZoom:
case CSSPropertyMinZoom:
case CSSPropertyZoom:
return percentValue;
default:
NOTREACHED();
break;
}
}
NOTREACHED();
return defaultValue;
}
Length ViewportStyleResolver::viewportLengthValue(CSSPropertyID id) {
ASSERT(id == CSSPropertyMaxHeight || id == CSSPropertyMinHeight ||
id == CSSPropertyMaxWidth || id == CSSPropertyMinWidth);
const CSSValue* value = m_propertySet->getPropertyCSSValue(id);
if (!value || !(value->isPrimitiveValue() || value->isIdentifierValue()))
return Length(); // auto
if (value->isIdentifierValue()) {
CSSValueID valueID = toCSSIdentifierValue(value)->getValueID();
if (valueID == CSSValueInternalExtendToZoom)
return Length(ExtendToZoom);
if (valueID == CSSValueAuto)
return Length(Auto);
}
const CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value);
ComputedStyle* documentStyle = m_document->mutableComputedStyle();
// If we have viewport units the conversion will mark the document style as
// having viewport units.
bool documentStyleHasViewportUnits = documentStyle->hasViewportUnits();
documentStyle->setHasViewportUnits(false);
FrameView* view = m_document->frame()->view();
DCHECK(view);
CSSToLengthConversionData::FontSizes fontSizes(documentStyle, documentStyle);
CSSToLengthConversionData::ViewportSize viewportSize(
view->initialViewportWidth(), view->initialViewportHeight());
Length result = primitiveValue->convertToLength(
CSSToLengthConversionData(documentStyle, fontSizes, viewportSize, 1.0f));
if (documentStyle->hasViewportUnits())
m_hasViewportUnits = true;
documentStyle->setHasViewportUnits(documentStyleHasViewportUnits);
return result;
}
void ViewportStyleResolver::initialViewportChanged() {
if (m_needsUpdate == CollectRules)
return;
auto& results = m_viewportDependentMediaQueryResults;
for (unsigned i = 0; i < results.size(); i++) {
if (m_initialViewportMedium->eval(results[i]->expression()) !=
results[i]->result()) {
m_needsUpdate = CollectRules;
return;
}
}
if (m_hasViewportUnits)
m_needsUpdate = Resolve;
}
void ViewportStyleResolver::setNeedsCollectRules() {
m_needsUpdate = CollectRules;
}
void ViewportStyleResolver::updateViewport(
DocumentStyleSheetCollection& collection) {
if (m_needsUpdate == NoUpdate)
return;
if (m_needsUpdate == CollectRules) {
reset();
collectViewportRulesFromUASheets();
if (RuntimeEnabledFeatures::cssViewportEnabled())
collection.collectViewportRules(*this);
}
resolve();
m_needsUpdate = NoUpdate;
}
DEFINE_TRACE(ViewportStyleResolver) {
visitor->trace(m_document);
visitor->trace(m_propertySet);
visitor->trace(m_initialViewportMedium);
visitor->trace(m_viewportDependentMediaQueryResults);
visitor->trace(m_deviceDependentMediaQueryResults);
}
} // namespace blink