Chromium Code Reviews| Index: chrome/browser/extensions/api/declarative_content/request_content_script_browsertest.cc |
| diff --git a/chrome/browser/extensions/api/declarative_content/request_content_script_browsertest.cc b/chrome/browser/extensions/api/declarative_content/request_content_script_browsertest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b3a12e2bdc4300cc15e8c45af74de2611d6a00c1 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/api/declarative_content/request_content_script_browsertest.cc |
| @@ -0,0 +1,243 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
|
Devlin
2014/08/26 21:59:16
nit of nits: This is actually more of an API test
Mark Dittmer
2014/08/27 22:37:04
File renamed.
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "base/files/file_path.h" |
| +#include "base/macros.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "chrome/browser/extensions/extension_action.h" |
| +#include "chrome/browser/extensions/extension_browsertest.h" |
| +#include "chrome/browser/extensions/extension_test_message_listener.h" |
| +#include "chrome/browser/extensions/test_extension_dir.h" |
| +#include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/tabs/tab_strip_model.h" |
| +#include "chrome/test/base/ui_test_utils.h" |
| +#include "content/public/test/browser_test_utils.h" |
| +#include "net/test/embedded_test_server/embedded_test_server.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace extensions { |
| + |
| +namespace { |
| + |
| +// Manifest permissions injected into |kManifest|: |
| +const char* kPermissions[] = { |
| + "*://*/*", // MP_ALL |
| + "http://127.0.0.1/*", // MP_PARTICULAR |
| + "http://nowhere.com/*" // MP_NOWHERE |
| +}; |
| + |
| +// Script matchers for injected into |kBackgroundScriptSource|: |
| +const char* kScriptMatchers[] = { |
| + "{ pageUrl: { hostContains: '' } }", // SM_ALL |
| + "{ pageUrl: { hostEquals: '127.0.0.1' } }", // SM_PARTICULAR |
| + "{ pageUrl: { hostEquals: 'nowhere.com' } }" // SM_NOWHERE |
| +}; |
| + |
| +// JSON/JS sources: |
| +const char kManifest[] = |
| + "{\n" |
| + " \"name\": \"Test DeclarativeContentScript\",\n" |
| + " \"manifest_version\": 2,\n" |
| + " \"version\": \"1.0\",\n" |
| + " \"description\": \"Test declarative content script interface\",\n" |
| + " \"permissions\": [\"declarativeContent\", \"%s\"],\n" |
| + " \"background\": {\n" |
| + " \"scripts\": [\"background.js\"]\n" |
| + " }\n" |
| + "}\n"; |
| +const char kBackgroundScriptSource[] = |
| + "var declarativeContent = chrome.declarativeContent;\n" |
| + "var PageStateMatcher = declarativeContent.PageStateMatcher;\n" |
| + "var RequestContentScript = declarativeContent.RequestContentScript;\n" |
| + "var ShowPageAction = declarativeContent.ShowPageAction;\n" |
| + "var onPageChanged = declarativeContent.onPageChanged;\n" |
| + "onPageChanged.removeRules(undefined, function() {\n" |
| + " onPageChanged.addRules(\n" |
| + " [{\n" |
| + " conditions: [new PageStateMatcher(%s)],\n" |
| + " actions: [new RequestContentScript({js: ['script.js']}\n" |
| + " )]\n" |
| + " }],\n" |
| + " function(details) {\n" |
| + " if (!chrome.runtime.lastError)\n" |
| + " chrome.test.sendMessage('injection setup');\n" |
| + " }\n" |
| + " );\n" |
| + "});\n"; |
| +const char kContentScriptSource[] = |
| + "chrome.test.sendMessage('injection succeeded');\n"; |
| + |
| +// Messages from scripts: |
| +const char kInjectionSetup[] = "injection setup"; |
| +const char kInjectionSucceeded[] = "injection succeeded"; |
| + |
| +enum ManifestPermissionType { |
| + MP_ALL = 0, |
|
Devlin
2014/08/26 21:59:16
If this and ScriptMatcherType are always gonna be
Mark Dittmer
2014/08/27 22:37:04
Currently, they fit into the same categorization s
|
| + MP_PARTICULAR, |
| + MP_NOWHERE |
| +}; |
| + |
| +enum ScriptMatcherType { |
| + SM_ALL = 0, |
| + SM_PARTICULAR, |
| + SM_NOWHERE |
| +}; |
| + |
| +// Runs all pending tasks in the renderer associated with |web_contents|. |
| +// Returns true on success. |
| +bool RunAllPendingInRenderer(content::WebContents* web_contents) { |
| + // TODO(devlin): If too many tests start to need this, move it somewhere |
| + // common. |
| + // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs |
| + // are sent synchronously, anything started prior to this method will finish |
| + // before this method returns (as content::ExecuteScript() is synchronous). |
| + return content::ExecuteScript(web_contents, "1 == 1;"); |
| +} |
| + |
| +} // namespace |
| + |
| +class RequestContentScriptBrowserTest : public ExtensionBrowserTest { |
| + public: |
| + RequestContentScriptBrowserTest(); |
| + |
| + // Performs script injection test on a common local URL using the given |
| + // |manifest_permission| and |script_matcher|. Does not return until |
| + // the renderer should have completed its task and any browser-side reactions |
| + // have been cleared from the task queue. |
| + testing::AssertionResult RunTest(ManifestPermissionType manifest_permission, |
|
Devlin
2014/08/26 21:59:16
indentation
Mark Dittmer
2014/08/27 22:37:04
Done.
|
| + ScriptMatcherType script_matcher); |
| + |
| + ExtensionTestMessageListener* injection_setup_listener() { |
| + return injection_setup_listener_.get(); |
| + } |
| + ExtensionTestMessageListener* injection_succeeded_listener() { |
| + return injection_succeeded_listener_.get(); |
| + } |
| + |
| + private: |
| + scoped_ptr<TestExtensionDir> test_extension_dir_; |
| + const Extension* extension_; |
| + scoped_ptr<ExtensionTestMessageListener> injection_setup_listener_; |
|
Devlin
2014/08/26 21:59:15
Why do these need to be member variables?
Mark Dittmer
2014/08/27 22:37:04
This ensures the lifetime of the listeners for use
Devlin
2014/08/27 23:01:11
See comment on new patch set.
|
| + scoped_ptr<ExtensionTestMessageListener> injection_succeeded_listener_; |
| + bool server_is_initialized_; |
| +}; |
| + |
| +RequestContentScriptBrowserTest::RequestContentScriptBrowserTest() |
| + : extension_(NULL), |
| + server_is_initialized_(false) {} |
| + |
| +testing::AssertionResult RequestContentScriptBrowserTest::RunTest( |
| + ManifestPermissionType manifest_permission, |
| + ScriptMatcherType script_matcher) { |
| + std::string manifest = base::StringPrintf(kManifest, |
|
Devlin
2014/08/26 21:59:16
Pull this part out into a CreatendLoadExtension(ty
Mark Dittmer
2014/08/28 13:02:41
Done.
|
| + kPermissions[manifest_permission]); |
| + std::string background_src = base::StringPrintf( |
| + kBackgroundScriptSource, |
| + kScriptMatchers[script_matcher]); |
| + |
| + scoped_ptr<TestExtensionDir> dir(new TestExtensionDir); |
| + dir->WriteManifest(manifest); |
| + dir->WriteFile(FILE_PATH_LITERAL("background.js"), background_src); |
| + dir->WriteFile(FILE_PATH_LITERAL("script.js"), |
| + kContentScriptSource); |
| + |
| + const Extension* extension = LoadExtension(dir->unpacked_path()); |
| + if (!extension) |
| + return testing::AssertionFailure() << "Failed to load extension."; |
| + |
| + if (extension_) |
|
Devlin
2014/08/26 21:59:15
nit: Cleanup _before_ creating the new stuff.
Mark Dittmer
2014/08/28 13:02:41
Done.
|
| + UnloadExtension(extension_->id()); |
| + |
| + test_extension_dir_.reset(dir.release()); |
| + extension_ = extension; |
| + |
| + injection_setup_listener_.reset(new ExtensionTestMessageListener( |
| + kInjectionSetup, |
| + false /* won't reply */)); |
| + injection_succeeded_listener_.reset(new ExtensionTestMessageListener( |
| + kInjectionSucceeded, |
| + false /* won't reply */)); |
| + |
| + injection_setup_listener_->set_extension_id(extension->id()); |
| + injection_succeeded_listener_->set_extension_id(extension->id()); |
| + |
| + if (!server_is_initialized_) { |
| + if (!embedded_test_server()->InitializeAndWaitUntilReady()) { |
|
Devlin
2014/08/26 21:59:15
Since this will only be done once, just do it in t
Mark Dittmer
2014/08/27 22:37:04
Done.
|
| + return testing::AssertionFailure() << |
| + "Could not initialize embedded test server."; |
| + } |
| + server_is_initialized_ = true; |
| + } |
| + |
| + // Wait for rules to be setup before navigating to trigger script injection. |
| + injection_setup_listener_->WaitUntilSatisfied(); |
| + |
|
Devlin
2014/08/26 21:59:15
We should also check that injection has *not* occu
Mark Dittmer
2014/08/27 22:37:04
Why? Done, just the same.
|
| + ui_test_utils::NavigateToURL( |
| + browser(), |
| + embedded_test_server()->GetURL("/extensions/test_file.html")); |
| + |
| + content::WebContents* web_contents = |
| + browser() ? browser()->tab_strip_model()->GetActiveWebContents() : NULL; |
| + if (!web_contents) |
| + return testing::AssertionFailure() << "No web contents."; |
| + |
| + // Give the extension plenty of time to inject. |
| + if (!RunAllPendingInRenderer(web_contents)) |
| + return testing::AssertionFailure() << "Could not run pending in renderer."; |
| + |
| + // Make sure all running tasks are complete. |
| + content::RunAllPendingInMessageLoop(); |
| + |
| + return testing::AssertionSuccess(); |
| +} |
| + |
| +// Try different permutations of "match all", "match particular domain (that is |
| +// visited by test)", and "match nonsense domain (not visited by test)" for |
| +// both manifest permissions and injection matcher conditions. |
| +IN_PROC_BROWSER_TEST_F(RequestContentScriptBrowserTest, |
| + PermissionMatcherAgreementInjection) { |
| + // Positive tests: permissions and matcher contain conditions that match URL |
| + // visited during test. |
| + EXPECT_TRUE(RunTest(MP_ALL, SM_ALL)); |
| + ASSERT_TRUE(injection_succeeded_listener()->was_satisfied()); |
|
Devlin
2014/08/26 21:59:16
Move this into RunTest as a third param (should_in
Mark Dittmer
2014/08/27 22:37:04
Done.
|
| + |
| + EXPECT_TRUE(RunTest(MP_ALL, SM_PARTICULAR)); |
| + ASSERT_TRUE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + EXPECT_TRUE(RunTest(MP_PARTICULAR, SM_ALL)); |
| + ASSERT_TRUE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + EXPECT_TRUE(RunTest(MP_PARTICULAR, SM_PARTICULAR)); |
| + ASSERT_TRUE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + // Negative tests: permissions or matcher (or both) contain conditions that |
| + // do not match URL visited during test. |
| + EXPECT_TRUE(RunTest(MP_NOWHERE, SM_ALL)); |
| + ASSERT_FALSE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + EXPECT_TRUE(RunTest(MP_NOWHERE, SM_PARTICULAR)); |
| + ASSERT_FALSE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + EXPECT_TRUE(RunTest(MP_NOWHERE, SM_NOWHERE)); |
| + ASSERT_FALSE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + EXPECT_TRUE(RunTest(MP_ALL, SM_NOWHERE)); |
| + ASSERT_FALSE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + EXPECT_TRUE(RunTest(MP_PARTICULAR, SM_NOWHERE)); |
| + ASSERT_FALSE(injection_succeeded_listener()->was_satisfied()); |
| + |
| + |
| + |
| + |
| + // EXPECT_TRUE(Init(MP_PARTICULAR, SM_ALL)); |
|
Devlin
2014/08/26 21:59:15
??
Mark Dittmer
2014/08/27 22:37:04
Experimentation leftovers. Removed.
|
| + // EXPECT_TRUE(RunTest()); |
| + // ASSERT_TRUE(injection_succeeded_listener()->WaitUntilSatisfied()); |
| + |
| + // EXPECT_TRUE(Init(MP_PARTICULAR, SM_ALL)); |
| + // EXPECT_TRUE(RunTest()); |
| + // ASSERT_TRUE(injection_succeeded_listener()->WaitUntilSatisfied()); |
| +} |
| + |
| +} // namespace extensions |