1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ssl/https_upgrades_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_db_task.h"
#include "components/history/core/browser/history_service.h"
#include "components/history/core/browser/history_types.h"
#include "components/history/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/sync/test/test_sync_service.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/process_manager.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/test_extension_dir.h"
#include "net/dns/mock_host_resolver.h"
#include "url/gurl.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
namespace {
class AddSyncedVisitTask : public history::HistoryDBTask {
public:
AddSyncedVisitTask(base::RunLoop* run_loop,
const GURL& url,
const history::VisitRow& visit)
: run_loop_(run_loop), url_(url), visit_(visit) {}
AddSyncedVisitTask(const AddSyncedVisitTask&) = delete;
AddSyncedVisitTask& operator=(const AddSyncedVisitTask&) = delete;
~AddSyncedVisitTask() override = default;
bool RunOnDBThread(history::HistoryBackend* backend,
history::HistoryDatabase* db) override {
history::VisitID visit_id = backend->AddSyncedVisit(
url_, u"Title", /*hidden=*/false, visit_, std::nullopt, std::nullopt);
EXPECT_NE(visit_id, history::kInvalidVisitID);
return true;
}
void DoneRunOnMainThread() override { run_loop_->QuitWhenIdle(); }
private:
raw_ptr<base::RunLoop> run_loop_;
GURL url_;
history::VisitRow visit_;
};
} // namespace
namespace extensions {
using ContextType = extensions::browser_test_util::ContextType;
class HistoryApiTest : public ExtensionApiTest,
public testing::WithParamInterface<ContextType> {
public:
HistoryApiTest() : ExtensionApiTest(GetParam()) {}
void SetUpOnMainThread() override {
ExtensionApiTest::SetUpOnMainThread();
host_resolver()->AddRule("www.a.com", "127.0.0.1");
host_resolver()->AddRule("www.b.com", "127.0.0.1");
}
static std::string ExecuteScript(const ExtensionId& extension_id,
content::BrowserContext* context,
const std::string& script) {
base::Value result = BackgroundScriptExecutor::ExecuteScript(
context, extension_id, script,
BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
EXPECT_TRUE(result.is_string());
return result.is_string() ? result.GetString() : std::string();
}
};
// Android only supports MV3 and later, therefore don't need to test for
// persistent background context.
#if !BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(PersistentBackground,
HistoryApiTest,
::testing::Values(ContextType::kPersistentBackground));
#endif // !BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(ServiceWorker,
HistoryApiTest,
::testing::Values(ContextType::kServiceWorker));
class SyncEnabledHistoryApiTest : public HistoryApiTest {
public:
void SetUpBrowserContextKeyedServices(
content::BrowserContext* context) override {
HistoryApiTest::SetUpBrowserContextKeyedServices(context);
// Set up a fake SyncService that'll pretend Sync is enabled (without
// actually talking to the server, or syncing anything). This is required
// for tests exercising "foreign" (aka synced) visits - without this, the
// HistoryBackend will notice that Sync isn't enabled and delete all foreign
// visits.
SyncServiceFactory::GetInstance()->SetTestingFactory(
context,
base::BindRepeating(
[](content::BrowserContext*) -> std::unique_ptr<KeyedService> {
return std::make_unique<syncer::TestSyncService>();
}));
}
};
INSTANTIATE_TEST_SUITE_P(PersistentBackground,
SyncEnabledHistoryApiTest,
::testing::Values(ContextType::kPersistentBackground));
INSTANTIATE_TEST_SUITE_P(ServiceWorker,
SyncEnabledHistoryApiTest,
::testing::Values(ContextType::kServiceWorker));
IN_PROC_BROWSER_TEST_P(HistoryApiTest, MiscSearch) {
ASSERT_TRUE(StartEmbeddedTestServer());
ASSERT_TRUE(RunExtensionTest("history/regular/misc_search")) << message_;
}
IN_PROC_BROWSER_TEST_P(HistoryApiTest, TimedSearch) {
ASSERT_TRUE(StartEmbeddedTestServer());
ASSERT_TRUE(RunExtensionTest("history/regular/timed_search")) << message_;
}
IN_PROC_BROWSER_TEST_P(HistoryApiTest, Delete) {
ASSERT_TRUE(StartEmbeddedTestServer());
ASSERT_TRUE(RunExtensionTest("history/regular/delete")) << message_;
}
IN_PROC_BROWSER_TEST_P(HistoryApiTest, DeleteProhibited) {
profile()->GetPrefs()->SetBoolean(prefs::kAllowDeletingBrowserHistory, false);
ASSERT_TRUE(StartEmbeddedTestServer());
ASSERT_TRUE(RunExtensionTest("history/regular/delete_prohibited"))
<< message_;
}
IN_PROC_BROWSER_TEST_P(HistoryApiTest, GetVisits) {
ASSERT_TRUE(StartEmbeddedTestServer());
ASSERT_TRUE(RunExtensionTest("history/regular/get_visits")) << message_;
}
IN_PROC_BROWSER_TEST_P(SyncEnabledHistoryApiTest, GetVisits_Foreign) {
ASSERT_TRUE(StartEmbeddedTestServer());
// Setup: Add a foreign (aka synced) history entry to the DB.
history::VisitRow visit;
visit.visit_time = base::Time::Now() - base::Minutes(1);
visit.originator_cache_guid = "some_originator";
visit.transition = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CHAIN_END);
visit.is_known_to_sync = true;
base::CancelableTaskTracker tracker;
base::RunLoop run_loop;
history::HistoryService* history_service =
HistoryServiceFactory::GetForProfile(profile(),
ServiceAccessType::EXPLICIT_ACCESS);
history_service->ScheduleDBTask(
FROM_HERE,
std::make_unique<AddSyncedVisitTask>(
&run_loop, GURL("https://www.synced.com/"), visit),
&tracker);
run_loop.Run();
static constexpr char kManifest[] =
R"({
"name": "chrome.history",
"version": "0.1",
"manifest_version": 2,
"permissions": ["history"],
"background": {
"scripts": ["get_visits_foreign.js"],
"persistent": true
}
})";
static constexpr char kBackgroundJs[] =
R"(chrome.test.runTests([
function getVisits() {
var query = {text: ''};
chrome.history.search(query, function(results) {
chrome.test.assertEq(1, results.length);
chrome.test.assertEq('https://www.synced.com/', results[0].url);
var id = results[0].id;
chrome.history.getVisits(
{url: results[0].url}, function(results) {
chrome.test.assertEq(1, results.length);
chrome.test.assertEq(id, results[0].id);
// The visit is *not* local!
chrome.test.assertFalse(results[0].isLocal);
chrome.test.succeed();
});
});
}
]);)";
TestExtensionDir test_dir;
test_dir.WriteManifest(kManifest);
test_dir.WriteFile(FILE_PATH_LITERAL("get_visits_foreign.js"), kBackgroundJs);
ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
}
IN_PROC_BROWSER_TEST_P(HistoryApiTest, SearchAfterAdd) {
ASSERT_TRUE(StartEmbeddedTestServer());
ASSERT_TRUE(RunExtensionTest("history/regular/search_after_add")) << message_;
}
// Test when History API is used from incognito mode, it has access to the
// regular mode history and actual incognito navigation has no effect on it.
IN_PROC_BROWSER_TEST_P(HistoryApiTest, Incognito) {
// TODO(crbug.com/40937027): Convert test to use HTTPS and then remove.
ScopedAllowHttpForHostnamesForTesting allow_http({"www.b.com"},
profile()->GetPrefs());
ASSERT_TRUE(StartEmbeddedTestServer());
Profile* incognito_profile =
profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
ExtensionTestMessageListener regular_listener("regular ready");
ExtensionTestMessageListener incognito_listener("incognito ready");
const Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("history/incognito"),
{.allow_in_incognito = true});
ASSERT_TRUE(extension);
ASSERT_TRUE(regular_listener.WaitUntilSatisfied());
ASSERT_TRUE(incognito_listener.WaitUntilSatisfied());
const ExtensionId& extension_id = extension->id();
// Check if history is empty in regular mode.
EXPECT_EQ("0",
ExecuteScript(extension_id, profile(), "countItemsInHistory()"));
// Insert an item in incognito mode.
EXPECT_EQ("success",
ExecuteScript(extension_id, incognito_profile, "addItem()"));
// Check history in incognito mode.
EXPECT_EQ("1", ExecuteScript(extension_id, incognito_profile,
"countItemsInHistory()"));
// Check history in regular mode.
EXPECT_EQ("1",
ExecuteScript(extension_id, profile(), "countItemsInHistory()"));
// Perform navigation in incognito mode.
const GURL b_com =
embedded_test_server()->GetURL("www.b.com", "/simple.html");
PlatformOpenURLOffTheRecord(incognito_profile, b_com);
// Check history in regular mode is not modified by incognito navigation.
EXPECT_EQ("1",
ExecuteScript(extension_id, profile(), "countItemsInHistory()"));
// Check that history in incognito mode is not modified by navigation as
// incognito navigations are not recorded in history.
EXPECT_EQ("1", ExecuteScript(extension_id, incognito_profile,
"countItemsInHistory()"));
// Perform navigation in regular mode.
content::TestNavigationObserver regular_observer(GetActiveWebContents());
ASSERT_TRUE(NavigateToURL(b_com));
EXPECT_TRUE(regular_observer.last_navigation_succeeded());
// Check history in regular mode is modified by navigation.
EXPECT_EQ("2",
ExecuteScript(extension_id, profile(), "countItemsInHistory()"));
// Check history in incognito mode is modified by navigation.
EXPECT_EQ("2", ExecuteScript(extension_id, incognito_profile,
"countItemsInHistory()"));
}
} // namespace extensions
|