WebKit Bugzilla
Attachment 360754 Details for
Bug 194104
: Parse and handle Ad Click Attribution attributes in HTMLAnchorElement::handleClick()
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-194104-20190131123252.patch (text/plain), 15.79 KB, created by
John Wilander
on 2019-01-31 12:32:52 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
John Wilander
Created:
2019-01-31 12:32:52 PST
Size:
15.79 KB
patch
obsolete
>Subversion Revision: 240677 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 5ed3184efc2897dbe9a8cfdd0b131176d6e5a6ee..70a1bc1da87dd8fbb289a3912841ce2694f7c4f9 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,30 @@ >+2019-01-31 John Wilander <wilander@apple.com> >+ >+ Parse and handle Ad Click Attribution attributes in HTMLAnchorElement::handleClick() >+ https://bugs.webkit.org/show_bug.cgi?id=194104 >+ <rdar://problem/47649991> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Test: http/tests/adClickAttribution/anchor-tag-attributes-validation.html >+ >+ This patch adds parsing and validation of the two new Ad Click Attribution >+ attributes in anchor elements: adcampaignid and addestination. The data is >+ not yet forwarded into the loader. >+ >+ * html/HTMLAnchorElement.cpp: >+ (WebCore::HTMLAnchorElement::parseAdClickAttribution const): >+ (WebCore::HTMLAnchorElement::handleClick): >+ Now calls HTMLAnchorElement::parseAdClickAttribution(). >+ * html/HTMLAnchorElement.h: >+ * loader/AdClickAttribution.cpp: >+ (WebCore::AdClickAttribution::convertToMaxEntropyValue): >+ Put here to have access to the right context including restrictions on >+ entropy. >+ * loader/AdClickAttribution.h: >+ Made WebCore::AdClickAttribution copyable since it's needed to have it be >+ WTF::Optional. >+ > 2019-01-29 Simon Fraser <simon.fraser@apple.com> > > REGRESSION(r240553): [iOS] Crash in ScrollingTree::updateTreeFromStateNode when attempting to log in to icloud.com >diff --git a/Source/WebCore/html/HTMLAnchorElement.cpp b/Source/WebCore/html/HTMLAnchorElement.cpp >index e7e167c95b4177513432f96c6f41e385afb62e44..ac35234689871b5c006485259c7753040c3f6a8c 100644 >--- a/Source/WebCore/html/HTMLAnchorElement.cpp >+++ b/Source/WebCore/html/HTMLAnchorElement.cpp >@@ -24,6 +24,7 @@ > #include "config.h" > #include "HTMLAnchorElement.h" > >+#include "AdClickAttribution.h" > #include "DOMTokenList.h" > #include "ElementIterator.h" > #include "EventHandler.h" >@@ -50,6 +51,7 @@ > #include "SecurityPolicy.h" > #include "Settings.h" > #include "URLUtils.h" >+#include "UserGestureIndicator.h" > #include <wtf/IsoMallocInlines.h> > #include <wtf/text/StringBuilder.h> > >@@ -394,6 +396,44 @@ bool HTMLAnchorElement::isSystemPreviewLink() const > } > #endif > >+Optional<AdClickAttribution> HTMLAnchorElement::parseAdClickAttribution() const >+{ >+ if (!RuntimeEnabledFeatures::sharedFeatures().adClickAttributionEnabled() || !UserGestureIndicator::processingUserGesture()) >+ return WTF::nullopt; >+ >+ auto adCampaignIDAttr = attributeWithoutSynchronization(adcampaignidAttr); >+ auto adDestinationAttr = attributeWithoutSynchronization(addestinationAttr); >+ >+ if (adCampaignIDAttr.isEmpty() && adDestinationAttr.isEmpty()) >+ return WTF::nullopt; >+ >+ if (adCampaignIDAttr.isEmpty() || adDestinationAttr.isEmpty()) { >+ document().addConsoleMessage(MessageSource::Storage, MessageLevel::Warning, "Both the campaign ID attribute and the destination attribute need to be set for Ad Click Attribution to work."); >+ return WTF::nullopt; >+ } >+ >+ auto convertedAdCampaignID = AdClickAttribution::convertToMaxEntropyValue(adCampaignIDAttr); >+ if (convertedAdCampaignID.hasException()) { >+ document().addConsoleMessage(MessageSource::Storage, MessageLevel::Warning, convertedAdCampaignID.releaseException().message()); >+ return WTF::nullopt; >+ } >+ >+ auto adCampaignID = convertedAdCampaignID.releaseReturnValue(); >+ URL adDestinationURL { URL(), adDestinationAttr }; >+ if (!adDestinationURL.isValid() || !adDestinationURL.protocolIsInHTTPFamily()) { >+ document().addConsoleMessage(MessageSource::Storage, MessageLevel::Warning, "The destination attribute for Ad Click Attribution could not be converted to a valid HTTP-family URL."); >+ return WTF::nullopt; >+ } >+ >+ RefPtr<Frame> frame = document().frame(); >+ if (!frame || !frame->isMainFrame()) { >+ document().addConsoleMessage(MessageSource::Storage, MessageLevel::Warning, "Ad Click Attribution is only supported in the main frame."); >+ return WTF::nullopt; >+ } >+ >+ return AdClickAttribution(AdClickAttribution::Campaign(adCampaignID), AdClickAttribution::Source(document().domain()), AdClickAttribution::Destination(adDestinationURL.host().toString())); >+} >+ > void HTMLAnchorElement::handleClick(Event& event) > { > event.setDefaultHandled(); >@@ -438,6 +478,10 @@ void HTMLAnchorElement::handleClick(Event& event) > else if (hasRel(Relation::NoOpener) || (RuntimeEnabledFeatures::sharedFeatures().blankAnchorTargetImpliesNoOpenerEnabled() && equalIgnoringASCIICase(effectiveTarget, "_blank"))) > newFrameOpenerPolicy = NewFrameOpenerPolicy::Suppress; > >+ auto adClickAttribution = parseAdClickAttribution(); >+ // Ad Click Attribution needs a subsequent matching conversion event to build its URL. >+ ASSERT(!adClickAttribution || adClickAttribution->url().isEmpty()); >+ > frame->loader().urlSelected(completedURL, effectiveTarget, &event, LockHistory::No, LockBackForwardList::No, shouldSendReferrer, document().shouldOpenExternalURLsPolicyToPropagate(), newFrameOpenerPolicy, downloadAttribute, systemPreviewInfo); > > sendPings(completedURL); >diff --git a/Source/WebCore/html/HTMLAnchorElement.h b/Source/WebCore/html/HTMLAnchorElement.h >index 6b02c9ab4d462685036e170396e45b81e8d329e8..61315ced17fa4d6e06526cd90bb588aadfa2af97 100644 >--- a/Source/WebCore/html/HTMLAnchorElement.h >+++ b/Source/WebCore/html/HTMLAnchorElement.h >@@ -28,9 +28,11 @@ > #include "SharedStringHash.h" > #include "URLUtils.h" > #include <wtf/OptionSet.h> >+#include <wtf/Optional.h> > > namespace WebCore { > >+class AdClickAttribution; > class DOMTokenList; > > // Link relation bitmask values. >@@ -95,6 +97,8 @@ private: > > void sendPings(const URL& destinationURL); > >+ Optional<AdClickAttribution> parseAdClickAttribution() const; >+ > void handleClick(Event&); > > enum EventType { >diff --git a/Source/WebCore/loader/AdClickAttribution.cpp b/Source/WebCore/loader/AdClickAttribution.cpp >index bdc0639372e3dd3e44ba8567b303fdcb3c665359..9eb7f1c035e52e926dbdec0cd194c88a8116de1d 100644 >--- a/Source/WebCore/loader/AdClickAttribution.cpp >+++ b/Source/WebCore/loader/AdClickAttribution.cpp >@@ -26,6 +26,8 @@ > #include "config.h" > #include "AdClickAttribution.h" > >+#include "Exception.h" >+#include <wtf/ASCIICType.h> > #include <wtf/RandomNumber.h> > #include <wtf/URL.h> > #include <wtf/text/StringBuilder.h> >@@ -90,4 +92,27 @@ URL AdClickAttribution::referrer() const > return URL(); > } > >+ExceptionOr<unsigned short> AdClickAttribution::convertToMaxEntropyValue(const String& toConvert) >+{ >+ if (toConvert.length() > 2) >+ return Exception { ConstraintError, makeString("\"", toConvert, "\" contains too many characters to be converted to a value allowed for Ad Click Attribution.") }; >+ >+ if (toConvert.length() < 2) >+ return Exception { ConstraintError, makeString("\"", toConvert, "\" contains too few characters to be converted to a value allowed for Ad Click Attribution.") }; >+ >+ if (!isASCIIDigit(toConvert.characterAt(0)) || !isASCIIDigit(toConvert.characterAt(1))) >+ return Exception { ConstraintError, makeString("\"", toConvert, "\" contains at least one non-digit character and can not be converted to a value allowed for Ad Click Attribution.") }; >+ >+ bool ok = false; >+ auto unsignedResult = toConvert.toUIntStrict(&ok, 10); >+ if (!ok) >+ return Exception { ConstraintError, makeString("\"", toConvert, "\" can not be converted to an unsigned integer which is required for Ad Click Attribution.") }; >+ >+ unsigned short result = static_cast<unsigned short>(unsignedResult); >+ if (result >= maxEntropy) >+ return Exception { ConstraintError, makeString("\"", toConvert, "\" is greater than or equal to ", String::number(maxEntropy), " and is not allowed for Ad Click Attribution.") }; >+ >+ return result; >+} >+ > } >diff --git a/Source/WebCore/loader/AdClickAttribution.h b/Source/WebCore/loader/AdClickAttribution.h >index 7df05ee5eb3ea87f9f9a2640538196e610d54733..4634995c2ed583a1542c1b5178636480193a4e1f 100644 >--- a/Source/WebCore/loader/AdClickAttribution.h >+++ b/Source/WebCore/loader/AdClickAttribution.h >@@ -25,6 +25,7 @@ > > #pragma once > >+#include "ExceptionOr.h" > #include "PublicSuffix.h" > #include <wtf/Noncopyable.h> > #include <wtf/Optional.h> >@@ -40,7 +41,6 @@ namespace WebCore { > constexpr unsigned short maxEntropy = 64; > > class AdClickAttribution { >- WTF_MAKE_NONCOPYABLE(AdClickAttribution); > public: > using CampaignId = unsigned short; > using ConversionData = unsigned short; >@@ -124,6 +124,8 @@ public: > WEBCORE_EXPORT URL referrer() const; > Optional<WallTime> earliestTimeToSend() const { return m_earliestTimeToSend; }; > >+ static ExceptionOr<unsigned short> convertToMaxEntropyValue(const String& toConvert); >+ > private: > bool isValid() const; > >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index ed148dc7264d84b78e4cd2b367e81fd24a7f6247..553e66217dc09d1f682aa1920f559b8de64de0da 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,16 @@ >+2019-01-31 John Wilander <wilander@apple.com> >+ >+ Parse and handle Ad Click Attribution attributes in HTMLAnchorElement::handleClick() >+ https://bugs.webkit.org/show_bug.cgi?id=194104 >+ <rdar://problem/47649991> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This test case makes sure invalid data triggers console warnings. >+ >+ * http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt: Added. >+ * http/tests/adClickAttribution/anchor-tag-attributes-validation.html: Added. >+ > 2019-01-29 Simon Fraser <simon.fraser@apple.com> > > REGRESSION(r240553): [iOS] Crash in ScrollingTree::updateTreeFromStateNode when attempting to log in to icloud.com >diff --git a/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt b/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..5f312267e4cee0a470b2129a22ea66409e3f6696 >--- /dev/null >+++ b/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt >@@ -0,0 +1,36 @@ >+CONSOLE MESSAGE: line 88: "100" contains too many characters to be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: "1" contains too few characters to be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: "98" is greater than or equal to 64 and is not allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: "-1" contains at least one non-digit character and can not be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: "ab" contains at least one non-digit character and can not be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: "1" contains at least one non-digit character and can not be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: " 1" contains at least one non-digit character and can not be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: "1 " contains at least one non-digit character and can not be converted to a value allowed for Ad Click Attribution. >+CONSOLE MESSAGE: line 88: The destination attribute for Ad Click Attribution could not be converted to a valid HTTP-family URL. >+CONSOLE MESSAGE: line 88: The destination attribute for Ad Click Attribution could not be converted to a valid HTTP-family URL. >+CONSOLE MESSAGE: line 88: The destination attribute for Ad Click Attribution could not be converted to a valid HTTP-family URL. >+CONSOLE MESSAGE: line 88: Both the campaign ID attribute and the destination attribute need to be set for Ad Click Attribution to work. >+CONSOLE MESSAGE: line 88: Both the campaign ID attribute and the destination attribute need to be set for Ad Click Attribution to work. >+Test for validity of ad click attribution attributes on anchor tags. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+Link1 >+Link2 >+Link3 >+Link4 >+Link5 >+Link6 >+Link7 >+Link8 >+Link9 >+Link10 >+Link11 >+Link12 >+Link13 >+Link14 >+ >diff --git a/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation.html b/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation.html >new file mode 100644 >index 0000000000000000000000000000000000000000..00f5c20ff612af02f231007f377de9dfe89dfbd1 >--- /dev/null >+++ b/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation.html >@@ -0,0 +1,76 @@ >+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AdClickAttributionEnabled=true ] --> >+<html lang="en"> >+<head> >+ <meta charset="UTF-8"> >+ <script src="/js-test-resources/js-test.js"></script> >+ <script src="/js-test-resources/ui-helper.js"></script> >+</head> >+<body onload="runAllTests()"> >+<script> >+ description("Test for validity of ad click attribution attributes on anchor tags."); >+ jsTestIsAsync = true; >+ >+ function createAdClickAttributionAnchorElement(elementID, adCampaignID, adDestination) { >+ let anchorElement = document.createElement("a"); >+ anchorElement.id = elementID; >+ anchorElement.adcampaignid = adCampaignID; >+ anchorElement.addestination = adDestination; >+ anchorElement.href = "#"; >+ anchorElement.innerText = "Link" + currentTest; >+ return anchorElement; >+ } >+ >+ function activateElement(elementID, callback) { >+ var element = document.getElementById(elementID); >+ var centerX = element.offsetLeft + element.offsetWidth / 2; >+ var centerY = element.offsetTop + element.offsetHeight / 2; >+ UIHelper.activateAt(centerX, centerY).then( >+ function () { >+ callback(); >+ }, >+ function () { >+ testFailed("Promise rejected."); >+ finishJSTest(); >+ } >+ ); >+ } >+ >+ let currentTest = 0; >+ function runOneTest(adCampaignID, adDestination, callback) { >+ const currentElementID = "test" + currentTest++; >+ const anchorElement = createAdClickAttributionAnchorElement(currentElementID, adCampaignID, adDestination); >+ document.body.appendChild(anchorElement); >+ const brElement = document.createElement("br"); >+ document.body.appendChild(brElement); >+ activateElement(currentElementID, callback); >+ } >+ >+ const validAdCampaignID = "03"; >+ const validAdDestination = "http://webkit.org"; >+ const testCases = [ >+ [ validAdCampaignID, validAdDestination ], >+ [ "100", validAdDestination ], // Too many characters. >+ [ "1", validAdDestination ], // Too few characters. >+ [ "98", validAdDestination ], // Too high value. >+ [ "-1", validAdDestination ], // Negative value. >+ [ "ab", validAdDestination ], // Non-digits. >+ [ "1", validAdDestination ], // Non-ASCII. >+ [ " 1", validAdDestination ], // Leading space. >+ [ "1 ", validAdDestination ], // Trailing space. >+ [ validAdCampaignID, "webkit.org" ], // Missing protocol. >+ [ validAdCampaignID, "://webkit.org" ], // Partially missing protocol. >+ [ validAdCampaignID, "" ], // Non-ASCII characters as destination. >+ [ "", validAdDestination ], // Empty campaign ID. >+ [ validAdCampaignID, "" ] // Empty destination. >+ ]; >+ >+ function runAllTests() { >+ if (currentTest < testCases.length) >+ runOneTest(testCases[currentTest][0], testCases[currentTest][1], runAllTests); >+ else >+ finishJSTest(); >+ } >+ >+</script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 194104
:
360752
|
360754
|
360783
|
360790
|
360803
|
360816
|
360820
|
360894
|
360900
|
360904
|
361029
|
361031