blob: 1a7fb06d5bb4344a0a9fe8fb0c773f5d51b0b5cb [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* (C) 2006 Alexey Proskuryakov (ap@webkit.org)
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
* Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2012-2013 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "core/dom/ViewportDescription.h"
#include "core/dom/Document.h"
#include "core/frame/FrameHost.h"
#include "core/frame/FrameView.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
#include "core/frame/VisualViewport.h"
#include "platform/Histogram.h"
#include "platform/weborigin/KURL.h"
namespace blink {
static const float& compareIgnoringAuto(const float& value1, const float& value2, const float& (*compare) (const float&, const float&))
{
if (value1 == ViewportDescription::ValueAuto)
return value2;
if (value2 == ViewportDescription::ValueAuto)
return value1;
return compare(value1, value2);
}
float ViewportDescription::resolveViewportLength(const Length& length, const FloatSize& initialViewportSize, Direction direction)
{
if (length.isAuto())
return ViewportDescription::ValueAuto;
if (length.isFixed())
return length.getFloatValue();
if (length.type() == ExtendToZoom)
return ViewportDescription::ValueExtendToZoom;
if (length.type() == Percent && direction == Horizontal)
return initialViewportSize.width() * length.getFloatValue() / 100.0f;
if (length.type() == Percent && direction == Vertical)
return initialViewportSize.height() * length.getFloatValue() / 100.0f;
if (length.type() == DeviceWidth)
return initialViewportSize.width();
if (length.type() == DeviceHeight)
return initialViewportSize.height();
ASSERT_NOT_REACHED();
return ViewportDescription::ValueAuto;
}
PageScaleConstraints ViewportDescription::resolve(const FloatSize& initialViewportSize, Length legacyFallbackWidth) const
{
float resultWidth = ValueAuto;
Length copyMaxWidth = maxWidth;
Length copyMinWidth = minWidth;
// In case the width (used for min- and max-width) is undefined.
if (isLegacyViewportType() && maxWidth.isAuto()) {
// The width viewport META property is translated into 'width' descriptors, setting
// the 'min' value to 'extend-to-zoom' and the 'max' value to the intended length.
// In case the UA-defines a min-width, use that as length.
if (zoom == ViewportDescription::ValueAuto) {
copyMinWidth = Length(ExtendToZoom);
copyMaxWidth = legacyFallbackWidth;
} else if (maxHeight.isAuto()) {
copyMinWidth = Length(ExtendToZoom);
copyMaxWidth = Length(ExtendToZoom);
}
}
float resultMaxWidth = resolveViewportLength(copyMaxWidth, initialViewportSize, Horizontal);
float resultMinWidth = resolveViewportLength(copyMinWidth, initialViewportSize, Horizontal);
float resultHeight = ValueAuto;
float resultMaxHeight = resolveViewportLength(maxHeight, initialViewportSize, Vertical);
float resultMinHeight = resolveViewportLength(minHeight, initialViewportSize, Vertical);
float resultZoom = zoom;
float resultMinZoom = minZoom;
float resultMaxZoom = maxZoom;
bool resultUserZoom = userZoom;
// 1. Resolve min-zoom and max-zoom values.
if (resultMinZoom != ViewportDescription::ValueAuto && resultMaxZoom != ViewportDescription::ValueAuto)
resultMaxZoom = std::max(resultMinZoom, resultMaxZoom);
// 2. Constrain zoom value to the [min-zoom, max-zoom] range.
if (resultZoom != ViewportDescription::ValueAuto)
resultZoom = compareIgnoringAuto(resultMinZoom, compareIgnoringAuto(resultMaxZoom, resultZoom, std::min), std::max);
float extendZoom = compareIgnoringAuto(resultZoom, resultMaxZoom, std::min);
// 3. Resolve non-"auto" lengths to pixel lengths.
if (extendZoom == ViewportDescription::ValueAuto) {
if (resultMaxWidth == ViewportDescription::ValueExtendToZoom)
resultMaxWidth = ViewportDescription::ValueAuto;
if (resultMaxHeight == ViewportDescription::ValueExtendToZoom)
resultMaxHeight = ViewportDescription::ValueAuto;
if (resultMinWidth == ViewportDescription::ValueExtendToZoom)
resultMinWidth = resultMaxWidth;
if (resultMinHeight == ViewportDescription::ValueExtendToZoom)
resultMinHeight = resultMaxHeight;
} else {
float extendWidth = initialViewportSize.width() / extendZoom;
float extendHeight = initialViewportSize.height() / extendZoom;
if (resultMaxWidth == ViewportDescription::ValueExtendToZoom)
resultMaxWidth = extendWidth;
if (resultMaxHeight == ViewportDescription::ValueExtendToZoom)
resultMaxHeight = extendHeight;
if (resultMinWidth == ViewportDescription::ValueExtendToZoom)
resultMinWidth = compareIgnoringAuto(extendWidth, resultMaxWidth, std::max);
if (resultMinHeight == ViewportDescription::ValueExtendToZoom)
resultMinHeight = compareIgnoringAuto(extendHeight, resultMaxHeight, std::max);
}
// 4. Resolve initial width from min/max descriptors.
if (resultMinWidth != ViewportDescription::ValueAuto || resultMaxWidth != ViewportDescription::ValueAuto)
resultWidth = compareIgnoringAuto(resultMinWidth, compareIgnoringAuto(resultMaxWidth, initialViewportSize.width(), std::min), std::max);
// 5. Resolve initial height from min/max descriptors.
if (resultMinHeight != ViewportDescription::ValueAuto || resultMaxHeight != ViewportDescription::ValueAuto)
resultHeight = compareIgnoringAuto(resultMinHeight, compareIgnoringAuto(resultMaxHeight, initialViewportSize.height(), std::min), std::max);
// 6-7. Resolve width value.
if (resultWidth == ViewportDescription::ValueAuto) {
if (resultHeight == ViewportDescription::ValueAuto || !initialViewportSize.height())
resultWidth = initialViewportSize.width();
else
resultWidth = resultHeight * (initialViewportSize.width() / initialViewportSize.height());
}
// 8. Resolve height value.
if (resultHeight == ViewportDescription::ValueAuto) {
if (!initialViewportSize.width())
resultHeight = initialViewportSize.height();
else
resultHeight = resultWidth * initialViewportSize.height() / initialViewportSize.width();
}
// Resolve initial-scale value.
if (resultZoom == ViewportDescription::ValueAuto) {
if (resultWidth != ViewportDescription::ValueAuto && resultWidth > 0)
resultZoom = initialViewportSize.width() / resultWidth;
if (resultHeight != ViewportDescription::ValueAuto && resultHeight > 0) {
// if 'auto', the initial-scale will be negative here and thus ignored.
resultZoom = std::max<float>(resultZoom, initialViewportSize.height() / resultHeight);
}
}
// If user-scalable = no, lock the min/max scale to the computed initial
// scale.
if (!resultUserZoom)
resultMinZoom = resultMaxZoom = resultZoom;
// Only set initialScale to a value if it was explicitly set.
if (zoom == ViewportDescription::ValueAuto)
resultZoom = ViewportDescription::ValueAuto;
PageScaleConstraints result;
result.minimumScale = resultMinZoom;
result.maximumScale = resultMaxZoom;
result.initialScale = resultZoom;
result.layoutSize.setWidth(resultWidth);
result.layoutSize.setHeight(resultHeight);
return result;
}
void ViewportDescription::reportMobilePageStats(const LocalFrame* mainFrame) const
{
#if OS(ANDROID)
enum ViewportUMAType {
NoViewportTag,
DeviceWidth,
ConstantWidth,
MetaWidthOther,
MetaHandheldFriendly,
MetaMobileOptimized,
XhtmlMobileProfile,
TypeCount
};
if (!mainFrame || !mainFrame->host() || !mainFrame->view() || !mainFrame->document())
return;
// Avoid chrome:// pages like the new-tab page (on Android new tab is non-http).
if (!mainFrame->document()->url().protocolIsInHTTPFamily())
return;
DEFINE_STATIC_LOCAL(EnumerationHistogram, metaTagTypeHistogram, ("Viewport.MetaTagType", TypeCount));
if (!isSpecifiedByAuthor()) {
metaTagTypeHistogram.count(mainFrame->document()->isMobileDocument() ? XhtmlMobileProfile : NoViewportTag);
return;
}
if (isMetaViewportType()) {
if (maxWidth.type() == blink::Fixed) {
metaTagTypeHistogram.count(ConstantWidth);
if (mainFrame->view()) {
// To get an idea of how "far" the viewport is from the device's ideal width, we
// report the zoom level that we'd need to be at for the entire page to be visible.
int viewportWidth = maxWidth.intValue();
int windowWidth = mainFrame->host()->visualViewport().size().width();
int overviewZoomPercent = 100 * windowWidth / static_cast<float>(viewportWidth);
DEFINE_STATIC_LOCAL(SparseHistogram, overviewZoomHistogram, ("Viewport.OverviewZoom"));
overviewZoomHistogram.sample(overviewZoomPercent);
}
} else if (maxWidth.type() == blink::DeviceWidth || maxWidth.type() == blink::ExtendToZoom) {
metaTagTypeHistogram.count(DeviceWidth);
} else {
// Overflow bucket for cases we may be unaware of.
metaTagTypeHistogram.count(MetaWidthOther);
}
} else if (type == ViewportDescription::HandheldFriendlyMeta) {
metaTagTypeHistogram.count(MetaHandheldFriendly);
} else if (type == ViewportDescription::MobileOptimizedMeta) {
metaTagTypeHistogram.count(MobileOptimizedMeta);
}
#endif
}
bool ViewportDescription::matchesHeuristicsForGpuRasterization() const
{
return maxWidth == Length(DeviceWidth)
&& minZoom == 1.0
&& minZoomIsExplicit;
}
} // namespace blink