| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "core/experiments/Experiments.h" | |
| 6 | |
| 7 #include "core/HTMLNames.h" | |
| 8 #include "core/dom/DOMException.h" | |
| 9 #include "core/dom/ExceptionCode.h" | |
| 10 #include "core/frame/FrameView.h" | |
| 11 #include "core/html/HTMLDocument.h" | |
| 12 #include "core/html/HTMLHeadElement.h" | |
| 13 #include "core/html/HTMLMetaElement.h" | |
| 14 #include "core/testing/DummyPageHolder.h" | |
| 15 #include "platform/weborigin/KURL.h" | |
| 16 #include "platform/weborigin/SecurityOrigin.h" | |
| 17 #include "public/platform/WebApiKeyValidator.h" | |
| 18 #include "testing/gtest/include/gtest/gtest.h" | |
| 19 | |
| 20 namespace blink { | |
| 21 namespace { | |
| 22 | |
| 23 const char kNonExistingAPIName[] = "This API does not exist"; | |
| 24 const char kFrobulateAPIName[] = "Frobulate"; | |
| 25 const char kFrobulateEnabledOrigin[] = "https://www.example.com"; | |
| 26 const char kFrobulateEnabledOriginUnsecure[] = "http://www.example.com"; | |
| 27 | |
| 28 // API Key that will appear valid. | |
| 29 const char kGoodAPIKey[] = "AnySignatureWillDo|https://www.example.com|Frobulate
|2000000000"; | |
| 30 | |
| 31 class MockApiKeyValidator : public WebApiKeyValidator { | |
| 32 public: | |
| 33 MockApiKeyValidator() | |
| 34 : m_response(false) | |
| 35 , m_callCount(0) | |
| 36 { | |
| 37 } | |
| 38 ~MockApiKeyValidator() override {} | |
| 39 | |
| 40 // blink::WebApiKeyValidator implementation | |
| 41 bool validateApiKey(const blink::WebString& apiKey, const blink::WebString&
origin, const blink::WebString& apiName) override | |
| 42 { | |
| 43 m_callCount++; | |
| 44 return m_response; | |
| 45 } | |
| 46 | |
| 47 // Useful methods for controlling the validator | |
| 48 void setResponse(bool response) | |
| 49 { | |
| 50 m_response = response; | |
| 51 } | |
| 52 void reset() | |
| 53 { | |
| 54 m_response = false; | |
| 55 m_callCount = 0; | |
| 56 } | |
| 57 int callCount() | |
| 58 { | |
| 59 return m_callCount; | |
| 60 } | |
| 61 | |
| 62 private: | |
| 63 bool m_response; | |
| 64 int m_callCount; | |
| 65 | |
| 66 DISALLOW_COPY_AND_ASSIGN(MockApiKeyValidator); | |
| 67 }; | |
| 68 | |
| 69 } // namespace | |
| 70 | |
| 71 class ExperimentsTest : public ::testing::Test { | |
| 72 protected: | |
| 73 ExperimentsTest() | |
| 74 : m_page(DummyPageHolder::create()) | |
| 75 , m_frameworkWasEnabled(RuntimeEnabledFeatures::experimentalFrameworkEna
bled()) | |
| 76 , m_apiKeyValidator(adoptPtr(new MockApiKeyValidator())) | |
| 77 { | |
| 78 if (!RuntimeEnabledFeatures::experimentalFrameworkEnabled()) { | |
| 79 RuntimeEnabledFeatures::setExperimentalFrameworkEnabled(true); | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 ~ExperimentsTest() | |
| 84 { | |
| 85 if (!m_frameworkWasEnabled) { | |
| 86 RuntimeEnabledFeatures::setExperimentalFrameworkEnabled(false); | |
| 87 } | |
| 88 | |
| 89 m_page.clear(); | |
| 90 } | |
| 91 | |
| 92 void SetUp() override | |
| 93 { | |
| 94 m_document = toHTMLDocument(&m_page->document()); | |
| 95 setInnerHTML( | |
| 96 "<html>" | |
| 97 "<head>" | |
| 98 "</head>" | |
| 99 "<body>" | |
| 100 "</body>" | |
| 101 "</html>"); | |
| 102 } | |
| 103 | |
| 104 ExecutionContext* executionContext() { return &(m_page->document()); } | |
| 105 MockApiKeyValidator* apiKeyValidator() { return m_apiKeyValidator.get(); } | |
| 106 HTMLDocument& document() const { return *m_document; } | |
| 107 | |
| 108 void setPageOrigin(const String& origin) | |
| 109 { | |
| 110 KURL pageURL(ParsedURLString, origin); | |
| 111 RefPtr<SecurityOrigin> pageOrigin = SecurityOrigin::create(pageURL); | |
| 112 m_page->document().updateSecurityOrigin(pageOrigin); | |
| 113 } | |
| 114 | |
| 115 void setInnerHTML(const char* htmlContent) | |
| 116 { | |
| 117 document().documentElement()->setInnerHTML(String::fromUTF8(htmlContent)
, ASSERT_NO_EXCEPTION); | |
| 118 document().view()->updateAllLifecyclePhases(); | |
| 119 } | |
| 120 | |
| 121 void addApiKey(const String& keyValue) | |
| 122 { | |
| 123 HTMLElement* head = document().head(); | |
| 124 ASSERT_TRUE(head); | |
| 125 | |
| 126 RefPtrWillBeRawPtr<HTMLMetaElement> meta = HTMLMetaElement::create(docum
ent()); | |
| 127 meta->setAttribute(HTMLNames::nameAttr, "api-experiments"); | |
| 128 AtomicString value(keyValue); | |
| 129 meta->setAttribute(HTMLNames::contentAttr, value); | |
| 130 head->appendChild(meta.release()); | |
| 131 } | |
| 132 | |
| 133 bool isApiEnabled(const String& origin, const String& apiName, const String&
apiKeyValue, String* errorMessage) | |
| 134 { | |
| 135 setPageOrigin(origin); | |
| 136 addApiKey(apiKeyValue); | |
| 137 return Experiments::isApiEnabled(executionContext(), apiName, errorMessa
ge, apiKeyValidator()); | |
| 138 } | |
| 139 | |
| 140 bool isApiEnabledWithoutErrorMessage(const String& origin, const String& api
Name, const char* apiKeyValue) | |
| 141 { | |
| 142 return isApiEnabled(origin, apiName, apiKeyValue, nullptr); | |
| 143 } | |
| 144 | |
| 145 private: | |
| 146 OwnPtr<DummyPageHolder> m_page; | |
| 147 RefPtrWillBePersistent<HTMLDocument> m_document; | |
| 148 const bool m_frameworkWasEnabled; | |
| 149 OwnPtr<MockApiKeyValidator> m_apiKeyValidator; | |
| 150 }; | |
| 151 | |
| 152 TEST_F(ExperimentsTest, EnabledNonExistingAPI) | |
| 153 { | |
| 154 String errorMessage; | |
| 155 bool isNonExistingApiEnabled = isApiEnabled(kFrobulateEnabledOrigin, | |
| 156 kNonExistingAPIName, | |
| 157 kGoodAPIKey, | |
| 158 &errorMessage); | |
| 159 EXPECT_FALSE(isNonExistingApiEnabled); | |
| 160 EXPECT_EQ(("The provided key(s) are not valid for the 'This API does not exi
st' API."), errorMessage); | |
| 161 } | |
| 162 | |
| 163 TEST_F(ExperimentsTest, EnabledNonExistingAPIWithoutErrorMessage) | |
| 164 { | |
| 165 bool isNonExistingApiEnabled = isApiEnabledWithoutErrorMessage( | |
| 166 kFrobulateEnabledOrigin, | |
| 167 kNonExistingAPIName, | |
| 168 kGoodAPIKey); | |
| 169 EXPECT_FALSE(isNonExistingApiEnabled); | |
| 170 } | |
| 171 | |
| 172 // The API should be enabled if a valid key for the origin is provided | |
| 173 TEST_F(ExperimentsTest, EnabledSecureRegisteredOrigin) | |
| 174 { | |
| 175 String errorMessage; | |
| 176 apiKeyValidator()->setResponse(true); | |
| 177 bool isOriginEnabled = isApiEnabled(kFrobulateEnabledOrigin, | |
| 178 kFrobulateAPIName, | |
| 179 kGoodAPIKey, | |
| 180 &errorMessage); | |
| 181 EXPECT_TRUE(isOriginEnabled); | |
| 182 EXPECT_TRUE(errorMessage.isEmpty()) << "Message should be empty, was: " << e
rrorMessage; | |
| 183 EXPECT_EQ(1, apiKeyValidator()->callCount()); | |
| 184 } | |
| 185 | |
| 186 // ... but if the browser says it's invalid for any reason, that's enough to | |
| 187 // reject. | |
| 188 TEST_F(ExperimentsTest, InvalidKeyResponseFromPlatform) | |
| 189 { | |
| 190 String errorMessage; | |
| 191 apiKeyValidator()->setResponse(false); | |
| 192 bool isOriginEnabled = isApiEnabled(kFrobulateEnabledOrigin, | |
| 193 kFrobulateAPIName, | |
| 194 kGoodAPIKey, | |
| 195 &errorMessage); | |
| 196 EXPECT_FALSE(isOriginEnabled); | |
| 197 EXPECT_EQ(("The provided key(s) are not valid for the 'Frobulate' API."), er
rorMessage); | |
| 198 EXPECT_EQ(1, apiKeyValidator()->callCount()); | |
| 199 } | |
| 200 | |
| 201 TEST_F(ExperimentsTest, EnabledSecureRegisteredOriginWithoutErrorMessage) | |
| 202 { | |
| 203 apiKeyValidator()->setResponse(true); | |
| 204 bool isOriginEnabled = isApiEnabledWithoutErrorMessage( | |
| 205 kFrobulateEnabledOrigin, | |
| 206 kFrobulateAPIName, | |
| 207 kGoodAPIKey); | |
| 208 EXPECT_TRUE(isOriginEnabled); | |
| 209 EXPECT_EQ(1, apiKeyValidator()->callCount()); | |
| 210 } | |
| 211 | |
| 212 // The API should not be enabled if the origin is unsecure, even if a valid | |
| 213 // key for the origin is provided | |
| 214 TEST_F(ExperimentsTest, EnabledNonSecureRegisteredOrigin) | |
| 215 { | |
| 216 String errorMessage; | |
| 217 bool isOriginEnabled = isApiEnabled(kFrobulateEnabledOriginUnsecure, | |
| 218 kFrobulateAPIName, | |
| 219 kGoodAPIKey, | |
| 220 &errorMessage); | |
| 221 EXPECT_FALSE(isOriginEnabled); | |
| 222 EXPECT_TRUE(errorMessage.contains("secure origin")) << "Message should indic
ate only secure origins are allowed, was: " << errorMessage; | |
| 223 EXPECT_EQ(0, apiKeyValidator()->callCount()); | |
| 224 } | |
| 225 | |
| 226 TEST_F(ExperimentsTest, DisabledException) | |
| 227 { | |
| 228 DOMException* disabledException = Experiments::createApiDisabledException(kN
onExistingAPIName); | |
| 229 ASSERT_TRUE(disabledException) << "An exception should have been created"; | |
| 230 EXPECT_EQ(DOMException::getErrorName(NotSupportedError), disabledException->
name()); | |
| 231 EXPECT_TRUE(disabledException->message().contains(kNonExistingAPIName)) << "
Message should contain the API name, was: " << disabledException->message(); | |
| 232 } | |
| 233 | |
| 234 } // namespace blink | |
| OLD | NEW |