Skip to content
Permalink
Browse files
Add modern API for overriding the page's specified viewport configura…
…tion

https://bugs.webkit.org/show_bug.cgi?id=167734
<rdar://problem/30331795>

Reviewed by Simon Fraser.

Source/WebCore:

New API test: WebKit.OverrideViewportArguments

* dom/Document.cpp:
(WebCore::Document::updateViewportArguments):
* dom/Document.h:
(WebCore::Document::viewportArguments const):
Make the viewportArguments() getter respect the overridden arguments.

* dom/ViewportArguments.cpp:
(WebCore::numericPrefix):
(WebCore::findSizeValue):
(WebCore::findScaleValue):
(WebCore::findBooleanValue):
(WebCore::parseViewportFitValue):
(WebCore::viewportErrorMessage):
(WebCore::reportViewportWarning):
(WebCore::setViewportFeature):
* dom/ViewportArguments.h:
Make it possible to parse ViewportArguments without a Document, so
that it can be used in the UI process. We only used the Document for
two things: error reporting, and getting the state of one setting.
Refactor error handling to use a passed-arund function, and add a
variant of setViewportFeature() that doesn't take a Document.

Source/WebKit:

* Shared/WebPageCreationParameters.cpp:
(WebKit::WebPageCreationParameters::encode const):
(WebKit::WebPageCreationParameters::decode):
* Shared/WebPageCreationParameters.h:
Plumb overrideViewportArguments in WebPageCreationParameters, so that
if the process crashes (or swaps) they are maintained.

* UIProcess/API/Cocoa/WKWebView.mm:
(viewportArgumentsFromDictionary):
(-[WKWebView _overrideViewportWithArguments:]):
Add SPI to set override viewport arguments. Parse them into a ViewportArguments
object and use the existing (now improved) overrideViewportArguments mechanism
to take over the page's viewport arguments.

* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::creationParameters):
* UIProcess/WebPageProxy.h:
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::setOverrideViewportArguments):
* WebProcess/WebPage/WebPage.cpp:
Plumb overrideViewportArguments around more.

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/OverrideViewportArguments.mm: Added.


Canonical link: https://commits.webkit.org/211069@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244151 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
hortont424 committed Apr 10, 2019
1 parent ff43547 commit 326d933233c0d355db3f1e0cc12a6d91f497cacd
Showing 17 changed files with 269 additions and 49 deletions.
@@ -1,3 +1,35 @@
2019-04-10 Tim Horton <timothy_horton@apple.com>

Add modern API for overriding the page's specified viewport configuration
https://bugs.webkit.org/show_bug.cgi?id=167734
<rdar://problem/30331795>

Reviewed by Simon Fraser.

New API test: WebKit.OverrideViewportArguments

* dom/Document.cpp:
(WebCore::Document::updateViewportArguments):
* dom/Document.h:
(WebCore::Document::viewportArguments const):
Make the viewportArguments() getter respect the overridden arguments.

* dom/ViewportArguments.cpp:
(WebCore::numericPrefix):
(WebCore::findSizeValue):
(WebCore::findScaleValue):
(WebCore::findBooleanValue):
(WebCore::parseViewportFitValue):
(WebCore::viewportErrorMessage):
(WebCore::reportViewportWarning):
(WebCore::setViewportFeature):
* dom/ViewportArguments.h:
Make it possible to parse ViewportArguments without a Document, so
that it can be used in the UI process. We only used the Document for
two things: error reporting, and getting the state of one setting.
Refactor error handling to use a passed-arund function, and add a
variant of setViewportFeature() that doesn't take a Document.

2019-04-10 Justin Fan <justin_fan@apple.com>

[Web GPU] Indexed drawing and GPUCommandEncoder crash prevention
@@ -3621,7 +3621,7 @@ void Document::updateViewportArguments()
#ifndef NDEBUG
m_didDispatchViewportPropertiesChanged = true;
#endif
page()->chrome().dispatchViewportPropertiesDidChange(m_overrideViewportArguments ? m_overrideViewportArguments.value() : m_viewportArguments);
page()->chrome().dispatchViewportPropertiesDidChange(viewportArguments());
page()->chrome().didReceiveDocType(*frame());
}
}
@@ -411,7 +411,7 @@ class Document
void clearSelectorQueryCache();

void setViewportArguments(const ViewportArguments& viewportArguments) { m_viewportArguments = viewportArguments; }
ViewportArguments viewportArguments() const { return m_viewportArguments; }
ViewportArguments viewportArguments() const { return m_overrideViewportArguments.valueOr(m_viewportArguments); }

WEBCORE_EXPORT void setOverrideViewportArguments(const Optional<ViewportArguments>&);

@@ -38,6 +38,8 @@

namespace WebCore {

typedef WTF::Function<void(ViewportErrorCode, StringView, StringView)> InternalViewportErrorHandler;

#if PLATFORM(GTK)
const float ViewportArguments::deprecatedTargetDPI = 160;
#endif
@@ -288,9 +290,7 @@ void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& resu
result.maximumScale = result.minimumScale = result.initialScale;
}

static void reportViewportWarning(Document&, ViewportErrorCode, StringView replacement1 = { }, StringView replacement2 = { });

static float numericPrefix(Document& document, StringView key, StringView value, bool* ok = nullptr)
static float numericPrefix(StringView key, StringView value, const InternalViewportErrorHandler& errorHandler, bool* ok = nullptr)
{
size_t parsedLength;
float numericValue;
@@ -299,19 +299,19 @@ static float numericPrefix(Document& document, StringView key, StringView value,
else
numericValue = charactersToFloat(value.characters16(), value.length(), parsedLength);
if (!parsedLength) {
reportViewportWarning(document, UnrecognizedViewportArgumentValueError, value, key);
errorHandler(UnrecognizedViewportArgumentValueError, value, key);
if (ok)
*ok = false;
return 0;
}
if (parsedLength < value.length())
reportViewportWarning(document, TruncatedViewportArgumentValueError, value, key);
errorHandler(TruncatedViewportArgumentValueError, value, key);
if (ok)
*ok = true;
return numericValue;
}

static float findSizeValue(Document& document, StringView key, StringView value, bool* valueWasExplicit = nullptr)
static float findSizeValue(StringView key, StringView value, const InternalViewportErrorHandler& errorHandler, bool* valueWasExplicit = nullptr)
{
// 1) Non-negative number values are translated to px lengths.
// 2) Negative number values are translated to auto.
@@ -327,7 +327,7 @@ static float findSizeValue(Document& document, StringView key, StringView value,
if (equalLettersIgnoringASCIICase(value, "device-height"))
return ViewportArguments::ValueDeviceHeight;

float sizeValue = numericPrefix(document, key, value);
float sizeValue = numericPrefix(key, value, errorHandler);

if (sizeValue < 0) {
if (valueWasExplicit)
@@ -338,7 +338,7 @@ static float findSizeValue(Document& document, StringView key, StringView value,
return sizeValue;
}

static float findScaleValue(Document& document, StringView key, StringView value)
static float findScaleValue(StringView key, StringView value, const InternalViewportErrorHandler& errorHandler)
{
// 1) Non-negative number values are translated to <number> values.
// 2) Negative number values are translated to auto.
@@ -355,18 +355,18 @@ static float findScaleValue(Document& document, StringView key, StringView value
if (equalLettersIgnoringASCIICase(value, "device-height"))
return 10;

float numericValue = numericPrefix(document, key, value);
float numericValue = numericPrefix(key, value, errorHandler);

if (numericValue < 0)
return ViewportArguments::ValueAuto;

if (numericValue > 10.0)
reportViewportWarning(document, MaximumScaleTooLargeError);
errorHandler(MaximumScaleTooLargeError, { }, { });

return numericValue;
}

static bool findBooleanValue(Document& document, StringView key, StringView value)
static bool findBooleanValue(StringView key, StringView value, const InternalViewportErrorHandler& errorHandler)
{
// yes and no are used as keywords.
// Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes.
@@ -380,10 +380,10 @@ static bool findBooleanValue(Document& document, StringView key, StringView valu
return true;
if (equalLettersIgnoringASCIICase(value, "device-height"))
return true;
return std::abs(numericPrefix(document, key, value)) >= 1;
return std::abs(numericPrefix(key, value, errorHandler)) >= 1;
}

static ViewportFit parseViewportFitValue(Document& document, StringView key, StringView value)
static ViewportFit parseViewportFitValue(StringView key, StringView value, const InternalViewportErrorHandler& errorHandler)
{
if (equalLettersIgnoringASCIICase(value, "auto"))
return ViewportFit::Auto;
@@ -392,39 +392,11 @@ static ViewportFit parseViewportFitValue(Document& document, StringView key, Str
if (equalLettersIgnoringASCIICase(value, "cover"))
return ViewportFit::Cover;

reportViewportWarning(document, UnrecognizedViewportArgumentValueError, value, key);
errorHandler(UnrecognizedViewportArgumentValueError, value, key);

return ViewportFit::Auto;
}

void setViewportFeature(ViewportArguments& arguments, Document& document, StringView key, StringView value)
{
if (equalLettersIgnoringASCIICase(key, "width"))
arguments.width = findSizeValue(document, key, value, &arguments.widthWasExplicit);
else if (equalLettersIgnoringASCIICase(key, "height"))
arguments.height = findSizeValue(document, key, value);
else if (equalLettersIgnoringASCIICase(key, "initial-scale"))
arguments.zoom = findScaleValue(document, key, value);
else if (equalLettersIgnoringASCIICase(key, "minimum-scale"))
arguments.minZoom = findScaleValue(document, key, value);
else if (equalLettersIgnoringASCIICase(key, "maximum-scale"))
arguments.maxZoom = findScaleValue(document, key, value);
else if (equalLettersIgnoringASCIICase(key, "user-scalable"))
arguments.userZoom = findBooleanValue(document, key, value);
#if PLATFORM(IOS_FAMILY)
else if (equalLettersIgnoringASCIICase(key, "minimal-ui")) {
// FIXME: Ignore silently for now. This code should eventually be removed
// so we start giving the warning in the web inspector as for other unimplemented keys.
}
#endif
else if (equalLettersIgnoringASCIICase(key, "shrink-to-fit"))
arguments.shrinkToFit = findBooleanValue(document, key, value);
else if (equalLettersIgnoringASCIICase(key, "viewport-fit") && document.settings().viewportFitEnabled())
arguments.viewportFit = parseViewportFitValue(document, key, value);
else
reportViewportWarning(document, UnrecognizedViewportArgumentKeyError, key);
}

static const char* viewportErrorMessageTemplate(ViewportErrorCode errorCode)
{
static const char* const errors[] = {
@@ -452,12 +424,8 @@ static MessageLevel viewportErrorMessageLevel(ViewportErrorCode errorCode)
return MessageLevel::Error;
}

void reportViewportWarning(Document& document, ViewportErrorCode errorCode, StringView replacement1, StringView replacement2)
static String viewportErrorMessage(ViewportErrorCode errorCode, StringView replacement1, StringView replacement2)
{
// FIXME: Why is this null check needed? Can't addConsoleMessage deal with this?
if (!document.frame())
return;

String message = viewportErrorMessageTemplate(errorCode);
if (!replacement1.isNull())
message.replace("%replacement1", replacement1.toStringWithoutCopying());
@@ -468,10 +436,58 @@ void reportViewportWarning(Document& document, ViewportErrorCode errorCode, Stri
if ((errorCode == UnrecognizedViewportArgumentValueError || errorCode == TruncatedViewportArgumentValueError) && replacement1.contains(';'))
message.append(" Note that ';' is not a separator in viewport values. The list should be comma-separated.");

return message;
}

static void reportViewportWarning(Document& document, ViewportErrorCode errorCode, const String& message)
{
// FIXME: Why is this null check needed? Can't addConsoleMessage deal with this?
if (!document.frame())
return;

// FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
document.addConsoleMessage(MessageSource::Rendering, viewportErrorMessageLevel(errorCode), message);
}

void setViewportFeature(ViewportArguments& arguments, StringView key, StringView value, bool viewportFitEnabled, const ViewportErrorHandler& errorHandler)
{
InternalViewportErrorHandler internalErrorHandler = [&errorHandler] (ViewportErrorCode errorCode, StringView replacement1, StringView replacement2) {
errorHandler(errorCode, viewportErrorMessage(errorCode, replacement1, replacement2));
};

if (equalLettersIgnoringASCIICase(key, "width"))
arguments.width = findSizeValue(key, value, internalErrorHandler, &arguments.widthWasExplicit);
else if (equalLettersIgnoringASCIICase(key, "height"))
arguments.height = findSizeValue(key, value, internalErrorHandler);
else if (equalLettersIgnoringASCIICase(key, "initial-scale"))
arguments.zoom = findScaleValue(key, value, internalErrorHandler);
else if (equalLettersIgnoringASCIICase(key, "minimum-scale"))
arguments.minZoom = findScaleValue(key, value, internalErrorHandler);
else if (equalLettersIgnoringASCIICase(key, "maximum-scale"))
arguments.maxZoom = findScaleValue(key, value, internalErrorHandler);
else if (equalLettersIgnoringASCIICase(key, "user-scalable"))
arguments.userZoom = findBooleanValue(key, value, internalErrorHandler);
#if PLATFORM(IOS_FAMILY)
else if (equalLettersIgnoringASCIICase(key, "minimal-ui")) {
// FIXME: Ignore silently for now. This code should eventually be removed
// so we start giving the warning in the web inspector as for other unimplemented keys.
}
#endif
else if (equalLettersIgnoringASCIICase(key, "shrink-to-fit"))
arguments.shrinkToFit = findBooleanValue(key, value, internalErrorHandler);
else if (equalLettersIgnoringASCIICase(key, "viewport-fit") && viewportFitEnabled)
arguments.viewportFit = parseViewportFitValue(key, value, internalErrorHandler);
else
internalErrorHandler(UnrecognizedViewportArgumentKeyError, key, { });
}

void setViewportFeature(ViewportArguments& arguments, Document& document, StringView key, StringView value)
{
setViewportFeature(arguments, key, value, document.settings().viewportFitEnabled(), [&](ViewportErrorCode errorCode, const String& message) {
reportViewportWarning(document, errorCode, message);
});
}

TextStream& operator<<(TextStream& ts, const ViewportArguments& viewportArguments)
{
TextStream::IndentScope indentScope(ts);
@@ -143,7 +143,9 @@ WEBCORE_EXPORT void restrictMinimumScaleFactorToViewportSize(ViewportAttributes&
WEBCORE_EXPORT void restrictScaleFactorToInitialScaleIfNotUserScalable(ViewportAttributes& result);
WEBCORE_EXPORT float computeMinimumScaleFactorForContentContained(const ViewportAttributes& result, const IntSize& viewportSize, const IntSize& contentSize);

typedef WTF::Function<void(ViewportErrorCode, const String&)> ViewportErrorHandler;
void setViewportFeature(ViewportArguments&, Document&, StringView key, StringView value);
WEBCORE_EXPORT void setViewportFeature(ViewportArguments&, StringView key, StringView value, bool viewportFitEnabled, const ViewportErrorHandler&);

WTF::TextStream& operator<<(WTF::TextStream&, const ViewportArguments&);

@@ -1,3 +1,34 @@
2019-04-10 Tim Horton <timothy_horton@apple.com>

Add modern API for overriding the page's specified viewport configuration
https://bugs.webkit.org/show_bug.cgi?id=167734
<rdar://problem/30331795>

Reviewed by Simon Fraser.

* Shared/WebPageCreationParameters.cpp:
(WebKit::WebPageCreationParameters::encode const):
(WebKit::WebPageCreationParameters::decode):
* Shared/WebPageCreationParameters.h:
Plumb overrideViewportArguments in WebPageCreationParameters, so that
if the process crashes (or swaps) they are maintained.

* UIProcess/API/Cocoa/WKWebView.mm:
(viewportArgumentsFromDictionary):
(-[WKWebView _overrideViewportWithArguments:]):
Add SPI to set override viewport arguments. Parse them into a ViewportArguments
object and use the existing (now improved) overrideViewportArguments mechanism
to take over the page's viewport arguments.

* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::creationParameters):
* UIProcess/WebPageProxy.h:
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::setOverrideViewportArguments):
* WebProcess/WebPage/WebPage.cpp:
Plumb overrideViewportArguments around more.

2019-04-10 Jiewen Tan <jiewen_tan@apple.com>

Add runJavaScriptInFrame for WebPageProxy/WebPage
@@ -95,6 +95,7 @@ void WebPageCreationParameters::encode(IPC::Encoder& encoder) const
encoder << maximumUnobscuredSize;
encoder << deviceOrientation;
encoder << keyboardIsAttached;
encoder << overrideViewportArguments;
#endif
#if PLATFORM(COCOA)
encoder << smartInsertDeleteEnabled;
@@ -279,6 +280,12 @@ Optional<WebPageCreationParameters> WebPageCreationParameters::decode(IPC::Decod
return WTF::nullopt;
if (!decoder.decode(parameters.keyboardIsAttached))
return WTF::nullopt;

Optional<Optional<WebCore::ViewportArguments>> overrideViewportArguments;
decoder >> overrideViewportArguments;
if (!overrideViewportArguments)
return WTF::nullopt;
parameters.overrideViewportArguments = WTFMove(*overrideViewportArguments);
#endif

#if PLATFORM(COCOA)
@@ -42,6 +42,7 @@
#include <WebCore/Pagination.h>
#include <WebCore/ScrollTypes.h>
#include <WebCore/UserInterfaceLayoutDirection.h>
#include <WebCore/ViewportArguments.h>
#include <pal/SessionID.h>
#include <wtf/HashMap.h>
#include <wtf/text/WTFString.h>
@@ -153,6 +154,7 @@ struct WebPageCreationParameters {
WebCore::FloatSize maximumUnobscuredSize;
int32_t deviceOrientation { 0 };
bool keyboardIsAttached { false };
Optional<WebCore::ViewportArguments> overrideViewportArguments;
#endif
#if PLATFORM(COCOA)
bool smartInsertDeleteEnabled;
@@ -6253,6 +6253,34 @@ - (void)_clearOverrideLayoutParameters
_maximumUnobscuredSizeOverride = CGSizeZero;
}

static WTF::Optional<WebCore::ViewportArguments> viewportArgumentsFromDictionary(NSDictionary<NSString *, NSString *> *viewportArgumentPairs, bool viewportFitEnabled)
{
if (!viewportArgumentPairs)
return WTF::nullopt;

WebCore::ViewportArguments viewportArguments(WebCore::ViewportArguments::ViewportMeta);

[viewportArgumentPairs enumerateKeysAndObjectsUsingBlock:makeBlockPtr([&] (NSString *key, NSString *value, BOOL* stop) {
if (![key isKindOfClass:[NSString class]] || ![value isKindOfClass:[NSString class]])
[NSException raise:NSInvalidArgumentException format:@"-[WKWebView _overrideViewportWithArguments:]: Keys and values must all be NSStrings."];
String keyString = key;
String valueString = value;
WebCore::setViewportFeature(viewportArguments, keyString, valueString, viewportFitEnabled, [] (WebCore::ViewportErrorCode, const String& errorMessage) {
NSLog(@"-[WKWebView _overrideViewportWithArguments:]: Error parsing viewport argument: %s", errorMessage.utf8().data());
});
}).get()];

return viewportArguments;
}

- (void)_overrideViewportWithArguments:(NSDictionary<NSString *, NSString *> *)arguments
{
if (!_page)
return;

_page->setOverrideViewportArguments(viewportArgumentsFromDictionary(arguments, _page->preferences().viewportFitEnabled()));
}

- (UIView *)_viewForFindUI
{
return [self viewForZoomingInScrollView:[self scrollView]];
@@ -281,6 +281,7 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) {
- (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler;

- (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride WK_API_AVAILABLE(ios(9_0));
- (void)_overrideViewportWithArguments:(NSDictionary<NSString *, NSString *> *)arguments WK_API_AVAILABLE(ios(WK_IOS_TBA));

- (void)_clearOverrideLayoutParameters WK_API_AVAILABLE(ios(11.0));
- (void)_clearInterfaceOrientationOverride WK_API_AVAILABLE(ios(11.0));
@@ -7023,6 +7023,7 @@ WebPageCreationParameters WebPageProxy::creationParameters(WebProcessProxy& proc
parameters.maximumUnobscuredSize = m_maximumUnobscuredSize;
parameters.deviceOrientation = m_deviceOrientation;
parameters.keyboardIsAttached = isInHardwareKeyboardMode();
parameters.overrideViewportArguments = m_overrideViewportArguments;
#endif

#if PLATFORM(MAC)

0 comments on commit 326d933

Please sign in to comment.