653 lines
20 KiB
JavaScript
653 lines
20 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
/*
|
|
* Test initializing from the search settings.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const legacyUseSavedOrderPrefName =
|
|
SearchUtils.BROWSER_SEARCH_PREF + "useDBForOrder";
|
|
|
|
var settingsTemplate;
|
|
|
|
/**
|
|
* Test reading from search.json.mozlz4
|
|
*/
|
|
add_setup(async function () {
|
|
SearchTestUtils.setRemoteSettingsConfig([
|
|
{ identifier: "engine1" },
|
|
{ identifier: "engine2" },
|
|
]);
|
|
await SearchTestUtils.initXPCShellAddonManager();
|
|
await Services.search.init();
|
|
});
|
|
|
|
async function loadSettingsFile(settingsFile, setVersion, setHashes) {
|
|
settingsTemplate = await readJSONFile(do_get_file(settingsFile));
|
|
if (setVersion) {
|
|
settingsTemplate.version = SearchUtils.SETTINGS_VERSION;
|
|
}
|
|
|
|
if (setHashes) {
|
|
settingsTemplate.metaData.hash = SearchUtils.getVerificationHash(
|
|
settingsTemplate.metaData.current
|
|
);
|
|
settingsTemplate.metaData.privateHash = SearchUtils.getVerificationHash(
|
|
settingsTemplate.metaData.private
|
|
);
|
|
}
|
|
|
|
delete settingsTemplate.visibleDefaultEngines;
|
|
|
|
await promiseSaveSettingsData(settingsTemplate);
|
|
}
|
|
|
|
/**
|
|
* Start the search service and confirm the engine properties match the expected values.
|
|
*
|
|
* @param {string} settingsFile
|
|
* The path to the settings file to use.
|
|
* @param {boolean} setVersion
|
|
* True if to set the version in the copied settings file.
|
|
* @param {boolean} expectedUseDBValue
|
|
* The value expected for the `useSavedOrder` metadata attribute.
|
|
*/
|
|
async function checkLoadSettingProperties(
|
|
settingsFile,
|
|
setVersion,
|
|
expectedUseDBValue
|
|
) {
|
|
info("init search service");
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
await loadSettingsFile(settingsFile, setVersion);
|
|
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
let engines = await ss.getEngines();
|
|
|
|
Assert.equal(
|
|
engines[0].name,
|
|
"engine1",
|
|
"Should have loaded the correct first engine"
|
|
);
|
|
Assert.equal(engines[0].alias, "testAlias", "Should have set the alias");
|
|
Assert.equal(engines[0].hidden, false, "Should have not hidden the engine");
|
|
Assert.equal(engines[0].id, "engine1");
|
|
|
|
Assert.equal(
|
|
engines[1].name,
|
|
"engine2",
|
|
"Should have loaded the correct second engine"
|
|
);
|
|
Assert.equal(engines[1].alias, "", "Should have not set the alias");
|
|
Assert.equal(engines[1].hidden, true, "Should have hidden the engine");
|
|
Assert.equal(engines[1].id, "engine2");
|
|
|
|
// The extra engine is the second in the list.
|
|
isSubObjectOf(EXPECTED_ENGINE.engine, engines[2], prop => {
|
|
return prop == "_iconURL";
|
|
});
|
|
Assert.ok(engines[2].id, "test-addon-id@mozilla.orgdefault");
|
|
|
|
let engineFromSS = ss.getEngineByName(EXPECTED_ENGINE.engine.name);
|
|
Assert.ok(!!engineFromSS);
|
|
isSubObjectOf(EXPECTED_ENGINE.engine, engineFromSS, prop => {
|
|
return prop == "_iconURL";
|
|
});
|
|
|
|
Assert.equal(
|
|
engineFromSS.getSubmission("foo").uri.spec,
|
|
"http://www.google.com/search?q=foo",
|
|
"Should have the correct URL with no mozparams"
|
|
);
|
|
|
|
Assert.equal(
|
|
ss._settings.getMetaDataAttribute("useSavedOrder"),
|
|
expectedUseDBValue,
|
|
"Should have set the useSavedOrder metadata correctly."
|
|
);
|
|
|
|
let migratedSettingsFile = await promiseSettingsData();
|
|
|
|
Assert.equal(migratedSettingsFile.engines[0].id, "engine1");
|
|
|
|
removeSettingsFile();
|
|
}
|
|
|
|
add_task(async function test_legacy_setting_engine_properties() {
|
|
Services.prefs.setBoolPref(legacyUseSavedOrderPrefName, true);
|
|
|
|
let legacySettings = await readJSONFile(
|
|
do_get_file("settings/v1-metadata-migration.json")
|
|
);
|
|
|
|
// Assert the engine ids have not been migrated yet
|
|
for (let engine of legacySettings.engines) {
|
|
Assert.ok(!("id" in engine));
|
|
}
|
|
Assert.ok(!("defaultEngineId" in legacySettings.metaData));
|
|
Assert.ok(!("privateDefaultEngineId" in legacySettings.metaData));
|
|
|
|
await checkLoadSettingProperties(
|
|
"settings/v1-metadata-migration.json",
|
|
false,
|
|
true
|
|
);
|
|
|
|
Assert.ok(
|
|
!Services.prefs.prefHasUserValue(legacyUseSavedOrderPrefName),
|
|
"Should have cleared the legacy pref."
|
|
);
|
|
});
|
|
|
|
add_task(
|
|
async function test_legacy_setting_migration_with_undefined_metaData_current_and_private() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
await loadSettingsFile("settings/v1-metadata-migration.json", false);
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
let migratedSettingsFile = await promiseSettingsData();
|
|
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.defaultEngineId,
|
|
"",
|
|
"When there is no metaData.current attribute in settings file, the migration should set the defaultEngineId to an empty string."
|
|
);
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.privateDefaultEngineId,
|
|
"",
|
|
"When there is no metaData.private attribute in settings file, the migration should set the privateDefaultEngineId to an empty string."
|
|
);
|
|
|
|
removeSettingsFile();
|
|
}
|
|
);
|
|
|
|
add_task(
|
|
async function test_legacy_setting_migration_with_correct_metaData_current_and_private_hashes() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
await loadSettingsFile(
|
|
"settings/v6-correct-default-engine-hashes.json",
|
|
false,
|
|
true
|
|
);
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
let migratedSettingsFile = await promiseSettingsData();
|
|
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.defaultEngineId,
|
|
"engine2",
|
|
"When the metaData.current and associated hash are correct, the migration should set the defaultEngineId to the engine id."
|
|
);
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.privateDefaultEngineId,
|
|
"engine2",
|
|
"When the metaData.private and associated hash are correct, the migration should set the privateDefaultEngineId to the private engine id."
|
|
);
|
|
|
|
removeSettingsFile();
|
|
}
|
|
);
|
|
|
|
add_task(
|
|
async function test_legacy_setting_migration_with_incorrect_metaData_current_and_private_hashes_app_provided() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
// Here we are testing correct migration for the case that a user has set
|
|
// their default engine to an application provided engine (but not the app
|
|
// default).
|
|
//
|
|
// In this case we should ignore invalid hashes for the default engines,
|
|
// and allow the select default to remain. This covers the case where
|
|
// a user has copied a profile from a different directory.
|
|
// See SearchService._getEngineDefault for more details.
|
|
|
|
await loadSettingsFile(
|
|
"settings/v6-wrong-default-engine-hashes.json",
|
|
false,
|
|
false
|
|
);
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
let migratedSettingsFile = await promiseSettingsData();
|
|
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.defaultEngineId,
|
|
"engine2",
|
|
"Should ignore invalid metaData.hash when the default engine is application provided."
|
|
);
|
|
Assert.equal(
|
|
Services.search.defaultEngine.name,
|
|
"engine2",
|
|
"Should have the correct engine set as default"
|
|
);
|
|
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.privateDefaultEngineId,
|
|
"engine2",
|
|
"Should ignore invalid metaData.privateHash when the default private engine is application provided."
|
|
);
|
|
Assert.equal(
|
|
Services.search.defaultPrivateEngine.name,
|
|
"engine2",
|
|
"Should have the correct engine set as default private"
|
|
);
|
|
|
|
removeSettingsFile();
|
|
}
|
|
);
|
|
|
|
add_task(
|
|
async function test_legacy_setting_migration_with_incorrect_metaData_current_and_private_hashes_third_party() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
// This test is checking that if the user has set a third-party engine as
|
|
// default, and the verification hash is invalid, then we do not copy
|
|
// the default engine setting.
|
|
|
|
await loadSettingsFile(
|
|
"settings/v6-wrong-third-party-engine-hashes.json",
|
|
false,
|
|
false
|
|
);
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
let migratedSettingsFile = await promiseSettingsData();
|
|
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.defaultEngineId,
|
|
"",
|
|
"Should reset the default engine when metaData.hash is invalid and the engine is not application provided."
|
|
);
|
|
Assert.equal(
|
|
Services.search.defaultEngine.name,
|
|
"engine1",
|
|
"Should have reset the default engine"
|
|
);
|
|
|
|
Assert.equal(
|
|
migratedSettingsFile.metaData.privateDefaultEngineId,
|
|
"",
|
|
"Should reset the default engine when metaData.privateHash is invalid and the engine is not application provided."
|
|
);
|
|
Assert.equal(
|
|
Services.search.defaultPrivateEngine.name,
|
|
"engine1",
|
|
"Should have reset the default private engine"
|
|
);
|
|
|
|
removeSettingsFile();
|
|
}
|
|
);
|
|
|
|
add_task(async function test_current_setting_engine_properties() {
|
|
await checkLoadSettingProperties(
|
|
"settings/settings-loading.json",
|
|
true,
|
|
false
|
|
);
|
|
});
|
|
|
|
add_task(async function test_settings_metadata_properties() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
await loadSettingsFile("settings/settings-loading.json");
|
|
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
let metaDataProperties = [
|
|
"locale",
|
|
"region",
|
|
"channel",
|
|
"experiment",
|
|
"distroID",
|
|
];
|
|
|
|
for (let name of metaDataProperties) {
|
|
Assert.notEqual(
|
|
ss._settings.getMetaDataAttribute(`${name}`),
|
|
undefined,
|
|
`Search settings should have ${name} property defined.`
|
|
);
|
|
}
|
|
|
|
removeSettingsFile();
|
|
});
|
|
|
|
add_task(async function test_settings_write_when_settings_changed() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
await loadSettingsFile("settings/settings-loading.json");
|
|
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
await settingsFileWritten;
|
|
|
|
Assert.ok(
|
|
ss._settings.isCurrentAndCachedSettingsEqual(),
|
|
"Settings and cached settings should be the same after search service initializaiton."
|
|
);
|
|
|
|
const settingsFileWritten2 = promiseAfterSettings();
|
|
ss._settings.setMetaDataAttribute("value", "test");
|
|
|
|
Assert.ok(
|
|
!ss._settings.isCurrentAndCachedSettingsEqual(),
|
|
"Settings should differ from cached settings after a new attribute is set."
|
|
);
|
|
|
|
await settingsFileWritten2;
|
|
info("Settings write complete");
|
|
|
|
Assert.ok(
|
|
ss._settings.isCurrentAndCachedSettingsEqual(),
|
|
"Settings and cached settings should be the same after new attribte on settings is written."
|
|
);
|
|
|
|
removeSettingsFile();
|
|
});
|
|
|
|
add_task(async function test_set_and_get_engine_metadata_attribute() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
await loadSettingsFile("settings/settings-loading.json");
|
|
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
await settingsFileWritten;
|
|
|
|
let engines = await ss.getEngines();
|
|
const settingsFileWritten2 = promiseAfterSettings();
|
|
ss._settings.setEngineMetaDataAttribute(engines[0].name, "value", "test");
|
|
await settingsFileWritten2;
|
|
|
|
Assert.equal(
|
|
"test",
|
|
ss._settings.getEngineMetaDataAttribute(engines[0].name, "value"),
|
|
`${engines[0].name}'s metadata property "value" should be set as "test" after calling getEngineMetaDataAttribute.`
|
|
);
|
|
|
|
let userSettings = await ss._settings.get();
|
|
let engine = userSettings.engines.find(e => e._name == engines[0].name);
|
|
|
|
Assert.equal(
|
|
"test",
|
|
engine._metaData.value,
|
|
`${engines[0].name}'s metadata property "value" should be set as "test" from settings file.`
|
|
);
|
|
|
|
removeSettingsFile();
|
|
});
|
|
|
|
add_task(
|
|
async function test_settings_write_prevented_when_settings_unchanged() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
await loadSettingsFile("settings/settings-loading.json");
|
|
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
await settingsFileWritten;
|
|
|
|
Assert.ok(
|
|
ss._settings.isCurrentAndCachedSettingsEqual(),
|
|
"Settings and cached settings should be the same after search service initializaiton."
|
|
);
|
|
|
|
// Update settings.
|
|
const settingsFileWritten2 = promiseAfterSettings();
|
|
ss._settings.setMetaDataAttribute("value", "test");
|
|
|
|
Assert.ok(
|
|
!ss._settings.isCurrentAndCachedSettingsEqual(),
|
|
"Settings should differ from cached settings after a new attribute is set."
|
|
);
|
|
await settingsFileWritten2;
|
|
|
|
// Set the same attribute as before to ensure there was no change.
|
|
// Settings write should be prevented.
|
|
let promiseWritePrevented = SearchTestUtils.promiseSearchNotification(
|
|
"write-prevented-when-settings-unchanged"
|
|
);
|
|
ss._settings.setMetaDataAttribute("value", "test");
|
|
|
|
Assert.ok(
|
|
ss._settings.isCurrentAndCachedSettingsEqual(),
|
|
"Settings and cached settings should be the same."
|
|
);
|
|
await promiseWritePrevented;
|
|
|
|
removeSettingsFile();
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Test that the JSON settings written in the profile is correct.
|
|
*/
|
|
add_task(async function test_settings_write() {
|
|
let ss = Services.search.wrappedJSObject;
|
|
info("test settings writing");
|
|
|
|
await loadSettingsFile("settings/settings-loading.json");
|
|
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
await settingsFileWritten;
|
|
|
|
let settingsData = await promiseSettingsData();
|
|
|
|
// Remove buildID and locale, as they are no longer used.
|
|
delete settingsTemplate.buildID;
|
|
delete settingsTemplate.locale;
|
|
|
|
for (let engine of settingsTemplate.engines) {
|
|
// Remove _shortName and description from the settings template, as they are
|
|
// no longer supported, but older settings used to have them. We keep them
|
|
// in the template as an example.
|
|
if ("_shortName" in engine) {
|
|
delete engine._shortName;
|
|
}
|
|
if ("description" in engine) {
|
|
delete engine.description;
|
|
}
|
|
if ("_urls" in engine) {
|
|
// Only app-provided engines support purpose, others do not,
|
|
// so filter them out of the expected template.
|
|
for (let urls of engine._urls) {
|
|
urls.params = urls.params.filter(p => !p.purpose);
|
|
// resultDomain is also no longer supported.
|
|
if ("resultDomain" in urls) {
|
|
delete urls.resultDomain;
|
|
}
|
|
}
|
|
}
|
|
// Remove queryCharset, if it is the same as the default, as we don't save
|
|
// it in that case.
|
|
if (engine?.queryCharset == SearchUtils.DEFAULT_QUERY_CHARSET) {
|
|
delete engine.queryCharset;
|
|
}
|
|
}
|
|
|
|
// Note: the file is copied with an old version number, which should have
|
|
// been updated on write.
|
|
settingsTemplate.version = SearchUtils.SETTINGS_VERSION;
|
|
|
|
isSubObjectOf(settingsTemplate, settingsData, (prop, value) => {
|
|
if (prop != "_iconURL" && prop != "{}") {
|
|
return false;
|
|
}
|
|
// Skip items that are to do with icons for extensions, as we can't
|
|
// control the uuid.
|
|
return value.startsWith("moz-extension://");
|
|
});
|
|
});
|
|
|
|
async function settings_write_check(disableFn) {
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
sinon.stub(ss._settings, "_write").returns(Promise.resolve());
|
|
|
|
// Simulate the search service being initialized.
|
|
disableFn(true);
|
|
|
|
ss._settings.setMetaDataAttribute("value", "test");
|
|
|
|
Assert.ok(
|
|
ss._settings._write.notCalled,
|
|
"Should not have attempted to _write"
|
|
);
|
|
|
|
// Wait for two periods of the normal delay to ensure we still do not write.
|
|
await new Promise(r =>
|
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
|
setTimeout(r, SearchSettings.SETTNGS_INVALIDATION_DELAY * 2)
|
|
);
|
|
|
|
Assert.ok(
|
|
ss._settings._write.notCalled,
|
|
"Should not have attempted to _write"
|
|
);
|
|
|
|
disableFn(false);
|
|
|
|
await TestUtils.waitForCondition(
|
|
() => ss._settings._write.calledOnce,
|
|
"Should attempt to write the settings."
|
|
);
|
|
|
|
sinon.restore();
|
|
}
|
|
|
|
add_task(async function test_settings_write_prevented_during_init() {
|
|
await settings_write_check(disable => {
|
|
let status = disable ? "success" : "failed";
|
|
Services.search.wrappedJSObject.forceInitializationStatusForTests(status);
|
|
});
|
|
});
|
|
|
|
add_task(async function test_settings_write_prevented_during_reload() {
|
|
await settings_write_check(
|
|
disable => (Services.search.wrappedJSObject._reloadingEngines = disable)
|
|
);
|
|
});
|
|
|
|
add_task(async function test_correct_change_reason_when_no_default_engine() {
|
|
Services.fog.initializeFOG();
|
|
|
|
let ss = Services.search.wrappedJSObject;
|
|
|
|
await loadSettingsFile("settings/settings-loading.json", false, false);
|
|
const settingsFileWritten = promiseAfterSettings();
|
|
|
|
await ss.reset();
|
|
await Services.search.init();
|
|
|
|
await settingsFileWritten;
|
|
|
|
sinon.stub(ss, "appDefaultEngine").get(() => null);
|
|
ss.forceCurrentEngineToBeNull();
|
|
ss._getEngineDefault(false);
|
|
|
|
let snapshot = Glean.searchEngineDefault.changed.testGetValue();
|
|
Assert.equal(
|
|
snapshot[0].extra.change_reason,
|
|
"no-existing-default",
|
|
"Should have triggered default changed event with reason 'no-existing-default'"
|
|
);
|
|
|
|
removeSettingsFile();
|
|
sinon.restore();
|
|
});
|
|
|
|
var EXPECTED_ENGINE = {
|
|
engine: {
|
|
name: "Test search engine",
|
|
alias: "",
|
|
wrappedJSObject: {
|
|
_extensionID: "test-addon-id@mozilla.org",
|
|
_iconURL:
|
|
"data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAA" +
|
|
"AIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9F" +
|
|
"sfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2F" +
|
|
"Ptft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2Fgg" +
|
|
"M%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F" +
|
|
"%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJ" +
|
|
"vvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%" +
|
|
"2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%" +
|
|
"2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%" +
|
|
"2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%" +
|
|
"2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYS" +
|
|
"BHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWc" +
|
|
"TxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4j" +
|
|
"wA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsgg" +
|
|
"A7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7" +
|
|
"kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%" +
|
|
"2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFE" +
|
|
"MwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%" +
|
|
"2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCT" +
|
|
"IYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesA" +
|
|
"AN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOc" +
|
|
"AAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v" +
|
|
"8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAA" +
|
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA",
|
|
_urls: [
|
|
{
|
|
type: "application/x-suggestions+json",
|
|
method: "GET",
|
|
template:
|
|
"http://suggestqueries.google.com/complete/search?output=firefox&client=firefox" +
|
|
"&q={searchTerms}",
|
|
params: "",
|
|
},
|
|
{
|
|
type: "text/html",
|
|
method: "GET",
|
|
template: "http://www.google.com/search",
|
|
params: [
|
|
{
|
|
name: "q",
|
|
value: "{searchTerms}",
|
|
purpose: undefined,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|