1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file. 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/files/file_path.h" 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/macros.h" 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/strings/stringprintf.h" 86d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/extension_browsertest.h" 96d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/extensions/test_extension_dir.h" 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/browser.h" 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h" 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/test/base/ui_test_utils.h" 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/test/browser_test_utils.h" 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/test/extension_test_message_listener.h" 15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "net/test/embedded_test_server/embedded_test_server.h" 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h" 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace extensions { 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace { 21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Manifest permissions injected into |kManifest|: 235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuconst char* kPermissions[] = { 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "*://*/*", // ALL 255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu "http://127.0.0.1/*", // PARTICULAR 265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu "http://nowhere.com/*" // NOWHERE 275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Script matchers for injected into |kBackgroundScriptSource|: 305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuconst char* kScriptMatchers[] = { 315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu "{ pageUrl: { hostContains: '' } }", // ALL 32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "{ pageUrl: { hostEquals: '127.0.0.1' } }", // PARTICULAR 33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "{ pageUrl: { hostEquals: 'nowhere.com' } }" // NOWHERE 34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)enum PermissionOrMatcherType { 37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ALL = 0, 3846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) PARTICULAR, 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) NOWHERE 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}; 4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// JSON/JS sources: 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)const char kManifest[] = 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) "{\n" 4546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " \"name\": \"Test DeclarativeContentScript\",\n" 4646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " \"manifest_version\": 2,\n" 476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " \"version\": \"1.0\",\n" 4846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " \"description\": \"Test declarative content script interface\",\n" 496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " \"permissions\": [\"declarativeContent\", \"%s\"],\n" 506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " \"background\": {\n" 516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " \"scripts\": [\"background.js\"]\n" 526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " }\n" 536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) "}\n"; 546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)const char kBackgroundScriptSource[] = 556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) "var declarativeContent = chrome.declarativeContent;\n" 566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) "var PageStateMatcher = declarativeContent.PageStateMatcher;\n" 576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) "var RequestContentScript = declarativeContent.RequestContentScript;\n" 586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) "var onPageChanged = declarativeContent.onPageChanged;\n" 596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) "onPageChanged.removeRules(undefined, function() {\n" 606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " onPageChanged.addRules(\n" 616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " [{\n" 626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " conditions: [new PageStateMatcher(%s)],\n" 636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) " actions: [new RequestContentScript({js: ['script.js']}\n" 6446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " )]\n" 6546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " }],\n" 6646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " function(details) {\n" 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " if (!chrome.runtime.lastError)\n" 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) " chrome.test.sendMessage('injection setup');\n" 6946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " }\n" 7046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) " );\n" 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "});\n"; 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const char kContentScriptSource[] = 7303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) "chrome.test.sendMessage('injection succeeded');\n"; 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Messages from scripts: 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const char kInjectionSetup[] = "injection setup"; 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const char kInjectionSucceeded[] = "injection succeeded"; 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Runs all pending tasks in the renderer associated with |web_contents|. 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Returns true on success. 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool RunAllPendingInRenderer(content::WebContents* web_contents) { 82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // TODO(devlin): If too many tests start to need this, move it somewhere 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // common. 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // This is slight hack to achieve a RunPendingInRenderer() method. Since IPCs 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // are sent synchronously, anything started prior to this method will finish 86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // before this method returns (as content::ExecuteScript() is synchronous). 87 return content::ExecuteScript(web_contents, "1 == 1;"); 88} 89 90} // namespace 91 92class RequestContentScriptAPITest : public ExtensionBrowserTest { 93 public: 94 RequestContentScriptAPITest(); 95 virtual ~RequestContentScriptAPITest() {} 96 97 // Performs script injection test on a common local URL using the given 98 // |manifest_permission| and |script_matcher|. Does not return until 99 // the renderer should have completed its task and any browser-side reactions 100 // have been cleared from the task queue. 101 testing::AssertionResult RunTest(PermissionOrMatcherType manifest_permission, 102 PermissionOrMatcherType script_matcher, 103 bool should_inject); 104 105 private: 106 testing::AssertionResult CreateAndLoadExtension( 107 PermissionOrMatcherType manifest_permission, 108 PermissionOrMatcherType script_matcher); 109 110 scoped_ptr<TestExtensionDir> test_extension_dir_; 111 const Extension* extension_; 112}; 113 114RequestContentScriptAPITest::RequestContentScriptAPITest() 115 : extension_(NULL) {} 116 117testing::AssertionResult RequestContentScriptAPITest::RunTest( 118 PermissionOrMatcherType manifest_permission, 119 PermissionOrMatcherType script_matcher, 120 bool should_inject) { 121 if (extension_) 122 UnloadExtension(extension_->id()); 123 testing::AssertionResult result = CreateAndLoadExtension(manifest_permission, 124 script_matcher); 125 if (!result) 126 return result; 127 128 // Setup listener for actual injection of script. 129 ExtensionTestMessageListener injection_succeeded_listener( 130 kInjectionSucceeded, 131 false /* won't reply */); 132 injection_succeeded_listener.set_extension_id(extension_->id()); 133 134 ui_test_utils::NavigateToURL( 135 browser(), 136 embedded_test_server()->GetURL("/extensions/test_file.html")); 137 138 content::WebContents* web_contents = 139 browser() ? browser()->tab_strip_model()->GetActiveWebContents() : NULL; 140 if (!web_contents) 141 return testing::AssertionFailure() << "No web contents."; 142 143 // Give the extension plenty of time to inject. 144 if (!RunAllPendingInRenderer(web_contents)) 145 return testing::AssertionFailure() << "Could not run pending in renderer."; 146 147 // Make sure all running tasks are complete. 148 content::RunAllPendingInMessageLoop(); 149 150 if (injection_succeeded_listener.was_satisfied() != should_inject) { 151 return testing::AssertionFailure() 152 << (should_inject ? 153 "Expected injection, but got none." : 154 "Expected no injection, but got one."); 155 } 156 157 return testing::AssertionSuccess(); 158} 159 160testing::AssertionResult RequestContentScriptAPITest::CreateAndLoadExtension( 161 PermissionOrMatcherType manifest_permission, 162 PermissionOrMatcherType script_matcher) { 163 // Setup a listener to note when injection rules have been setup. 164 ExtensionTestMessageListener injection_setup_listener( 165 kInjectionSetup, 166 false /* won't reply */); 167 168 std::string manifest = base::StringPrintf(kManifest, 169 kPermissions[manifest_permission]); 170 std::string background_src = base::StringPrintf( 171 kBackgroundScriptSource, 172 kScriptMatchers[script_matcher]); 173 174 scoped_ptr<TestExtensionDir> dir(new TestExtensionDir); 175 dir->WriteManifest(manifest); 176 dir->WriteFile(FILE_PATH_LITERAL("background.js"), background_src); 177 dir->WriteFile(FILE_PATH_LITERAL("script.js"), 178 kContentScriptSource); 179 180 const Extension* extension = LoadExtension(dir->unpacked_path()); 181 if (!extension) 182 return testing::AssertionFailure() << "Failed to load extension."; 183 184 test_extension_dir_.reset(dir.release()); 185 extension_ = extension; 186 187 // Wait for rules to be setup before navigating to trigger script injection. 188 injection_setup_listener.WaitUntilSatisfied(); 189 190 return testing::AssertionSuccess(); 191} 192 193 194// Try different permutations of "match all", "match particular domain (that is 195// visited by test)", and "match nonsense domain (not visited by test)" for 196// both manifest permissions and injection matcher conditions. 197IN_PROC_BROWSER_TEST_F(RequestContentScriptAPITest, 198 PermissionMatcherAgreementInjection) { 199 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 200 201 // Positive tests: permissions and matcher contain conditions that match URL 202 // visited during test. 203 EXPECT_TRUE(RunTest(ALL, ALL, true)); 204 EXPECT_TRUE(RunTest(ALL, PARTICULAR, true)); 205 EXPECT_TRUE(RunTest(PARTICULAR, ALL, true)); 206 EXPECT_TRUE(RunTest(PARTICULAR, PARTICULAR, true)); 207 208 // Negative tests: permissions or matcher (or both) contain conditions that 209 // do not match URL visited during test. 210 EXPECT_TRUE(RunTest(NOWHERE, ALL, false)); 211 EXPECT_TRUE(RunTest(NOWHERE, PARTICULAR, false)); 212 EXPECT_TRUE(RunTest(NOWHERE, NOWHERE, false)); 213 EXPECT_TRUE(RunTest(ALL, NOWHERE, false)); 214 EXPECT_TRUE(RunTest(PARTICULAR, NOWHERE, false)); 215 216 // TODO(markdittmer): Add more tests: 217 // - Inject script with multiple files 218 // - Inject multiple scripts 219 // - Match on CSS selector conditions 220 // - Match all frames in document containing frames 221} 222 223} // namespace extensions 224