Merge fx-team to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-07-18 15:21:24 -04:00
commit c300f93ef4
93 changed files with 4208 additions and 48026 deletions

View File

@ -152,6 +152,7 @@ pref("dom.disable_open_during_load", true);
pref("privacy.popups.showBrowserMessage", true);
pref("keyword.enabled", true);
pref("browser.fixup.domainwhitelist.localhost", true);
pref("accessibility.typeaheadfind", false);
pref("accessibility.typeaheadfind.timeout", 5000);

File diff suppressed because one or more lines are too long

View File

@ -276,6 +276,14 @@ var gPluginHandler = {
return;
}
if (eventType == "PluginCrashed" &&
!(event.target instanceof Ci.nsIObjectLoadingContent)) {
// If the event target is not a plugin object (i.e., an <object> or
// <embed> element), this call is for a window-global plugin.
this.pluginInstanceCrashed(event.target, event);
return;
}
let plugin = event.target;
let doc = plugin.ownerDocument;
@ -1143,7 +1151,7 @@ var gPluginHandler = {
// Crashed-plugin event listener. Called for every instance of a
// plugin in content.
pluginInstanceCrashed: function (plugin, aEvent) {
pluginInstanceCrashed: function (target, aEvent) {
// Ensure the plugin and event are of the right type.
if (!(aEvent instanceof Ci.nsIDOMCustomEvent))
return;
@ -1161,18 +1169,22 @@ var gPluginHandler = {
let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
//
// Configure the crashed-plugin placeholder.
//
let plugin = null, doc;
if (target instanceof Ci.nsIObjectLoadingContent) {
plugin = target;
doc = plugin.ownerDocument;
} else {
doc = target.document;
if (!doc) {
return;
}
// doPrompt is specific to the crashed plugin overlay, and
// therefore is not applicable for window-global plugins.
doPrompt = false;
}
// Force a layout flush so the binding is attached.
plugin.clientTop;
let overlay = this.getPluginUI(plugin, "main");
let statusDiv = this.getPluginUI(plugin, "submitStatus");
let doc = plugin.ownerDocument;
#ifdef MOZ_CRASHREPORTER
let status;
#ifdef MOZ_CRASHREPORTER
// Determine which message to show regarding crash reports.
if (submittedReport) { // submitReports && !doPrompt, handled in observer
status = "submitted";
@ -1180,31 +1192,15 @@ var gPluginHandler = {
else if (!submitReports && !doPrompt) {
status = "noSubmit";
}
else { // doPrompt
else if (!pluginDumpID) {
// If we don't have a minidumpID, we can't (or didn't) submit anything.
// This can happen if the plugin is killed from the task manager.
status = "noReport";
}
else {
status = "please";
this.getPluginUI(plugin, "submitButton").addEventListener("click",
function (event) {
if (event.button != 0 || !event.isTrusted)
return;
this.submitReport(pluginDumpID, browserDumpID, plugin);
pref.setBoolPref("", optInCB.checked);
}.bind(this));
let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
optInCB.checked = pref.getBoolPref("");
}
// If we don't have a minidumpID, we can't (or didn't) submit anything.
// This can happen if the plugin is killed from the task manager.
if (!pluginDumpID) {
status = "noReport";
}
statusDiv.setAttribute("status", status);
let helpIcon = this.getPluginUI(plugin, "helpIcon");
this.addLinkClickCallback(helpIcon, "openHelpPage");
// If we're showing the link to manually trigger report submission, we'll
// want to be able to update all the instances of the UI for this crash to
// show an updated message when a report is submitted.
@ -1238,26 +1234,15 @@ var gPluginHandler = {
}
#endif
let crashText = this.getPluginUI(plugin, "crashedText");
crashText.textContent = messageString;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
let link = this.getPluginUI(plugin, "reloadLink");
this.addLinkClickCallback(link, "reloadPage", browser);
let notificationBox = gBrowser.getNotificationBox(browser);
let isShowing = false;
let isShowing = this.shouldShowOverlay(plugin, overlay);
// Is the <object>'s size too small to hold what we want to show?
if (!isShowing) {
// First try hiding the crash report submission UI.
statusDiv.removeAttribute("status");
isShowing = this.shouldShowOverlay(plugin, overlay);
if (plugin) {
// If there's no plugin (an <object> or <embed> element), this call is
// for a window-global plugin. In this case, there's no overlay to show.
isShowing = _setUpPluginOverlay.call(this, plugin, doPrompt, browser);
}
this.setVisibility(plugin, overlay, isShowing);
if (isShowing) {
// If a previous plugin on the page was too small and resulted in adding a
@ -1330,5 +1315,54 @@ var gPluginHandler = {
}, false);
}
// Configure the crashed-plugin placeholder.
// Returns true if the plugin overlay is visible.
function _setUpPluginOverlay(plugin, doPromptSubmit, browser) {
if (!plugin) {
return false;
}
// Force a layout flush so the binding is attached.
plugin.clientTop;
let overlay = this.getPluginUI(plugin, "main");
let statusDiv = this.getPluginUI(plugin, "submitStatus");
if (doPromptSubmit) {
this.getPluginUI(plugin, "submitButton").addEventListener("click",
function (event) {
if (event.button != 0 || !event.isTrusted)
return;
this.submitReport(pluginDumpID, browserDumpID, plugin);
pref.setBoolPref("", optInCB.checked);
}.bind(this));
let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
optInCB.checked = pref.getBoolPref("");
}
statusDiv.setAttribute("status", status);
let helpIcon = this.getPluginUI(plugin, "helpIcon");
this.addLinkClickCallback(helpIcon, "openHelpPage");
let crashText = this.getPluginUI(plugin, "crashedText");
crashText.textContent = messageString;
let link = this.getPluginUI(plugin, "reloadLink");
this.addLinkClickCallback(link, "reloadPage", browser);
let isShowing = this.shouldShowOverlay(plugin, overlay);
// Is the <object>'s size too small to hold what we want to show?
if (!isShowing) {
// First try hiding the crash report submission UI.
statusDiv.removeAttribute("status");
isShowing = this.shouldShowOverlay(plugin, overlay);
}
this.setVisibility(plugin, overlay, isShowing);
return isShowing;
}
}
};

View File

@ -22,6 +22,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
"resource://gre/modules/ShortcutUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
"@mozilla.org/network/dns-service;1",
"nsIDNSService");
const nsIWebNavigation = Ci.nsIWebNavigation;
var gLastBrowserCharset = null;
@ -745,6 +749,101 @@ const gFormSubmitObserver = {
}
};
function gKeywordURIFixup(fixupInfo, topic, data) {
fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
// We get called irrespective of whether we did a keyword search, or
// whether the original input would be vaguely interpretable as a URL,
// so figure that out first.
let alternativeURI = fixupInfo.fixedURI;
if (!fixupInfo.fixupUsedKeyword || !alternativeURI) {
return;
}
// We should have a document loader...
let docshellRef = fixupInfo.consumer;
try {
docshellRef.QueryInterface(Ci.nsIDocumentLoader);
} catch (ex) {
return;
}
// ... from which we can deduce the browser
let browser = gBrowser.getBrowserForDocument(docshellRef.document);
if (!browser)
return;
// At this point we're still only just about to load this URI.
// When the async DNS lookup comes back, we may be in any of these states:
// 1) still on the previous URI, waiting for the preferredURI (keyword
// search) to respond;
// 2) at the keyword search URI (preferredURI)
// 3) at some other page because the user stopped navigation.
// We keep track of the currentURI to detect case (1) in the DNS lookup
// callback.
let previousURI = browser.currentURI;
// now swap for a weak ref so we don't hang on to browser needlessly
// even if the DNS query takes forever
let weakBrowser = Cu.getWeakReference(browser);
browser = null;
// Additionally, we need the host of the parsed url
let hostName = alternativeURI.host;
// and the ascii-only host for the pref:
let asciiHost = alternativeURI.asciiHost;
let onLookupComplete = (request, record, status) => {
let browser = weakBrowser.get();
if (!Components.isSuccessCode(status) || !browser)
return;
let currentURI = browser.currentURI;
// If we're in case (3) (see above), don't show an info bar.
if (!currentURI.equals(previousURI) &&
!currentURI.equals(fixupInfo.preferredURI)) {
return;
}
// show infobar offering to visit the host
let notificationBox = gBrowser.getNotificationBox(browser);
if (notificationBox.getNotificationWithValue("keyword-uri-fixup"))
return;
let message = gNavigatorBundle.getFormattedString(
"keywordURIFixup.message", [hostName]);
let yesMessage = gNavigatorBundle.getFormattedString(
"keywordURIFixup.goTo", [hostName])
let buttons = [
{
label: yesMessage,
accessKey: gNavigatorBundle.getString("keywordURIFixup.goTo.accesskey"),
callback: function() {
let pref = "browser.fixup.domainwhitelist." + asciiHost;
Services.prefs.setBoolPref(pref, true);
openUILinkIn(alternativeURI.spec, "current");
}
},
{
label: gNavigatorBundle.getString("keywordURIFixup.dismiss"),
accessKey: gNavigatorBundle.getString("keywordURIFixup.dismiss.accesskey"),
callback: function() {
let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
notificationBox.removeNotification(notification, true);
}
}
];
let notification =
notificationBox.appendNotification(message,"keyword-uri-fixup", null,
notificationBox.PRIORITY_INFO_HIGH,
buttons);
notification.persistence = 1;
};
gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread);
}
var gBrowserInit = {
delayedStartupFinished: false,
@ -1051,6 +1150,7 @@ var gBrowserInit = {
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup", false);
BrowserOffline.init();
OfflineApps.init();
@ -1354,6 +1454,7 @@ var gBrowserInit = {
Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
try {
gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);

View File

@ -401,6 +401,7 @@ skip-if = e10s # Bug 932651 - getClipboardData in specialpowersAPI.js not e10s f
skip-if = e10s # Bug ?????? - obscure non-windows child process crashes on try
[browser_urlbarRevert.js]
skip-if = e10s # Bug ?????? - ESC reverted the location bar value - Got foobar, expected example.com
[browser_urlbarSearchSingleWordNotification.js]
[browser_urlbarStop.js]
skip-if = e10s # Bug ????? - test calls gBrowser.contentWindow.stop
[browser_urlbarTrimURLs.js]

View File

@ -107,8 +107,8 @@ function test() {
});
Services.prefs.setBoolPref(PREF_PRELOAD, false);
// set directory source to empty links
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
// set directory source to dummy/empty links
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, 'data:application/json,{"test":1}');
}
let observer = {

View File

@ -0,0 +1,80 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
registerCleanupFunction(function() {
Services.prefs.clearUserPref("browser.fixup.domainwhitelist.localhost");
});
function promiseNotificationForTab(value, expected, tab=gBrowser.selectedTab) {
let deferred = Promise.defer();
let notificationBox = gBrowser.getNotificationBox(tab.linkedBrowser);
if (expected) {
waitForCondition(() => notificationBox.getNotificationWithValue(value) !== null,
deferred.resolve, "Were expecting to get a notification");
} else {
setTimeout(() => {
is(notificationBox.getNotificationWithValue(value), null, "We are expecting to not get a notification");
deferred.resolve();
}, 1000);
}
return deferred.promise;
}
function* runURLBarSearchTest(valueToOpen, expectSearch, expectNotification) {
gURLBar.value = valueToOpen;
let expectedURI;
if (!expectSearch) {
expectedURI = "http://" + valueToOpen + "/";
} else {
yield new Promise(resolve => {
Services.search.init(resolve)
});
expectedURI = Services.search.defaultEngine.getSubmission(valueToOpen, null, "keyword").uri.spec;
}
gURLBar.focus();
let docLoadPromise = waitForDocLoadAndStopIt(expectedURI);
EventUtils.synthesizeKey("VK_RETURN", {});
yield docLoadPromise;
yield promiseNotificationForTab("keyword-uri-fixup", expectNotification);
}
add_task(function* test_navigate_full_domain() {
let tab = gBrowser.selectedTab = gBrowser.addTab();
yield* runURLBarSearchTest("www.mozilla.org", false, false);
gBrowser.removeTab(tab);
});
add_task(function* test_navigate_single_host() {
Services.prefs.setBoolPref("browser.fixup.domainwhitelist.localhost", false);
let tab = gBrowser.selectedTab = gBrowser.addTab();
yield* runURLBarSearchTest("localhost", true, true);
let notificationBox = gBrowser.getNotificationBox(tab.linkedBrowser);
let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup");
let docLoadPromise = waitForDocLoadAndStopIt("http://localhost/");
notification.querySelector(".notification-button-default").click();
// check pref value
let pref = "browser.fixup.domainwhitelist.localhost";
let prefValue = Services.prefs.getBoolPref(pref);
ok(prefValue, "Pref should have been toggled");
yield docLoadPromise;
gBrowser.removeTab(tab);
// Now try again with the pref set.
let tab = gBrowser.selectedTab = gBrowser.addTab();
yield* runURLBarSearchTest("localhost", false, false);
gBrowser.removeTab(tab);
});
add_task(function* test_navigate_invalid_url() {
let tab = gBrowser.selectedTab = gBrowser.addTab();
yield* runURLBarSearchTest("mozilla is awesome", true, false);
gBrowser.removeTab(tab);
});

View File

@ -24,8 +24,8 @@ let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
let gWindow = window;
// Default to empty directory links
let gDirectorySource = "data:application/json,{}";
// Default to dummy/empty directory links
let gDirectorySource = 'data:application/json,{"test":1}';
// The tests assume all three rows of sites are shown, but the window may be too
// short to actually show three rows. Resize it if necessary.

View File

@ -62,6 +62,7 @@ run-if = crashreporter
[browser_CTP_notificationBar.js]
[browser_CTP_outsideScrollArea.js]
[browser_CTP_resize.js]
[browser_globalplugin_crashinfobar.js]
[browser_pageInfo_plugins.js]
[browser_pluginnotification.js]
[browser_pluginplaypreview.js]

View File

@ -0,0 +1,69 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let gTestBrowser = null;
let propBagProperties = {
pluginName: "GlobalTestPlugin",
pluginDumpID: "1234",
browserDumpID: "5678",
submittedCrashReport: false
}
// Test that plugin crash submissions still work properly after
// click-to-play activation.
function test() {
waitForExplicitFinish();
let tab = gBrowser.loadOneTab("about:blank", { inBackground: false });
gTestBrowser = gBrowser.getBrowserForTab(tab);
gTestBrowser.addEventListener("PluginCrashed", onCrash, false);
gTestBrowser.addEventListener("load", onPageLoad, true);
registerCleanupFunction(function cleanUp() {
gTestBrowser.removeEventListener("PluginCrashed", onCrash, false);
gTestBrowser.removeEventListener("load", onPageLoad, true);
gBrowser.removeTab(tab);
});
}
function onPageLoad() {
executeSoon(generateCrashEvent);
}
function generateCrashEvent() {
let window = gTestBrowser.contentWindow;
let propBag = Cc["@mozilla.org/hash-property-bag;1"]
.createInstance(Ci.nsIWritablePropertyBag);
for (let [name, val] of Iterator(propBagProperties)) {
propBag.setProperty(name, val);
}
let event = window.document.createEvent("CustomEvent");
event.initCustomEvent("PluginCrashed", true, true, propBag);
window.dispatchEvent(event);
}
function onCrash(event) {
let target = event.target;
is (target, gTestBrowser.contentWindow, "Event target is the window.");
let propBag = event.detail.QueryInterface(Ci.nsIPropertyBag2);
for (let [name, val] of Iterator(propBagProperties)) {
let type = typeof val;
let propVal = type == "string"
? propBag.getPropertyAsAString(name)
: propBag.getPropertyAsBool(name);
is (propVal, val, "Correct property in detail propBag: " + name + ".");
}
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
let notification = notificationBox.getNotificationWithValue("plugin-crashed");
ok(notification, "Infobar was shown.");
is(notification.priority, notificationBox.PRIORITY_WARNING_MEDIUM, "Correct priority.");
is(notification.getAttribute("label"), "The GlobalTestPlugin plugin has crashed.", "Correct message.");
finish();
}

View File

@ -28,7 +28,6 @@ support-files =
[browser_aboutHome_activation.js]
[browser_addons.js]
[browser_blocklist.js]
[browser_defaults.js]
[browser_share.js]
[browser_social_activation.js]
[browser_social_chatwindow.js]

View File

@ -8,7 +8,7 @@ const ID_SUFFIX = "@services.mozilla.org";
const STRING_TYPE_NAME = "type.%ID%.name";
const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
let manifest = { // builtin provider
let manifest = {
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
@ -28,19 +28,15 @@ function test() {
waitForExplicitFinish();
let prefname = getManifestPrefname(manifest);
setBuiltinManifestPref(prefname, manifest);
// ensure that manifest2 is NOT showing as builtin
is(SocialService.getOriginActivationType(manifest.origin), "builtin", "manifest is builtin");
is(SocialService.getOriginActivationType(manifest2.origin), "foreign", "manifest2 is not builtin");
is(SocialService.getOriginActivationType(manifest.origin), "foreign", "manifest is foreign");
is(SocialService.getOriginActivationType(manifest2.origin), "foreign", "manifest2 is foreign");
Services.prefs.setBoolPref("social.remote-install.enabled", true);
runSocialTests(tests, undefined, undefined, function () {
Services.prefs.clearUserPref("social.remote-install.enabled");
// clear our builtin pref
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
resetBuiltinManifestPref(prefname);
// just in case the tests failed, clear these here as well
Services.prefs.clearUserPref("social.whitelist");
Services.prefs.clearUserPref("social.directories");
finish();
});
@ -169,9 +165,9 @@ var tests = {
expectEvent = "onEnabling";
setManifestPref(prefname, manifest);
SocialService.addBuiltinProvider(manifest.origin, function(provider) {
SocialService.enableProvider(manifest.origin, function(provider) {
expectEvent = "onDisabling";
SocialService.removeProvider(provider.origin, function() {
SocialService.disableProvider(provider.origin, function() {
AddonManager.removeAddonListener(listener);
Services.prefs.clearUserPref(prefname);
next();
@ -199,83 +195,7 @@ var tests = {
is(SocialService.getOriginActivationType(installFrom), "foreign", "testing foriegn install");
Social.installProvider(doc, manifest2, function(addonManifest) {
Services.prefs.clearUserPref("social.whitelist");
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
Social.uninstallProvider(addonManifest.origin);
gBrowser.removeTab(tab);
});
});
});
},
testBuiltinInstallWithoutManifest: function(next) {
// send installProvider null for the manifest
AddonManager.addAddonListener(installListener(next, manifest));
let panel = document.getElementById("servicesInstall-notification");
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
info("servicesInstall-notification panel opened");
panel.button.click();
});
let prefname = getManifestPrefname(manifest);
let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
let installFrom = doc.nodePrincipal.origin;
is(SocialService.getOriginActivationType(installFrom), "builtin", "testing builtin install");
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
Social.installProvider(doc, null, function(addonManifest) {
ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
Social.uninstallProvider(addonManifest.origin);
gBrowser.removeTab(tab);
});
});
});
},
testBuiltinInstall: function(next) {
// send installProvider a json object for the manifest
AddonManager.addAddonListener(installListener(next, manifest));
let panel = document.getElementById("servicesInstall-notification");
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
info("servicesInstall-notification panel opened");
panel.button.click();
});
let prefname = getManifestPrefname(manifest);
let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
let installFrom = doc.nodePrincipal.origin;
is(SocialService.getOriginActivationType(installFrom), "builtin", "testing builtin install");
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
Social.installProvider(doc, manifest, function(addonManifest) {
ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
Social.uninstallProvider(addonManifest.origin);
gBrowser.removeTab(tab);
});
});
});
},
testWhitelistInstall: function(next) {
AddonManager.addAddonListener(installListener(next, manifest2));
let panel = document.getElementById("servicesInstall-notification");
PopupNotifications.panel.addEventListener("popupshown", function onpopupshown() {
PopupNotifications.panel.removeEventListener("popupshown", onpopupshown);
info("servicesInstall-notification panel opened");
panel.button.click();
});
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
let installFrom = doc.nodePrincipal.origin;
Services.prefs.setCharPref("social.whitelist", installFrom);
is(SocialService.getOriginActivationType(installFrom), "whitelist", "testing whitelist install");
Social.installProvider(doc, manifest2, function(addonManifest) {
Services.prefs.clearUserPref("social.whitelist");
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
SocialService.enableProvider(addonManifest.origin, function(provider) {
Social.uninstallProvider(addonManifest.origin);
gBrowser.removeTab(tab);
});
@ -299,7 +219,7 @@ var tests = {
is(SocialService.getOriginActivationType(installFrom), "directory", "testing directory install");
Social.installProvider(doc, manifest2, function(addonManifest) {
Services.prefs.clearUserPref("social.directories");
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
SocialService.enableProvider(addonManifest.origin, function(provider) {
Social.uninstallProvider(addonManifest.origin);
gBrowser.removeTab(tab);
});
@ -322,7 +242,7 @@ var tests = {
let installFrom = doc.nodePrincipal.origin;
Services.prefs.setCharPref("social.whitelist", installFrom);
Social.installProvider(doc, manifest2, function(addonManifest) {
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
SocialService.enableProvider(addonManifest.origin, function(provider) {
is(provider.manifest.version, 1, "manifest version is 1");
// watch for the provider-update and test the new version

View File

@ -60,12 +60,12 @@ var tests = {
try {
SocialService.addProvider(manifest, function(provider) {
try {
SocialService.removeProvider(provider.origin, function() {
SocialService.disableProvider(provider.origin, function() {
ok(true, "added and removed provider");
finishTest(true);
});
} catch(e) {
ok(false, "SocialService.removeProvider threw exception: " + e);
ok(false, "SocialService.disableProvider threw exception: " + e);
finishTest(false);
}
});
@ -85,7 +85,7 @@ var tests = {
setAndUpdateBlocklist(blocklistURL, function() {
try {
SocialService.addProvider(manifest_bad, function(provider) {
SocialService.removeProvider(provider.origin, function() {
SocialService.disableProvider(provider.origin, function() {
ok(false, "SocialService.addProvider should throw blocklist exception");
finishTest(false);
});

View File

@ -1,14 +0,0 @@
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
// this test ensures that any builtin providers have the builtin flag that we
// need to help with "install" of a builtin.
function test() {
let manifestPrefs = Services.prefs.getDefaultBranch("social.manifest.");
let prefs = manifestPrefs.getChildList("", []);
ok(prefs.length > 0, "we have builtin providers");
for (let pref of prefs) {
let manifest = JSON.parse(manifestPrefs.getComplexValue(pref, Ci.nsISupportsString).data);
ok(manifest.builtin, "manifest is builtin " + manifest.origin);
}
}

View File

@ -115,7 +115,7 @@ var tests = {
is(shareButton.getAttribute("disabled"), "true", "share button attribute is disabled");
// button should be visible
is(shareButton.hidden, false, "share button is visible");
SocialService.removeProvider(manifest.origin, next);
SocialService.disableProvider(manifest.origin, next);
});
},
testShareEnabledOnActivation: function(next) {
@ -163,7 +163,7 @@ var tests = {
if (testData) {
executeSoon(runOneTest);
} else {
SocialService.removeProvider(manifest.origin, next);
SocialService.disableProvider(manifest.origin, next);
}
break;
}
@ -227,7 +227,7 @@ var tests = {
case "got-share-data-message":
is(JSON.stringify(e.data.result), expecting, "microdata data ok");
gBrowser.removeTab(testTab);
SocialService.removeProvider(manifest.origin, next);
SocialService.disableProvider(manifest.origin, next);
break;
}
}

View File

@ -6,6 +6,29 @@ let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).So
let tabsToRemove = [];
function removeAllProviders(callback) {
// all the providers may have been added.
function removeProviders() {
if (Social.providers.length < 1) {
executeSoon(function() {
is(Social.providers.length, 0, "all providers removed");
executeSoon(callback);
});
return;
}
// a full install sets the manifest into a pref, addProvider alone doesn't,
// make sure we uninstall if the manifest was added.
if (Social.providers[0].manifest) {
SocialService.uninstallProvider(Social.providers[0].origin, removeProviders);
} else {
SocialService.disableProvider(Social.providers[0].origin, removeProviders);
}
}
removeProviders();
}
function postTestCleanup(callback) {
// any tabs opened by the test.
for (let tab of tabsToRemove)
@ -15,37 +38,8 @@ function postTestCleanup(callback) {
// to fully open - the end result is that the panel might stay open
//SocialUI.activationPanel.hidePopup();
Services.prefs.clearUserPref("social.whitelist");
// all providers may have had their manifests added.
for (let manifest of gProviders)
Services.prefs.clearUserPref("social.manifest." + manifest.origin);
// all the providers may have been added.
let providers = gProviders.slice(0)
function removeProviders() {
if (providers.length < 1) {
executeSoon(function() {
is(Social.providers.length, 0, "all providers removed");
callback();
});
return;
}
let provider = providers.pop();
try {
SocialService.removeProvider(provider.origin, removeProviders);
} catch(ex) {
removeProviders();
}
}
removeProviders();
}
function addBuiltinManifest(manifest) {
let prefname = getManifestPrefname(manifest);
setBuiltinManifestPref(prefname, manifest);
return prefname;
removeAllProviders(callback);
}
function addTab(url, callback) {
@ -210,74 +204,52 @@ var tests = {
ok(panel.hidden, "activation panel still hidden");
checkSocialUI();
Services.prefs.clearUserPref("social.remote-install.enabled");
next();
removeAllProviders(next);
});
},
testIFrameActivation: function(next) {
Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
activateIFrameProvider(gTestDomains[0], function() {
is(SocialUI.enabled, false, "SocialUI is not enabled");
ok(!SocialSidebar.provider, "provider is not installed");
let panel = document.getElementById("servicesInstall-notification");
ok(panel.hidden, "activation panel still hidden");
checkSocialUI();
Services.prefs.clearUserPref("social.whitelist");
next();
removeAllProviders(next);
});
},
testActivationFirstProvider: function(next) {
Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
// first up we add a manifest entry for a single provider.
activateOneProvider(gProviders[0], false, function() {
// we deactivated leaving no providers left, so Social is disabled.
ok(!SocialSidebar.provider, "should be no provider left after disabling");
checkSocialUI();
Services.prefs.clearUserPref("social.whitelist");
next();
removeAllProviders(next);
});
},
testActivationBuiltin: function(next) {
let prefname = addBuiltinManifest(gProviders[0]);
is(SocialService.getOriginActivationType(gTestDomains[0]), "builtin", "manifest is builtin");
// first up we add a manifest entry for a single provider.
activateOneProvider(gProviders[0], false, function() {
// we deactivated leaving no providers left, so Social is disabled.
ok(!SocialSidebar.provider, "should be no provider left after disabling");
checkSocialUI();
resetBuiltinManifestPref(prefname);
next();
});
},
testActivationMultipleProvider: function(next) {
// The trick with this test is to make sure that Social.providers[1] is
// the current provider when doing the undo - this makes sure that the
// Social code doesn't fallback to Social.providers[0], which it will
// do in some cases (but those cases do not include what this test does)
// first enable the 2 providers
Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
SocialService.addProvider(gProviders[0], function() {
SocialService.addProvider(gProviders[1], function() {
checkSocialUI();
// activate the last provider.
let prefname = addBuiltinManifest(gProviders[2]);
activateOneProvider(gProviders[2], false, function() {
// we deactivated - the first provider should be enabled.
is(SocialSidebar.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
checkSocialUI();
Services.prefs.clearUserPref("social.whitelist");
resetBuiltinManifestPref(prefname);
next();
removeAllProviders(next);
});
});
});
},
testAddonManagerDoubleInstall: function(next) {
Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
// Create a new tab and load about:addons
let blanktab = gBrowser.addTab();
gBrowser.selectedTab = blanktab;
@ -290,12 +262,11 @@ var tests = {
let browser = blanktab.linkedBrowser;
is(browser.currentURI.spec, "about:addons", "about:addons should load into blank tab.");
let prefname = addBuiltinManifest(gProviders[0]);
activateOneProvider(gProviders[0], true, function() {
info("first activation completed");
is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html");
is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html", "postActivationURL loaded");
gBrowser.removeTab(gBrowser.selectedTab);
is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_activate.html");
is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_activate.html", "activation page selected");
gBrowser.removeTab(gBrowser.selectedTab);
tabsToRemove.pop();
// uninstall the provider
@ -303,7 +274,7 @@ var tests = {
checkSocialUI();
activateOneProvider(gProviders[0], true, function() {
info("second activation completed");
is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html");
is(gBrowser.contentDocument.location.href, gProviders[0].origin + "/browser/browser/base/content/test/social/social_postActivation.html", "postActivationURL loaded");
gBrowser.removeTab(gBrowser.selectedTab);
// after closing the addons tab, verify provider is still installed
@ -311,9 +282,7 @@ var tests = {
gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
AddonManager.getAddonsByTypes(["service"], function(aAddons) {
is(aAddons.length, 1, "there can be only one");
Services.prefs.clearUserPref("social.whitelist");
resetBuiltinManifestPref(prefname);
next();
removeAllProviders(next);
});
});

View File

@ -89,7 +89,7 @@ var tests = {
is(button.hidden, false, "mark button is visible");
checkSocialUI(window);
SocialService.removeProvider(manifest2.origin, next);
SocialService.disableProvider(manifest2.origin, next);
});
},
testNoButtonOnEnable: function(next) {
@ -107,7 +107,7 @@ var tests = {
let doc = tab.linkedBrowser.contentDocument;
Social.installProvider(doc, manifest3, function(addonManifest) {
// enable the provider so we know the button would have appeared
SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
SocialService.enableProvider(manifest3.origin, function(provider) {
is(provider.origin, manifest3.origin, "provider is installed");
let id = SocialMarks._toolbarHelper.idFromOrigin(provider.origin);
let widget = CustomizableUI.getWidget(id);
@ -134,7 +134,7 @@ var tests = {
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
Social.installProvider(doc, manifest2, function(addonManifest) {
SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
SocialService.enableProvider(manifest2.origin, function(provider) {
is(provider.origin, manifest2.origin, "provider is installed");
let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
let widget = CustomizableUI.getWidget(id).forWindow(window)
@ -334,7 +334,7 @@ var tests = {
// enable the provider now
let provider = Social._getProviderFromOrigin(manifest2.origin);
ok(provider, "provider is installed");
SocialService.removeProvider(manifest2.origin, function() {
SocialService.disableProvider(manifest2.origin, function() {
let id = SocialMarks._toolbarHelper.idFromOrigin(manifest2.origin);
waitForCondition(function() {
// getWidget now returns null since we've destroyed the widget
@ -381,7 +381,7 @@ var tests = {
let doc = tab.linkedBrowser.contentDocument;
Social.installProvider(doc, manifest, function(addonManifest) {
// enable the provider so we know the button would have appeared
SocialService.addBuiltinProvider(manifest.origin, function(provider) {
SocialService.enableProvider(manifest.origin, function(provider) {
waitForCondition(function() { return CustomizableUI.getWidget(id) },
function() {
gBrowser.removeTab(tab);

View File

@ -70,7 +70,7 @@ function doTest() {
checkShown(true);
// disable social.
SocialService.removeProvider(SocialSidebar.provider.origin, function() {
SocialService.disableProvider(SocialSidebar.provider.origin, function() {
checkShown(false);
is(Social.providers.length, 0, "no providers left");
defaultFinishChecks();

View File

@ -68,7 +68,7 @@ var tests = {
let doc = tab.linkedBrowser.contentDocument;
Social.installProvider(doc, manifest3, function(addonManifest) {
// enable the provider so we know the button would have appeared
SocialService.addBuiltinProvider(manifest3.origin, function(provider) {
SocialService.enableProvider(manifest3.origin, function(provider) {
is(provider.origin, manifest3.origin, "provider is installed");
let id = SocialStatus._toolbarHelper.idFromOrigin(provider.origin);
let widget = CustomizableUI.getWidget(id);
@ -94,7 +94,7 @@ var tests = {
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
Social.installProvider(doc, manifest2, function(addonManifest) {
SocialService.addBuiltinProvider(manifest2.origin, function(provider) {
SocialService.enableProvider(manifest2.origin, function(provider) {
is(provider.origin, manifest2.origin, "provider is installed");
let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
let widget = CustomizableUI.getWidget(id).forWindow(window);
@ -156,7 +156,7 @@ var tests = {
// enable the provider now
let provider = Social._getProviderFromOrigin(manifest2.origin);
ok(provider, "provider is installed");
SocialService.removeProvider(manifest2.origin, function() {
SocialService.disableProvider(manifest2.origin, function() {
let id = SocialStatus._toolbarHelper.idFromOrigin(manifest2.origin);
waitForCondition(function() { return !document.getElementById(id) },
function() {

View File

@ -110,8 +110,8 @@ let tests = {
checkSocialUI(w1);
// disable social and re-check
SocialService.removeProvider(manifest.origin, function() {
SocialService.removeProvider(manifest2.origin, function() {
SocialService.disableProvider(manifest.origin, function() {
SocialService.disableProvider(manifest2.origin, function() {
ok(!Social.enabled, "social is disabled");
is(Social.providers.length, 0, "no providers");
ok(!w1.SocialSidebar.opened, "w1 sidebar is closed");
@ -161,7 +161,7 @@ let tests = {
ok(!Services.prefs.prefHasUserValue("social.sidebar.provider"), "4. global state unset");
ok(!SocialSidebar.opened, "4. main sidebar is still closed");
ok(!w1.SocialSidebar.opened, "4. window sidebar is closed");
SocialService.removeProvider(manifest.origin, function() {
SocialService.disableProvider(manifest.origin, function() {
Services.prefs.clearUserPref("social.manifest.test");
cbnext();
});
@ -180,8 +180,8 @@ let tests = {
testPerWindowSidebar: function(cbnext) {
function finishCheck() {
// disable social and re-check
SocialService.removeProvider(manifest.origin, function() {
SocialService.removeProvider(manifest2.origin, function() {
SocialService.disableProvider(manifest.origin, function() {
SocialService.disableProvider(manifest2.origin, function() {
ok(!Social.enabled, "social is disabled");
is(Social.providers.length, 0, "no providers");
Services.prefs.clearUserPref("social.manifest.test");

View File

@ -106,14 +106,14 @@ function runSocialTestWithProvider(manifest, callback, finishcallback) {
// If we're "cleaning up", don't call finish when done.
let callback = cleanup ? function () {} : finishIfDone;
// Similarly, if we're cleaning up, catch exceptions from removeProvider
let removeProvider = SocialService.removeProvider.bind(SocialService);
let removeProvider = SocialService.disableProvider.bind(SocialService);
if (cleanup) {
removeProvider = function (origin, cb) {
try {
SocialService.removeProvider(origin, cb);
SocialService.disableProvider(origin, cb);
} catch (ex) {
// Ignore "provider doesn't exist" errors.
if (ex.message.indexOf("SocialService.removeProvider: no provider with origin") == 0)
if (ex.message.indexOf("SocialService.disableProvider: no provider with origin") == 0)
return;
info("Failed to clean up provider " + origin + ": " + ex);
}

View File

@ -72,6 +72,7 @@
try {
if (this._prefs.getBoolPref("unifiedcomplete")) {
this.setAttribute("autocompletesearch", "unifiedcomplete");
this.mSearchNames = null;
}
} catch (ex) {}
@ -612,6 +613,7 @@
this.setAttribute("autocompletesearch",
useUnifiedComplete ? "unifiedcomplete"
: "urlinline history");
this.mSearchNames = null;
}
}
]]></body>

View File

@ -207,9 +207,10 @@ let UI = {
setupBusyTimeout: function() {
this.cancelBusyTimeout();
this._busyTimeout = setTimeout(() => {
let busyPromise = this._busyPromise;
this.unbusy();
UI.reportError("error_operationTimeout", this._busyOperationDescription);
this._busyPromise.reject("promise timeout: " + this._busyOperationDescription);
busyPromise.reject("promise timeout: " + this._busyOperationDescription);
}, 30000);
},
@ -810,8 +811,12 @@ let Cmds = {
runtimeAppsNode.firstChild.remove();
}
for (let i = 0; i < AppManager.webAppsStore.object.all.length; i++) {
let app = AppManager.webAppsStore.object.all[i];
let sortedApps = AppManager.webAppsStore.object.all;
sortedApps = sortedApps.sort((a, b) => {
return a.name > b.name;
});
for (let i = 0; i < sortedApps.length; i++) {
let app = sortedApps[i];
let panelItemNode = document.createElement("toolbarbutton");
panelItemNode.className = "panel-item";
panelItemNode.setAttribute("label", app.name);

View File

@ -4,7 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
pref("devtools.webide.showProjectEditor", true);
pref("devtools.webide.templatesURL", "http://code.cdn.mozilla.net/templates/list.json");
pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
pref("devtools.webide.autoinstallADBHelper", true);
pref("devtools.webide.lastprojectlocation", "");
pref("devtools.webide.restoreLastProject", true);

View File

@ -1586,7 +1586,7 @@ Experiments.ExperimentEntry.prototype = {
{ name: "endTime",
condition: () => now < data.endTime },
{ name: "maxStartTime",
condition: () => !data.maxStartTime || now <= data.maxStartTime },
condition: () => this._startDate || !data.maxStartTime || now <= data.maxStartTime },
{ name: "maxActiveSeconds",
condition: () => !this._startDate || now <= (startSec + maxActive) },
{ name: "appName",

View File

@ -1539,6 +1539,66 @@ add_task(function* testEnabledAfterRestart() {
yield testCleanup(experiments);
});
// If experiment add-ons were ever started, maxStartTime shouldn't be evaluated
// anymore. Ensure that if maxStartTime is passed but experiment has started
// already, maxStartTime does not cause deactivation.
add_task(function* testMaxStartTimeEvaluation() {
// Dates the following tests are based on.
let startDate = new Date(2014, 5, 1, 12);
let now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
let maxStartDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
let endDate = futureDate(startDate, 1000 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
// The manifest data we test with.
// We set a value for maxStartTime.
gManifestObject = {
"version": 1,
experiments: [
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
startTime: dateToSeconds(startDate),
endTime: dateToSeconds(endDate),
maxActiveSeconds: 1000 * SEC_IN_ONE_DAY,
maxStartTime: dateToSeconds(maxStartDate),
appName: ["XPCShell"],
channel: ["nightly"],
},
],
};
let experiments = new Experiments.Experiments(gPolicy);
let addons = yield getExperimentAddons();
Assert.equal(addons.length, 0, "Precondition: No experiment add-ons installed.");
yield experiments.updateManifest();
let fromManifest = yield experiments.getExperiments();
Assert.equal(fromManifest.length, 1, "A single experiment is known.");
addons = yield getExperimentAddons();
Assert.equal(addons.length, 1, "A single experiment add-on is installed.");
Assert.ok(addons[0].isActive, "That experiment is active.");
dump("Setting current time to maxStartTime + 100 days and reloading manifest\n");
now = futureDate(maxStartDate, 100 * MS_IN_ONE_DAY);
defineNow(gPolicy, now);
yield experiments.updateManifest();
addons = yield getExperimentAddons();
Assert.equal(addons.length, 1, "The experiment is still there.");
Assert.ok(addons[0].isActive, "It is still active.");
yield testCleanup(experiments);
});
// Test coverage for an add-on uninstall disabling the experiment and that it stays
// disabled over restarts.
add_task(function* test_foreignUninstallAndRestart() {

View File

@ -112,6 +112,17 @@ crashedpluginsMessage.submitButton.label=Submit a crash report
crashedpluginsMessage.submitButton.accesskey=S
crashedpluginsMessage.learnMore=Learn More…
# Keyword fixup messages
# LOCALIZATION NOTE (keywordURIFixup.message): Used when the user tries to visit
# a local host page, by the time the DNS request recognizes it, we have already
# loaded a search page for the given word. An infobar then asks to the user
# whether he rather wanted to visit the host. %S is the recognized host.
keywordURIFixup.message=Did you mean to go to %S?
keywordURIFixup.goTo=Yes, take me to %S
keywordURIFixup.goTo.accesskey=Y
keywordURIFixup.dismiss=No thanks
keywordURIFixup.dismiss.accesskey=N
## Plugin doorhanger strings
# LOCALIZATION NOTE (pluginActivateNew.message): Used for newly-installed
# plugins which are not known to be unsafe. %1$S is the plugin name and %2$S

View File

@ -252,6 +252,7 @@ pref("privacy.donottrackheader.value", -1);
pref("dom.disable_window_open_dialog_feature", true);
pref("keyword.enabled", true);
pref("browser.fixup.domainwhitelist.localhost", true);
pref("accessibility.typeaheadfind", false);
pref("accessibility.typeaheadfind.timeout", 5000);

View File

@ -178,9 +178,9 @@ this.Social = {
// Activation functionality
activateFromOrigin: function (origin, callback) {
// For now only "builtin" providers can be activated. It's OK if the
// provider has already been activated - we still get called back with it.
SocialService.addBuiltinProvider(origin, callback);
// It's OK if the provider has already been activated - we still get called
// back with it.
SocialService.enableProvider(origin, callback);
},
// Page Marking functionality

View File

@ -20,13 +20,13 @@ function testStartupEnabled() {
function testDisableAfterStartup() {
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
SocialService.removeProvider(Social.providers[0].origin, function() {
SocialService.disableProvider(Social.providers[0].origin, function() {
do_wait_observer("social:providers-changed", function() {
do_check_eq(Social.enabled, false, "Social is disabled");
do_check_eq(Social.providers.length, 0, "no social providers available");
do_test_finished();
run_next_test();
});
SocialService.removeProvider(Social.providers[0].origin)
SocialService.disableProvider(Social.providers[0].origin)
});
}

View File

@ -123,12 +123,25 @@ nsDefaultURIFixup::CreateExposableURI(nsIURI *aURI, nsIURI **aReturn)
NS_IMETHODIMP
nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupFlags,
nsIInputStream **aPostData, nsIURI **aURI)
{
nsCOMPtr<nsIURIFixupInfo> fixupInfo;
nsresult rv = GetFixupURIInfo(aStringURI, aFixupFlags, aPostData,
getter_AddRefs(fixupInfo));
NS_ENSURE_SUCCESS(rv, rv);
fixupInfo->GetPreferredURI(aURI);
return rv;
}
NS_IMETHODIMP
nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI, uint32_t aFixupFlags,
nsIInputStream **aPostData, nsIURIFixupInfo **aInfo)
{
NS_ENSURE_ARG(!aStringURI.IsEmpty());
NS_ENSURE_ARG_POINTER(aURI);
nsresult rv;
*aURI = nullptr;
nsRefPtr<nsDefaultURIFixupInfo> info = new nsDefaultURIFixupInfo(aStringURI);
NS_ADDREF(*aInfo = info);
nsAutoCString uriString(aStringURI);
uriString.Trim(" "); // Cleanup the empty spaces that might be on each end.
@ -149,26 +162,35 @@ nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupF
if (scheme.LowerCaseEqualsLiteral("view-source"))
{
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIURIFixupInfo> uriInfo;
uint32_t newFixupFlags = aFixupFlags & ~FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
rv = CreateFixupURI(Substring(uriString,
rv = GetFixupURIInfo(Substring(uriString,
sizeof("view-source:") - 1,
uriString.Length() -
(sizeof("view-source:") - 1)),
newFixupFlags, aPostData, getter_AddRefs(uri));
newFixupFlags, aPostData, getter_AddRefs(uriInfo));
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
nsAutoCString spec;
nsCOMPtr<nsIURI> uri;
uriInfo->GetPreferredURI(getter_AddRefs(uri));
if (!uri)
return NS_ERROR_FAILURE;
uri->GetSpec(spec);
uriString.AssignLiteral("view-source:");
uriString.Append(spec);
}
else {
// Check for if it is a file URL
FileURIFixup(uriString, aURI);
if(*aURI)
nsCOMPtr<nsIURI> uri;
FileURIFixup(uriString, getter_AddRefs(uri));
if (uri)
{
uri.swap(info->mFixedURI);
info->mPreferredURI = info->mFixedURI;
return NS_OK;
}
#if defined(XP_WIN)
// Not a file URL, so translate '\' to '/' for convenience in the common protocols
@ -223,6 +245,8 @@ nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupF
sInitializedPrefCaches = true;
}
info->mInputHasProtocol = !scheme.IsEmpty();
// Fix up common scheme typos.
if (sFixTypos && (aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
@ -266,17 +290,25 @@ nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupF
ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default");
nsCOMPtr<nsIURI> uri;
if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
// Just try to create an URL out of it
rv = NS_NewURI(aURI, uriString, nullptr);
rv = NS_NewURI(getter_AddRefs(uri), uriString, nullptr);
if (NS_SUCCEEDED(rv)) {
info->mFixedURI = uri;
// Figure out whether this had a domain or just a single hostname:
nsAutoCString host;
uri->GetHost(host);
info->mInputHostHasDot = host.FindChar('.') != kNotFound;
}
if (!*aURI && rv != NS_ERROR_MALFORMED_URI) {
if (!uri && rv != NS_ERROR_MALFORMED_URI) {
return rv;
}
}
if (*aURI && ourHandler == extHandler && sFixupKeywords &&
if (uri && ourHandler == extHandler && sFixupKeywords &&
(aFixupFlags & FIXUP_FLAG_FIX_SCHEME_TYPOS)) {
nsCOMPtr<nsIExternalProtocolService> extProtService =
do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
@ -291,85 +323,64 @@ nsDefaultURIFixup::CreateFixupURI(const nsACString& aStringURI, uint32_t aFixupF
// It's more likely the user wants to search, and so we
// chuck this over to their preferred search provider instead:
if (!handlerExists) {
NS_RELEASE(*aURI);
KeywordToURI(uriString, aPostData, aURI);
nsresult rv = KeywordToURI(uriString, aPostData, getter_AddRefs(uri));
if (NS_SUCCEEDED(rv) && uri) {
info->mFixupUsedKeyword = true;
}
}
}
}
if (*aURI) {
if (uri) {
if (aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI)
MakeAlternateURI(*aURI);
MakeAlternateURI(uri);
info->mPreferredURI = uri;
return NS_OK;
}
// Fix up protocol string before calling KeywordURIFixup, because
// it cares about the hostname of such URIs:
nsCOMPtr<nsIURI> uriWithProtocol;
// NB: this rv gets returned at the end of this method if we never
// do a keyword fixup after this (because the pref or the flags passed
// might not let us).
rv = FixupURIProtocol(uriString, getter_AddRefs(uriWithProtocol));
if (uriWithProtocol) {
info->mFixedURI = uriWithProtocol;
nsAutoCString host;
uriWithProtocol->GetHost(host);
info->mInputHostHasDot = host.FindChar('.') != kNotFound;
}
// See if it is a keyword
// Test whether keywords need to be fixed up
if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP)) {
KeywordURIFixup(uriString, aPostData, aURI);
if(*aURI)
KeywordURIFixup(uriString, info, aPostData);
if (info->mPreferredURI)
return NS_OK;
}
// Prune duff protocol schemes
//
// ://totallybroken.url.com
// //shorthand.url.com
//
if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://")))
{
uriString = StringTail(uriString, uriString.Length() - 3);
}
else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//")))
{
uriString = StringTail(uriString, uriString.Length() - 2);
}
// Add ftp:// or http:// to front of url if it has no spec
//
// Should fix:
//
// no-scheme.com
// ftp.no-scheme.com
// ftp4.no-scheme.com
// no-scheme.com/query?foo=http://www.foo.com
//
int32_t schemeDelim = uriString.Find("://",0);
int32_t firstDelim = uriString.FindCharInSet("/:");
if (schemeDelim <= 0 ||
(firstDelim != -1 && schemeDelim > firstDelim)) {
// find host name
int32_t hostPos = uriString.FindCharInSet("/:?#");
if (hostPos == -1)
hostPos = uriString.Length();
// extract host name
nsAutoCString hostSpec;
uriString.Left(hostSpec, hostPos);
// insert url spec corresponding to host name
if (IsLikelyFTP(hostSpec))
uriString.InsertLiteral("ftp://", 0);
else
uriString.InsertLiteral("http://", 0);
} // end if checkprotocol
rv = NS_NewURI(aURI, uriString, nullptr);
// Did the caller want us to try an alternative URI?
// If so, attempt to fixup http://foo into http://www.foo.com
if (*aURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
MakeAlternateURI(*aURI);
if (info->mFixedURI && aFixupFlags & FIXUP_FLAGS_MAKE_ALTERNATE_URI) {
MakeAlternateURI(info->mFixedURI);
}
// If we still haven't been able to construct a valid URI, try to force a
// keyword match. This catches search strings with '.' or ':' in them.
if (!*aURI && sFixupKeywords)
if (info->mFixedURI)
{
KeywordToURI(aStringURI, aPostData, aURI);
if(*aURI)
info->mPreferredURI = info->mFixedURI;
}
else if (sFixupKeywords && (aFixupFlags & FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP))
{
rv = KeywordToURI(aStringURI, aPostData, getter_AddRefs(info->mPreferredURI));
if (NS_SUCCEEDED(rv) && info->mPreferredURI)
{
info->mFixupUsedKeyword = true;
return NS_OK;
}
}
return rv;
@ -708,6 +719,61 @@ nsresult nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
return NS_ERROR_FAILURE;
}
nsresult
nsDefaultURIFixup::FixupURIProtocol(const nsACString & aURIString,
nsIURI** aURI)
{
nsAutoCString uriString(aURIString);
*aURI = nullptr;
// Prune duff protocol schemes
//
// ://totallybroken.url.com
// //shorthand.url.com
//
if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("://")))
{
uriString = StringTail(uriString, uriString.Length() - 3);
}
else if (StringBeginsWith(uriString, NS_LITERAL_CSTRING("//")))
{
uriString = StringTail(uriString, uriString.Length() - 2);
}
// Add ftp:// or http:// to front of url if it has no spec
//
// Should fix:
//
// no-scheme.com
// ftp.no-scheme.com
// ftp4.no-scheme.com
// no-scheme.com/query?foo=http://www.foo.com
//
int32_t schemeDelim = uriString.Find("://",0);
int32_t firstDelim = uriString.FindCharInSet("/:");
if (schemeDelim <= 0 ||
(firstDelim != -1 && schemeDelim > firstDelim)) {
// find host name
int32_t hostPos = uriString.FindCharInSet("/:?#");
if (hostPos == -1)
hostPos = uriString.Length();
// extract host name
nsAutoCString hostSpec;
uriString.Left(hostSpec, hostPos);
// insert url spec corresponding to host name
if (IsLikelyFTP(hostSpec))
uriString.InsertLiteral("ftp://", 0);
else
uriString.InsertLiteral("http://", 0);
} // end if checkprotocol
return NS_NewURI(aURI, uriString, nullptr);
}
bool nsDefaultURIFixup::PossiblyHostPortUrl(const nsACString &aUrl)
{
// Oh dear, the protocol is invalid. Test if the protocol might
@ -830,8 +896,8 @@ bool nsDefaultURIFixup::PossiblyByteExpandedFileName(const nsAString& aIn)
}
void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
nsIInputStream **aPostData,
nsIURI** aURI)
nsDefaultURIFixupInfo* aFixupInfo,
nsIInputStream **aPostData)
{
// These are keyword formatted strings
// "what is mozilla"
@ -840,6 +906,7 @@ void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
// "?mozilla" - anything that begins with a question mark
// "?site:mozilla.org docshell"
// Things that have a quote before the first dot/colon
// "mozilla" - checked against a whitelist to see if it's a host or not
// These are not keyword formatted strings
// "www.blah.com" - first space-separated substring contains a dot, doesn't start with "?"
@ -865,16 +932,46 @@ void nsDefaultURIFixup::KeywordURIFixup(const nsACString & aURIString,
uint32_t quoteLoc = std::min(uint32_t(aURIString.FindChar('"')),
uint32_t(aURIString.FindChar('\'')));
nsresult rv;
if (((spaceLoc < dotLoc || quoteLoc < dotLoc) &&
(spaceLoc < colonLoc || quoteLoc < colonLoc) &&
(spaceLoc < qMarkLoc || quoteLoc < qMarkLoc)) ||
qMarkLoc == 0)
{
KeywordToURI(aURIString, aPostData, aURI);
rv = KeywordToURI(aURIString, aPostData,
getter_AddRefs(aFixupInfo->mPreferredURI));
if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
{
aFixupInfo->mFixupUsedKeyword = true;
}
}
else if (dotLoc == uint32_t(kNotFound) && colonLoc == uint32_t(kNotFound) &&
qMarkLoc == uint32_t(kNotFound))
{
nsAutoCString asciiHost;
if (NS_SUCCEEDED(aFixupInfo->mFixedURI->GetAsciiHost(asciiHost)) &&
!asciiHost.IsEmpty())
{
// Check if this domain is whitelisted as an actual
// domain (which will prevent a keyword query)
nsAutoCString pref("browser.fixup.domainwhitelist.");
pref.Append(asciiHost);
if (Preferences::GetBool(pref.get(), false))
{
return;
}
}
// If we get here, we don't have a valid URI, or we did but the
// host is not whitelisted, so we do a keyword search *anyway*:
rv = KeywordToURI(aURIString, aPostData,
getter_AddRefs(aFixupInfo->mPreferredURI));
if (NS_SUCCEEDED(rv) && aFixupInfo->mPreferredURI)
{
aFixupInfo->mFixupUsedKeyword = true;
}
}
}
nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup)
{
nsDefaultURIFixup *fixup = new nsDefaultURIFixup;
@ -885,3 +982,78 @@ nsresult NS_NewURIFixup(nsIURIFixup **aURIFixup)
return fixup->QueryInterface(NS_GET_IID(nsIURIFixup), (void **) aURIFixup);
}
/* Implementation of nsIURIFixupInfo */
NS_IMPL_ISUPPORTS(nsDefaultURIFixupInfo, nsIURIFixupInfo)
nsDefaultURIFixupInfo::nsDefaultURIFixupInfo(const nsACString& aOriginalInput):
mFixupUsedKeyword(false),
mInputHasProtocol(false),
mInputHostHasDot(false)
{
mOriginalInput = aOriginalInput;
}
nsDefaultURIFixupInfo::~nsDefaultURIFixupInfo()
{
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetConsumer(nsISupports** aConsumer)
{
*aConsumer = mConsumer;
NS_IF_ADDREF(*aConsumer);
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::SetConsumer(nsISupports* aConsumer)
{
mConsumer = aConsumer;
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetPreferredURI(nsIURI** aPreferredURI)
{
*aPreferredURI = mPreferredURI;
NS_IF_ADDREF(*aPreferredURI);
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetFixedURI(nsIURI** aFixedURI)
{
*aFixedURI = mFixedURI;
NS_IF_ADDREF(*aFixedURI);
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetFixupUsedKeyword(bool* aOut)
{
*aOut = mFixupUsedKeyword;
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetInputHasProtocol(bool* aOut)
{
*aOut = mInputHasProtocol;
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetInputHostHasDot(bool* aOut)
{
*aOut = mInputHostHasDot;
return NS_OK;
}
NS_IMETHODIMP
nsDefaultURIFixupInfo::GetOriginalInput(nsACString& aInput)
{
aInput = mOriginalInput;
return NS_OK;
}

View File

@ -9,6 +9,8 @@
#include "nsIURIFixup.h"
class nsDefaultURIFixupInfo;
/* Header file */
class nsDefaultURIFixup : public nsIURIFixup
{
@ -25,11 +27,36 @@ private:
/* additional members */
nsresult FileURIFixup(const nsACString &aStringURI, nsIURI** aURI);
nsresult ConvertFileToStringURI(const nsACString& aIn, nsCString& aOut);
void KeywordURIFixup(const nsACString &aStringURI, nsIInputStream** aPostData, nsIURI** aURI);
nsresult FixupURIProtocol(const nsACString& aIn, nsIURI** aURI);
void KeywordURIFixup(const nsACString &aStringURI,
nsDefaultURIFixupInfo* aFixupInfo,
nsIInputStream** aPostData);
bool PossiblyByteExpandedFileName(const nsAString& aIn);
bool PossiblyHostPortUrl(const nsACString& aUrl);
bool MakeAlternateURI(nsIURI *aURI);
bool IsLikelyFTP(const nsCString& aHostSpec);
};
class nsDefaultURIFixupInfo : public nsIURIFixupInfo
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIURIFIXUPINFO
nsDefaultURIFixupInfo(const nsACString& aOriginalInput);
friend class nsDefaultURIFixup;
protected:
virtual ~nsDefaultURIFixupInfo();
private:
nsCOMPtr<nsISupports> mConsumer;
nsCOMPtr<nsIURI> mPreferredURI;
nsCOMPtr<nsIURI> mFixedURI;
bool mFixupUsedKeyword;
bool mInputHasProtocol;
bool mInputHostHasDot;
nsAutoCString mOriginalInput;
};
#endif

View File

@ -4362,15 +4362,29 @@ nsDocShell::LoadURIWithBase(const char16_t * aURI,
fixupFlags |= nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS;
}
nsCOMPtr<nsIInputStream> fixupStream;
rv = sURIFixup->CreateFixupURI(uriString, fixupFlags,
getter_AddRefs(fixupStream),
getter_AddRefs(uri));
nsCOMPtr<nsIURIFixupInfo> fixupInfo;
rv = sURIFixup->GetFixupURIInfo(uriString, fixupFlags,
getter_AddRefs(fixupStream),
getter_AddRefs(fixupInfo));
if (NS_SUCCEEDED(rv)) {
fixupInfo->GetPreferredURI(getter_AddRefs(uri));
fixupInfo->SetConsumer(GetAsSupports(this));
}
if (fixupStream) {
// CreateFixupURI only returns a post data stream if it succeeded
// and changed the URI, in which case we should override the
// passed-in post data.
postStream = fixupStream;
}
if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
if (serv) {
serv->NotifyObservers(fixupInfo, "keyword-uri-fixup", aURI);
}
}
}
// else no fixup service so just use the URI we created and see
// what happens

View File

@ -9,10 +9,61 @@
interface nsIURI;
interface nsIInputStream;
/**
* Interface indicating what we found/corrected when fixing up a URI
*/
[scriptable, uuid(c9b6cc32-c24e-4283-adaa-9290577fd609)]
interface nsIURIFixupInfo : nsISupports
{
/**
* Consumer that asked for fixed up URI.
*/
attribute nsISupports consumer;
/**
* Our best guess as to what URI the consumer will want. Might
* be null if we couldn't salvage anything (for instance, because
* the input was invalid as a URI and FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP
* was not passed)
*/
readonly attribute nsIURI preferredURI;
/**
* The fixed-up original input, *never* using a keyword search.
* (might be null if the original input was not recoverable as
* a URL, e.g. "foo bar"!)
*/
readonly attribute nsIURI fixedURI;
/**
* Whether the preferred option ended up using a keyword search.
*/
readonly attribute boolean fixupUsedKeyword;
/**
* Whether we think there was a protocol specified in some way,
* even if we corrected it (e.g. "ttp://foo.com/bar")
*/
readonly attribute boolean inputHasProtocol;
/**
* Whether the input included a dot in the hostname, e.g. "mozilla.org"
* rather than just "mozilla". This makes a difference in terms of when we
* decide to do a keyword search or not.
*/
readonly attribute boolean inputHostHasDot;
/**
* The original input
*/
readonly attribute AUTF8String originalInput;
};
/**
* Interface implemented by objects capable of fixing up strings into URIs
*/
[scriptable, uuid(731877f8-973b-414c-b772-9ca1f3fffb7e)]
[scriptable, uuid(80d4932e-bb2e-4afb-98e0-de9cc9ea7d82)]
interface nsIURIFixup : nsISupports
{
/** No fixup flags. */
@ -63,6 +114,20 @@ interface nsIURIFixup : nsISupports
nsIURI createFixupURI(in AUTF8String aURIText, in unsigned long aFixupFlags,
[optional] out nsIInputStream aPostData);
/**
* Same as createFixupURI, but returns information about what it corrected
* (e.g. whether we could rescue the URI or "just" generated a keyword
* search URI instead).
*
* @param aURIText Candidate URI.
* @param aFixupFlags Flags that govern ways the URI may be fixed up.
* @param aPostData The POST data to submit with the returned
* URI (see nsISearchSubmission).
*/
nsIURIFixupInfo getFixupURIInfo(in AUTF8String aURIText,
in unsigned long aFixupFlags,
[optional] out nsIInputStream aPostData);
/**
* Converts the specified keyword string into a URI. Note that it's the
* caller's responsibility to check whether keywords are enabled and

View File

@ -0,0 +1,147 @@
let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].
getService(Ci.nsIURIFixup);
Components.utils.import("resource://gre/modules/Services.jsm");
let prefList = ["browser.fixup.typo.scheme", "keyword.enabled"];
for (let pref of prefList) {
Services.prefs.setBoolPref(pref, true);
}
const kSearchEngineID = "test_urifixup_search_engine";
const kSearchEngineURL = "http://www.example.org/?search={searchTerms}";
Services.search.addEngineWithDetails(kSearchEngineID, "", "", "", "get",
kSearchEngineURL);
let oldDefaultEngine = Services.search.defaultEngine;
Services.search.defaultEngine = Services.search.getEngineByName(kSearchEngineID);
let selectedName = Services.search.defaultEngine.name;
do_check_eq(selectedName, kSearchEngineID);
do_register_cleanup(function() {
if (oldDefaultEngine) {
Services.search.defaultEngine = oldDefaultEngine;
}
let engine = Services.search.getEngineByName(kSearchEngineID);
if (engine) {
Services.search.removeEngine(engine);
}
Services.prefs.clearUserPref("keyword.enabled");
Services.prefs.clearUserPref("browser.fixup.typo.scheme");
});
let flagInputs = [
urifixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
urifixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI,
urifixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
];
flagInputs.concat([
flagInputs[0] | flagInputs[1],
flagInputs[1] | flagInputs[2],
flagInputs[0] | flagInputs[2],
flagInputs[0] | flagInputs[1] | flagInputs[2]
]);
let testcases = [
["http://www.mozilla.org", "http://www.mozilla.org/"],
["://www.mozilla.org", "http://www.mozilla.org/"],
["www.mozilla.org", "http://www.mozilla.org/"],
["http://mozilla/", "http://mozilla/"],
["127.0.0.1", "http://127.0.0.1/"],
["1234", "http://1234/"],
["host/foo.txt", "http://host/foo.txt"],
["mozilla", "http://mozilla/"],
["mozilla is amazing", null],
["", null],
];
if (Services.appinfo.OS.toLowerCase().startsWith("win")) {
testcases.push(["C:\\some\\file.txt", "file:///C:/some/file.txt"]);
} else {
testcases.push(["/some/file.txt", "file:///some/file.txt"]);
}
function run_test() {
for (let [testInput, expectedFixedURI] of testcases) {
for (let flags of flagInputs) {
let info;
let fixupURIOnly = null;
try {
fixupURIOnly = urifixup.createFixupURI(testInput, flags);
} catch (ex) {
do_check_eq(expectedFixedURI, null);
}
try {
info = urifixup.getFixupURIInfo(testInput, flags);
} catch (ex) {
// Both APIs should return an error in the same cases.
do_check_eq(expectedFixedURI, null);
do_check_eq(fixupURIOnly, null);
continue;
}
// Both APIs should then also be using the same spec.
do_check_eq(fixupURIOnly.spec, info.preferredURI.spec);
let isFileURL = expectedFixedURI && expectedFixedURI.startsWith("file");
// Check the fixedURI:
let alternateURI = flags & urifixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI;
if (!isFileURL && alternateURI && !info.inputHostHasDot && info.fixedURI) {
let originalURI = Services.io.newURI(expectedFixedURI, null, null);
do_check_eq(info.fixedURI.host, "www." + originalURI.host + ".com");
} else {
do_check_eq(info.fixedURI && info.fixedURI.spec, expectedFixedURI);
}
// Check booleans on input:
if (isFileURL) {
do_check_eq(info.inputHasProtocol, testInput.startsWith("file:"));
do_check_eq(info.inputHostHasDot, false);
} else {
// The duff protocol doesn't count, so > 0 rather than -1:
do_check_eq(info.inputHasProtocol, testInput.indexOf(":") > 0);
let dotIndex = testInput.indexOf(".");
let slashIndex = testInput.replace("://", "").indexOf("/");
slashIndex = slashIndex == -1 ? testInput.length : slashIndex;
do_check_eq(info.inputHostHasDot, dotIndex != -1 && slashIndex > dotIndex);
}
let couldDoKeywordLookup = flags & urifixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
// Check the preferred URI
if (info.inputHostHasDot || info.inputHasProtocol) {
// In these cases, we should never be doing a keyword lookup and
// the fixed URI should be preferred:
do_check_eq(info.preferredURI.spec, info.fixedURI.spec);
} else if (!isFileURL && couldDoKeywordLookup && testInput.indexOf(".") == -1) {
// Otherwise, and assuming we're allowed, there will be a search URI:
let urlparamInput = testInput.replace(/ /g, '+');
let searchURL = kSearchEngineURL.replace("{searchTerms}", urlparamInput);
do_check_eq(info.preferredURI.spec, searchURL);
} else if (info.fixedURI) {
// This is for lack of keyword lookup, combined with hostnames with no
// protocol:
do_check_eq(info.fixedURI, info.preferredURI);
if (isFileURL) {
do_check_eq(info.fixedURI.host, "");
} else {
let hostMatch = testInput.match(/(?:[^:\/]*:\/\/)?([^\/]+)(\/|$)/);
let host = hostMatch ? hostMatch[1] : "";
if (alternateURI) {
do_check_eq(info.fixedURI.host, "www." + host + ".com");
} else {
do_check_eq(info.fixedURI.host, host);
}
}
} else {
do_check_true(false, "There should be no cases where we got here, " +
"there's no keyword lookup, and no fixed URI." +
"Offending input: " + testInput);
}
do_check_eq(testInput, info.originalInput);
}
}
}

View File

@ -7,6 +7,8 @@ tail =
[test_nsDefaultURIFixup.js]
[test_nsDefaultURIFixup_search.js]
skip-if = os == 'android'
[test_nsDefaultURIFixup_info.js]
skip-if = os == 'android'
[test_nsIDownloadHistory.js]
[test_pb_notification.js]
# Bug 751575: unrelated JS changes cause timeouts on random platforms

View File

@ -230,6 +230,7 @@ pref("dom.disable_window_print", true);
pref("dom.disable_window_find", true);
pref("keyword.enabled", true);
pref("browser.fixup.domainwhitelist.localhost", true);
pref("accessibility.typeaheadfind", false);
pref("accessibility.typeaheadfind.timeout", 5000);

View File

@ -106,7 +106,13 @@
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|locale|layoutDirection"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:launchMode="singleTask"
android:exported="true"
android:theme="@style/Gecko.App">
<!-- We export this activity so that it can be launched by explicit
intents, in particular homescreen shortcuts. See Bug 1032217.
In future we would prefer to move all intent filters off the .App
alias and onto BrowserApp so that we can deprecate activities
that refer to pre-processed class names. -->
</activity>
<!-- Fennec is shipped as the Android package named
@ -255,10 +261,16 @@
android:taskAffinity="org.mozilla.gecko.WEBAPP"
android:process=":@ANDROID_PACKAGE_NAME@.Webapp"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@style/Gecko.App">
<!-- We export this activity so that it can be launched by explicit
intents, in particular old-style WebApp launching homescreen
shortcuts. Such shortcuts were made before the new "synthetic
APK" WebApps were deployed. See Bug 1032217. -->
</activity>
<!-- Alias Webapp so we can launch it from the package namespace. -->
<!-- Alias Webapp so we can launch it from the package namespace. Prefer
to launch with the fully qualified name "org.mozilla.gecko.Webapp". -->
<activity-alias android:name=".Webapp"
android:label="@string/webapp_generic_name"
android:targetActivity="org.mozilla.gecko.Webapp">

View File

@ -5,18 +5,13 @@
package org.mozilla.gecko;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@ -81,8 +76,6 @@ import android.hardware.SensorEventListener;
import android.location.Location;
import android.location.LocationListener;
import android.net.Uri;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -90,12 +83,6 @@ import android.os.PowerManager;
import android.os.StrictMode;
import android.provider.ContactsContract;
import android.provider.MediaStore.Images.Media;
import android.telephony.CellLocation;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Base64;
@ -165,8 +152,6 @@ public abstract class GeckoApp
public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
public static final String SAVED_STATE_PRIVATE_SESSION = "privateSession";
static private final String LOCATION_URL = "https://location.services.mozilla.com/v1/submit";
// Delay before running one-time "cleanup" tasks that may be needed
// after a version upgrade.
private static final int CLEANUP_DEFERRAL_SECONDS = 15;
@ -207,9 +192,6 @@ public abstract class GeckoApp
private volatile HealthRecorder mHealthRecorder = null;
private volatile Locale mLastLocale = null;
private int mSignalStrenth;
private PhoneStateListener mPhoneStateListener = null;
private boolean mShouldReportGeoData;
private EventListener mWebappEventListener;
abstract public int getLayout();
@ -249,15 +231,6 @@ public abstract class GeckoApp
}
public LocationListener getLocationListener() {
if (mShouldReportGeoData && mPhoneStateListener == null) {
mPhoneStateListener = new PhoneStateListener() {
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
setCurrentSignalStrenth(signalStrength);
}
};
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
return this;
}
@ -1578,10 +1551,7 @@ public abstract class GeckoApp
PrefsHelper.getPref("app.geo.reportdata", new PrefsHelper.PrefHandlerBase() {
@Override public void prefValue(String pref, int value) {
if (value == 1)
mShouldReportGeoData = true;
else
mShouldReportGeoData = false;
// Acting on this pref is Bug 1036508; for now, do nothing.
}
});
@ -2373,197 +2343,6 @@ public abstract class GeckoApp
public void onLocationChanged(Location location) {
// No logging here: user-identifying information.
GeckoAppShell.sendEventToGecko(GeckoEvent.createLocationEvent(location));
if (mShouldReportGeoData)
collectAndReportLocInfo(location);
}
public void setCurrentSignalStrenth(SignalStrength ss) {
if (ss.isGsm())
mSignalStrenth = ss.getGsmSignalStrength();
}
private int getCellInfo(JSONArray cellInfo) {
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null)
return TelephonyManager.PHONE_TYPE_NONE;
List<NeighboringCellInfo> cells = tm.getNeighboringCellInfo();
CellLocation cl = tm.getCellLocation();
String mcc = "", mnc = "";
if (cl instanceof GsmCellLocation) {
JSONObject obj = new JSONObject();
GsmCellLocation gcl = (GsmCellLocation)cl;
try {
obj.put("lac", gcl.getLac());
obj.put("cid", gcl.getCid());
int psc = (Build.VERSION.SDK_INT >= 9) ? gcl.getPsc() : -1;
obj.put("psc", psc);
switch(tm.getNetworkType()) {
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_EDGE:
obj.put("radio", "gsm");
break;
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSPAP:
obj.put("radio", "umts");
break;
}
String mcc_mnc = tm.getNetworkOperator();
if (mcc_mnc.length() > 3) {
mcc = mcc_mnc.substring(0, 3);
mnc = mcc_mnc.substring(3);
obj.put("mcc", mcc);
obj.put("mnc", mnc);
}
obj.put("asu", mSignalStrenth);
} catch(JSONException jsonex) {}
cellInfo.put(obj);
}
if (cells != null) {
for (NeighboringCellInfo nci : cells) {
try {
JSONObject obj = new JSONObject();
obj.put("lac", nci.getLac());
obj.put("cid", nci.getCid());
obj.put("psc", nci.getPsc());
obj.put("mcc", mcc);
obj.put("mnc", mnc);
int dbm;
switch(nci.getNetworkType()) {
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_EDGE:
obj.put("radio", "gsm");
break;
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSPAP:
obj.put("radio", "umts");
break;
}
obj.put("asu", nci.getRssi());
cellInfo.put(obj);
} catch(JSONException jsonex) {}
}
}
return tm.getPhoneType();
}
private static boolean shouldLog(final ScanResult sr) {
return sr.SSID == null || !sr.SSID.endsWith("_nomap");
}
private void collectAndReportLocInfo(Location location) {
final JSONObject locInfo = new JSONObject();
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
wm.startScan();
try {
JSONArray cellInfo = new JSONArray();
String radioType = getRadioTypeName(getCellInfo(cellInfo));
if (radioType != null) {
locInfo.put("radio", radioType);
}
locInfo.put("lon", location.getLongitude());
locInfo.put("lat", location.getLatitude());
// If we have an accuracy, round it up to the next meter.
if (location.hasAccuracy()) {
locInfo.put("accuracy", (int) Math.ceil(location.getAccuracy()));
}
// If we have an altitude, round it to the nearest meter.
if (location.hasAltitude()) {
locInfo.put("altitude", Math.round(location.getAltitude()));
}
// Reduce timestamp precision so as to expose less PII.
DateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
locInfo.put("time", df.format(new Date(location.getTime())));
locInfo.put("cell", cellInfo);
JSONArray wifiInfo = new JSONArray();
List<ScanResult> aps = wm.getScanResults();
if (aps != null) {
for (ScanResult ap : aps) {
if (!shouldLog(ap))
continue;
JSONObject obj = new JSONObject();
obj.put("key", ap.BSSID);
obj.put("frequency", ap.frequency);
obj.put("signal", ap.level);
wifiInfo.put(obj);
}
}
locInfo.put("wifi", wifiInfo);
} catch (JSONException jsonex) {
Log.w(LOGTAG, "json exception", jsonex);
return;
}
ThreadUtils.postToBackgroundThread(new Runnable() {
public void run() {
try {
URL url = new URL(LOCATION_URL);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
urlConnection.setDoOutput(true);
// Workaround for a bug in Android HttpURLConnection. When the library
// reuses a stale connection, the connection may fail with an EOFException.
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT <= 18) {
urlConnection.setRequestProperty("Connection", "Close");
}
JSONArray batch = new JSONArray();
batch.put(locInfo);
JSONObject wrapper = new JSONObject();
wrapper.put("items", batch);
byte[] bytes = wrapper.toString().getBytes();
urlConnection.setFixedLengthStreamingMode(bytes.length);
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(bytes);
out.flush();
} catch (JSONException jsonex) {
Log.e(LOGTAG, "error wrapping data as a batch", jsonex);
} catch (IOException ioex) {
Log.e(LOGTAG, "error submitting data", ioex);
} finally {
urlConnection.disconnect();
}
} catch (IOException ioex) {
Log.e(LOGTAG, "error submitting data", ioex);
}
}
});
}
private static String getRadioTypeName(int phoneType) {
switch (phoneType) {
case TelephonyManager.PHONE_TYPE_CDMA:
return "cdma";
case TelephonyManager.PHONE_TYPE_GSM:
return "gsm";
case TelephonyManager.PHONE_TYPE_NONE:
case TelephonyManager.PHONE_TYPE_SIP:
// These devices have no radio.
return null;
default:
Log.e(LOGTAG, "", new IllegalArgumentException("Unexpected PHONE_TYPE: " + phoneType));
return null;
}
}
@Override

View File

@ -1524,8 +1524,8 @@ var BrowserApp = {
let url = data.url;
let flags;
if (!data.engine && /^[0-9]+$/.test(url)) {
// If the query is a number and we're not using a search engine,
if (!data.engine && /^\w+$/.test(url.trim())) {
// If the query is a single word and we're not using a search engine,
// force a search (see bug 993705; workaround for bug 693808).
url = URIFixup.keywordToURI(url).spec;
} else {

View File

@ -20,8 +20,6 @@ public class Constants {
public static final String PRESEARCH_FRAGMENT = "org.mozilla.search.PRESEARCH_FRAGMENT";
public static final String SEARCH_FRAGMENT = "org.mozilla.search.SEARCH_FRAGMENT";
public static final String AUTOCOMPLETE_ROW_LIMIT = "5";
public static final String YAHOO_WEB_SEARCH_BASE_URL = "https://search.yahoo.com/search?p=";
public static final String YAHOO_WEB_SEARCH_RESULTS_FILTER = "//search.yahoo.com";
}

View File

@ -9,10 +9,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import java.util.List;
/**
* The adapter that is used to populate the autocomplete rows.
*/
class AutoCompleteAdapter extends ArrayAdapter<AutoCompleteModel> {
class AutoCompleteAdapter extends ArrayAdapter<String> {
private final AcceptsJumpTaps acceptsJumpTaps;
@ -21,6 +23,9 @@ class AutoCompleteAdapter extends ArrayAdapter<AutoCompleteModel> {
// and supplying our own view.
super(context, 0);
this.acceptsJumpTaps = acceptsJumpTaps;
// Disable notifying on change. We will notify ourselves in update.
setNotifyOnChange(false);
}
@Override
@ -34,12 +39,23 @@ class AutoCompleteAdapter extends ArrayAdapter<AutoCompleteModel> {
}
view.setOnJumpListener(acceptsJumpTaps);
AutoCompleteModel model = getItem(position);
view.setMainText(model.getMainText());
view.setMainText(getItem(position));
return view;
}
/**
* Updates adapter content with new list of search suggestions.
*
* @param suggestions List of search suggestions.
*/
public void update(List<String> suggestions) {
clear();
if (suggestions != null) {
for (String s : suggestions) {
add(s);
}
}
notifyDataSetChanged();
}
}

View File

@ -1,84 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.search.autocomplete;
import android.app.Activity;
import android.database.Cursor;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import java.util.ArrayList;
/**
* A single entry point for querying all agents.
* <p/>
* An agent is responsible for querying some underlying data source. It could be a
* flat file, or a REST endpoint, or a content provider.
*/
class AutoCompleteAgentManager {
private final Handler mainUiHandler;
private final Handler localHandler;
private final AutoCompleteWordListAgent autoCompleteWordListAgent;
public AutoCompleteAgentManager(Activity activity, Handler mainUiHandler) {
HandlerThread thread = new HandlerThread("org.mozilla.search.autocomplete.SuggestionAgent");
// TODO: Where to kill this thread?
thread.start();
Log.i("AUTOCOMPLETE", "Starting thread");
this.mainUiHandler = mainUiHandler;
localHandler = new SuggestionMessageHandler(thread.getLooper());
autoCompleteWordListAgent = new AutoCompleteWordListAgent(activity);
}
/**
* Process the next incoming query.
*/
public void search(String queryString) {
// TODO check if there's a pending search.. not sure how to handle that.
localHandler.sendMessage(localHandler.obtainMessage(0, queryString));
}
/**
* This background thread runs the queries; the results get sent back through mainUiHandler
* <p/>
* TODO: Refactor this wordlist search and add other search providers (eg: Yahoo)
*/
private class SuggestionMessageHandler extends Handler {
private SuggestionMessageHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (null == msg.obj) {
return;
}
Cursor cursor =
autoCompleteWordListAgent.getWordMatches(((String) msg.obj).toLowerCase());
ArrayList<AutoCompleteModel> res = new ArrayList<AutoCompleteModel>();
if (null == cursor) {
return;
}
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
res.add(new AutoCompleteModel(cursor.getString(
cursor.getColumnIndex(AutoCompleteWordListAgent.COL_WORD))));
}
mainUiHandler.sendMessage(Message.obtain(mainUiHandler, 0, res));
}
}
}

View File

@ -1,31 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.search.autocomplete;
/**
* The SuggestionModel is the data model behind the autocomplete rows. Right now it
* only has a text field. In the future, this could be extended to include other
* types of rows. For example, a row that has a URL and the name of a website.
*/
class AutoCompleteModel {
// The text that should immediately jump out to the user;
// for example, the name of a restaurant or the title
// of a website.
private final String mainText;
public AutoCompleteModel(String mainText) {
this.mainText = mainText;
}
public String getMainText() {
return mainText;
}
public String toString() {
return mainText;
}
}

View File

@ -1,153 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.search.autocomplete;
import android.app.Activity;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;
import android.widget.Toast;
import org.mozilla.search.Constants;
import org.mozilla.search.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Helper to search a word dictionary.
* From: https://developer.android.com/training/search/search.html
*/
class AutoCompleteWordListAgent {
public static final String COL_WORD = "WORD";
private static final String TAG = "DictionaryDatabase";
private static final String DATABASE_NAME = "DICTIONARY";
private static final String FTS_VIRTUAL_TABLE = "FTS";
private static final int DATABASE_VERSION = 1;
private final DatabaseOpenHelper databaseOpenHelper;
public AutoCompleteWordListAgent(Activity activity) {
databaseOpenHelper = new DatabaseOpenHelper(activity);
// DB helper uses lazy initialization, so this forces the db helper to start indexing the
// wordlist
databaseOpenHelper.getReadableDatabase();
}
public Cursor getWordMatches(String query) {
String selection = COL_WORD + " MATCH ?";
String[] selectionArgs = new String[]{query + "*"};
return query(selection, selectionArgs);
}
private Cursor query(String selection, String[] selectionArgs) {
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(FTS_VIRTUAL_TABLE);
Cursor cursor = builder.query(databaseOpenHelper.getReadableDatabase(), null, selection,
selectionArgs, null, null, null, Constants.AUTOCOMPLETE_ROW_LIMIT);
if (cursor == null) {
return null;
} else if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
return cursor;
}
private static class DatabaseOpenHelper extends SQLiteOpenHelper {
private final Activity activity;
private SQLiteDatabase database;
private static final String FTS_TABLE_CREATE =
"CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE + " USING fts3 (" + COL_WORD + ")";
DatabaseOpenHelper(Activity activity) {
super(activity, DATABASE_NAME, null, DATABASE_VERSION);
this.activity = activity;
}
@Override
public void onCreate(SQLiteDatabase db) {
database = db;
database.execSQL(FTS_TABLE_CREATE);
loadDictionary();
}
private void loadDictionary() {
new Thread(new Runnable() {
public void run() {
try {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "Starting post-install indexing",
Toast.LENGTH_SHORT).show();
Toast.makeText(activity,
"Don't worry; Mark & Ian we'll figure out a way around " +
"this :)", Toast.LENGTH_SHORT
).show();
}
});
loadWords();
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "All done!", Toast.LENGTH_SHORT).show();
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private void loadWords() throws IOException {
final Resources resources = activity.getResources();
InputStream inputStream = resources.openRawResource(R.raw.en_us);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String sql = "INSERT INTO " + FTS_VIRTUAL_TABLE + " VALUES (?);";
SQLiteStatement statement = database.compileStatement(sql);
database.beginTransaction();
try {
String line;
while (null != (line = reader.readLine())) {
statement.clearBindings();
statement.bindString(1, line.trim());
statement.execute();
}
} finally {
database.setTransactionSuccessful();
database.endTransaction();
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion +
", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
onCreate(db);
}
}
}

View File

@ -7,9 +7,10 @@ package org.mozilla.search.autocomplete;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
@ -27,22 +28,37 @@ import android.widget.TextView;
import org.mozilla.search.R;
import java.util.List;
/**
* A fragment to handle autocomplete. Its interface with the outside
* world should be very very limited.
* <p/>
* TODO: Add more search providers (other than the dictionary)
*/
public class SearchFragment extends Fragment implements AdapterView.OnItemClickListener,
TextView.OnEditorActionListener, AcceptsJumpTaps {
public class SearchFragment extends Fragment
implements TextView.OnEditorActionListener, AcceptsJumpTaps {
private static final int LOADER_ID_SUGGESTION = 0;
private static final String KEY_SEARCH_TERM = "search_term";
// Timeout for the suggestion client to respond
private static final int SUGGESTION_TIMEOUT = 3000;
// Maximum number of results returned by the suggestion client
private static final int SUGGESTION_MAX = 5;
private View mainView;
private FrameLayout backdropFrame;
private EditText searchBar;
private ListView suggestionDropdown;
private InputMethodManager inputMethodManager;
private AutoCompleteAdapter autoCompleteAdapter;
private AutoCompleteAgentManager autoCompleteAgentManager;
private SuggestClient suggestClient;
private SuggestionLoaderCallbacks suggestionLoaderCallbacks;
private State state;
private enum State {
@ -58,7 +74,6 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mainView = inflater.inflate(R.layout.search_auto_complete, container, false);
backdropFrame = (FrameLayout) mainView.findViewById(R.id.auto_complete_backdrop);
searchBar = (EditText) mainView.findViewById(R.id.auto_complete_search_bar);
@ -71,17 +86,17 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
searchBar.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
autoCompleteAgentManager.search(s.toString());
final Bundle args = new Bundle();
args.putString(KEY_SEARCH_TERM, s.toString());
getLoaderManager().restartLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks);
}
});
searchBar.setOnEditorActionListener(this);
@ -106,18 +121,8 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
backdropFrame.setOnClickListener(new BackdropClickListener());
autoCompleteAdapter = new AutoCompleteAdapter(getActivity(), this);
// Disable notifying on change. We're going to be changing the entire dataset, so
// we don't want multiple re-draws.
autoCompleteAdapter.setNotifyOnChange(false);
suggestionDropdown.setAdapter(autoCompleteAdapter);
initRows();
autoCompleteAgentManager =
new AutoCompleteAgentManager(getActivity(), new MainUiHandler(autoCompleteAdapter));
// This will hide the autocomplete box and background frame.
transitionToWaiting();
@ -125,12 +130,18 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
suggestionDropdown.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String query = ((AutoCompleteModel) suggestionDropdown.getItemAtPosition(position))
.getMainText();
String query = (String) suggestionDropdown.getItemAtPosition(position);
startSearch(query);
}
});
// TODO: Don't hard-code this template string (bug 1039758)
final String template = "https://search.yahoo.com/sugg/ff?" +
"output=fxjson&appid=ffm&command=__searchTerms__&nresults=" + SUGGESTION_MAX;
suggestClient = new SuggestClient(getActivity(), template, SUGGESTION_TIMEOUT, SUGGESTION_MAX);
suggestionLoaderCallbacks = new SuggestionLoaderCallbacks();
return mainView;
}
@ -146,15 +157,8 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
suggestionDropdown = null;
}
autoCompleteAdapter = null;
}
/**
* Handler for clicks of individual items.
*/
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// TODO: Right now each row has its own click handler.
// Can we
suggestClient = null;
suggestionLoaderCallbacks = null;
}
/**
@ -169,18 +173,6 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
return false;
}
private void initRows() {
// TODO: Query history for these items.
autoCompleteAdapter.add(new AutoCompleteModel("banana"));
autoCompleteAdapter.add(new AutoCompleteModel("cat pics"));
autoCompleteAdapter.add(new AutoCompleteModel("mexican food"));
autoCompleteAdapter.add(new AutoCompleteModel("cuba libre"));
autoCompleteAdapter.notifyDataSetChanged();
}
/**
* Send a search intent and put the widget into waiting.
*/
@ -226,41 +218,78 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
searchBar.setText(suggestion);
// Move cursor to end of search input.
searchBar.setSelection(suggestion.length());
autoCompleteAgentManager.search(suggestion);
}
/**
* Receives messages from the SuggestionAgent's background thread.
*/
private static class MainUiHandler extends Handler {
final AutoCompleteAdapter autoCompleteAdapter1;
public MainUiHandler(AutoCompleteAdapter autoCompleteAdapter) {
autoCompleteAdapter1 = autoCompleteAdapter;
private class SuggestionLoaderCallbacks implements LoaderManager.LoaderCallbacks<List<String>> {
@Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
// suggestClient is set to null in onDestroyView(), so using it
// safely here relies on the fact that onCreateLoader() is called
// synchronously in restartLoader().
return new SuggestionAsyncLoader(getActivity(), suggestClient, args.getString(KEY_SEARCH_TERM));
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (null == msg.obj) {
return;
public void onLoadFinished(Loader<List<String>> loader, List<String> suggestions) {
autoCompleteAdapter.update(suggestions);
}
@Override
public void onLoaderReset(Loader<List<String>> loader) {
if (autoCompleteAdapter != null) {
autoCompleteAdapter.update(null);
}
}
}
private static class SuggestionAsyncLoader extends AsyncTaskLoader<List<String>> {
private final SuggestClient suggestClient;
private final String searchTerm;
private List<String> suggestions;
public SuggestionAsyncLoader(Context context, SuggestClient suggestClient, String searchTerm) {
super(context);
this.suggestClient = suggestClient;
this.searchTerm = searchTerm;
this.suggestions = null;
}
@Override
public List<String> loadInBackground() {
return suggestClient.query(searchTerm);
}
@Override
public void deliverResult(List<String> suggestions) {
this.suggestions = suggestions;
if (isStarted()) {
super.deliverResult(suggestions);
}
}
@Override
protected void onStartLoading() {
if (suggestions != null) {
deliverResult(suggestions);
}
if (!(msg.obj instanceof Iterable)) {
return;
if (takeContentChanged() || suggestions == null) {
forceLoad();
}
}
autoCompleteAdapter1.clear();
@Override
protected void onStopLoading() {
cancelLoad();
}
for (Object obj : (Iterable) msg.obj) {
if (obj instanceof AutoCompleteModel) {
autoCompleteAdapter1.add((AutoCompleteModel) obj);
}
}
autoCompleteAdapter1.notifyDataSetChanged();
@Override
protected void onReset() {
super.onReset();
onStopLoading();
suggestions = null;
}
}

View File

@ -0,0 +1,145 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.search.autocomplete;
import org.json.JSONArray;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
/**
* Use network-based search suggestions.
*/
public class SuggestClient {
private static final String LOGTAG = "GeckoSuggestClient";
private static final String USER_AGENT = "";
private final Context mContext;
private final int mTimeout;
// should contain the string "__searchTerms__", which is replaced with the query
private final String mSuggestTemplate;
// the maximum number of suggestions to return
private final int mMaxResults;
// used by robocop for testing
private boolean mCheckNetwork;
// used to make suggestions appear instantly after opt-in
private String mPrevQuery;
private ArrayList<String> mPrevResults;
public SuggestClient(Context context, String suggestTemplate, int timeout, int maxResults) {
mContext = context;
mMaxResults = maxResults;
mSuggestTemplate = suggestTemplate;
mTimeout = timeout;
mCheckNetwork = true;
}
/**
* Queries for a given search term and returns an ArrayList of suggestions.
*/
public ArrayList<String> query(String query) {
if (query.equals(mPrevQuery))
return mPrevResults;
ArrayList<String> suggestions = new ArrayList<String>();
if (TextUtils.isEmpty(mSuggestTemplate) || TextUtils.isEmpty(query)) {
return suggestions;
}
if (!isNetworkConnected() && mCheckNetwork) {
Log.i(LOGTAG, "Not connected to network");
return suggestions;
}
try {
String encoded = URLEncoder.encode(query, "UTF-8");
String suggestUri = mSuggestTemplate.replace("__searchTerms__", encoded);
URL url = new URL(suggestUri);
String json = null;
HttpURLConnection urlConnection = null;
InputStream in = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(mTimeout);
urlConnection.setRequestProperty("User-Agent", USER_AGENT);
in = new BufferedInputStream(urlConnection.getInputStream());
json = convertStreamToString(in);
} finally {
if (urlConnection != null)
urlConnection.disconnect();
if (in != null) {
try {
in.close();
} catch (IOException e) {
Log.e(LOGTAG, "error", e);
}
}
}
if (json != null) {
/*
* Sample result:
* ["foo",["food network","foothill college","foot locker",...]]
*/
JSONArray results = new JSONArray(json);
JSONArray jsonSuggestions = results.getJSONArray(1);
int added = 0;
for (int i = 0; (i < jsonSuggestions.length()) && (added < mMaxResults); i++) {
String suggestion = jsonSuggestions.getString(i);
if (!suggestion.equalsIgnoreCase(query)) {
suggestions.add(suggestion);
added++;
}
}
} else {
Log.e(LOGTAG, "Suggestion query failed");
}
} catch (Exception e) {
Log.e(LOGTAG, "Error", e);
}
mPrevQuery = query;
mPrevResults = suggestions;
return suggestions;
}
private boolean isNetworkConnected() {
NetworkInfo networkInfo = getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnected();
}
private NetworkInfo getActiveNetworkInfo() {
ConnectivityManager connectivity = (ConnectivityManager) mContext
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity == null)
return null;
return connectivity.getActiveNetworkInfo();
}
private String convertStreamToString(java.io.InputStream is) {
try {
return new java.util.Scanner(is).useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,11 +8,9 @@ search_activity_sources = [
'java/org/mozilla/search/autocomplete/AcceptsJumpTaps.java',
'java/org/mozilla/search/autocomplete/AcceptsSearchQuery.java',
'java/org/mozilla/search/autocomplete/AutoCompleteAdapter.java',
'java/org/mozilla/search/autocomplete/AutoCompleteAgentManager.java',
'java/org/mozilla/search/autocomplete/AutoCompleteModel.java',
'java/org/mozilla/search/autocomplete/AutoCompleteRowView.java',
'java/org/mozilla/search/autocomplete/AutoCompleteWordListAgent.java',
'java/org/mozilla/search/autocomplete/SearchFragment.java',
'java/org/mozilla/search/autocomplete/SuggestClient.java',
'java/org/mozilla/search/Constants.java',
'java/org/mozilla/search/MainActivity.java',
'java/org/mozilla/search/PostSearchFragment.java',

View File

@ -4062,10 +4062,9 @@ pref("memory.dump_reports_on_oom", false);
// Number of stack frames to capture in createObjectURL for about:memory.
pref("memory.blob_report.stack_frames", 0);
// comma separated list of domain origins (e.g. https://domain.com) for
// providers that can install from their own website without user warnings.
// entries are
pref("social.whitelist", "https://mozsocial.cliqz.com,https://now.msn.com,https://mixi.jp");
// comma separated list of domain origins (e.g. https://domain.com) that still
// need localStorage in the frameworker
pref("social.whitelist", "https://mozsocial.cliqz.com");
// comma separated list of domain origins (e.g. https://domain.com) for
// directory websites (e.g. AMO) that can install providers for other sites
pref("social.directories", "https://activations.cdn.mozilla.net");

View File

@ -19,7 +19,7 @@ const TOPIC_PREFCHANGED = "nsPref:changed";
const DEFAULT_BEHAVIOR = 0;
const PREF_BRANCH = "browser.urlbar";
const PREF_BRANCH = "browser.urlbar.";
// Prefs are defined as [pref name, default value].
const PREF_ENABLED = [ "autocomplete.enabled", true ];
@ -91,7 +91,7 @@ const SQL_BOOKMARK_TAGS_FRAGMENT = sql(
"( SELECT title FROM moz_bookmarks WHERE fk = h.id AND title NOTNULL",
"ORDER BY lastModified DESC LIMIT 1",
") AS btitle,",
"( SELECT GROUP_CONCAT(t.title, ',')",
"( SELECT GROUP_CONCAT(t.title, ', ')",
"FROM moz_bookmarks b",
"JOIN moz_bookmarks t ON t.id = +b.parent AND t.parent = :parent",
"WHERE b.fk = h.id",
@ -187,7 +187,7 @@ const SQL_KEYWORD_QUERY = sql(
const SQL_HOST_QUERY = sql(
"/* do not warn (bug NA): not worth to index on (typed, frecency) */",
"SELECT :query_type, host || '/', prefix || host || '/',",
"SELECT :query_type, host || '/', IFNULL(prefix, '') || host || '/',",
"NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frecency",
"FROM moz_hosts",
"WHERE host BETWEEN :searchString AND :searchString || X'FFFF'",
@ -200,7 +200,7 @@ const SQL_TYPED_HOST_QUERY = SQL_HOST_QUERY.replace("/*CONDITIONS*/",
"AND typed = 1");
const SQL_URL_QUERY = sql(
"/* do not warn (bug no): cannot use an index */",
"SELECT :query_type, h.url,",
"SELECT :query_type, h.url, NULL,",
"NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, h.frecency",
"FROM moz_places h",
"WHERE h.frecency <> 0",
@ -434,41 +434,62 @@ function stripPrefix(spec)
return spec;
}
/**
* Strip http and trailing separators from a spec.
*
* @param spec
* The text to modify.
* @return the modified spec.
*/
function stripHttpAndTrim(spec) {
if (spec.startsWith("http://")) {
spec = spec.slice(7);
}
if (spec.endsWith("?")) {
spec = spec.slice(0, -1);
}
if (spec.endsWith("/")) {
spec = spec.slice(0, -1);
}
return spec;
}
////////////////////////////////////////////////////////////////////////////////
//// Search Class
//// Manages a single instance of an autocomplete search.
function Search(searchString, searchParam, autocompleteListener,
resultListener, autocompleteSearch) {
// We want to store the original string with no leading or trailing
// whitespace for case sensitive searches.
this._originalSearchString = searchString.trim();
this._searchString = fixupSearchText(this._originalSearchString.toLowerCase());
this._searchTokens =
this.filterTokens(getUnfilteredSearchTokens(this._searchString));
// The protocol and the host are lowercased by nsIURI, so it's fine to
// lowercase the typed prefix, to add it back to the results later.
this._strippedPrefix = this._originalSearchString.slice(
0, this._originalSearchString.length - this._searchString.length
).toLowerCase();
// The URIs in the database are fixed-up, so we can match on a lowercased
// host, but the path must be matched in a case sensitive way.
let pathIndex =
this._originalSearchString.indexOf("/", this._strippedPrefix.length);
this._autofillUrlSearchString = fixupSearchText(
this._originalSearchString.slice(0, pathIndex).toLowerCase() +
this._originalSearchString.slice(pathIndex)
);
this._enableActions = searchParam.split(" ").indexOf("enable-actions") != -1;
this._listener = autocompleteListener;
this._autocompleteSearch = autocompleteSearch;
// We want to store the original string for case sensitive searches.
this._originalSearchString = searchString;
this._trimmedOriginalSearchString = searchString.trim();
this._searchString = fixupSearchText(this._trimmedOriginalSearchString.toLowerCase());
this._matchBehavior = Prefs.matchBehavior;
// Set the default behavior for this search.
this._behavior = this._searchString ? Prefs.defaultBehavior
: Prefs.emptySearchDefaultBehavior;
this._enableActions = searchParam.split(" ").indexOf("enable-actions") != -1;
this._searchTokens =
this.filterTokens(getUnfilteredSearchTokens(this._searchString));
// The protocol and the host are lowercased by nsIURI, so it's fine to
// lowercase the typed prefix, to add it back to the results later.
this._strippedPrefix = this._trimmedOriginalSearchString.slice(
0, this._trimmedOriginalSearchString.length - this._searchString.length
).toLowerCase();
// The URIs in the database are fixed-up, so we can match on a lowercased
// host, but the path must be matched in a case sensitive way.
let pathIndex =
this._trimmedOriginalSearchString.indexOf("/", this._strippedPrefix.length);
this._autofillUrlSearchString = fixupSearchText(
this._trimmedOriginalSearchString.slice(0, pathIndex).toLowerCase() +
this._trimmedOriginalSearchString.slice(pathIndex)
);
this._listener = autocompleteListener;
this._autocompleteSearch = autocompleteSearch;
// Create a new result to add eventual matches. Note we need a result
// regardless having matches.
let result = Cc["@mozilla.org/autocomplete/simple-result;1"]
@ -597,10 +618,11 @@ Search.prototype = {
this._switchToTabQuery,
this._searchQuery ];
if (this._searchTokens.length == 1) {
yield this._matchPriorityUrl();
} else if (this._searchTokens.length > 1) {
if (this._searchTokens.length > 0 &&
PlacesUtils.bookmarks.getURIForKeyword(this._searchTokens[0])) {
queries.unshift(this._keywordQuery);
} else if (this._searchTokens.length == 1) {
yield this._matchPriorityUrl();
}
if (this._shouldAutofill) {
@ -638,7 +660,7 @@ Search.prototype = {
this._matchBehavior = MATCH_ANYWHERE;
for (let [query, params] of [ this._adaptiveQuery,
this._searchQuery ]) {
yield conn.executeCached(query, params, this._onResultRow);
yield conn.executeCached(query, params, this._onResultRow.bind(this));
if (!this.pending)
return;
}
@ -714,8 +736,9 @@ Search.prototype = {
}
// Must check both id and url, cause keywords dinamically modify the url.
let urlMapKey = stripHttpAndTrim(match.value);
if ((!match.placeId || !this._usedPlaceIds.has(match.placeId)) &&
!this._usedURLs.has(stripPrefix(match.value))) {
!this._usedURLs.has(urlMapKey)) {
// Add this to our internal tracker to ensure duplicates do not end up in
// the result.
// Not all entries have a place id, thus we fallback to the url for them.
@ -724,7 +747,7 @@ Search.prototype = {
// are faster too.
if (match.placeId)
this._usedPlaceIds.add(match.placeId);
this._usedURLs.add(stripPrefix(match.value));
this._usedURLs.add(urlMapKey);
this._result.appendMatch(match.value,
match.comment,
@ -756,8 +779,7 @@ Search.prototype = {
// If the untrimmed value doesn't preserve the user's input just
// ignore it and complete to the found host.
if (untrimmedHost &&
!untrimmedHost.toLowerCase().contains(this._originalSearchString.toLowerCase())) {
// THIS CAUSES null TO BE SHOWN AS TITLE.
!untrimmedHost.toLowerCase().contains(this._trimmedOriginalSearchString.toLowerCase())) {
untrimmedHost = null;
}
@ -791,8 +813,7 @@ Search.prototype = {
// ignore it and complete to the found url.
let untrimmedURL = prefix + url;
if (untrimmedURL &&
!untrimmedURL.toLowerCase().contains(this._originalSearchString.toLowerCase())) {
// THIS CAUSES null TO BE SHOWN AS TITLE.
!untrimmedURL.toLowerCase().contains(this._trimmedOriginalSearchString.toLowerCase())) {
untrimmedURL = null;
}
@ -870,6 +891,9 @@ Search.prototype = {
}
}
if (action)
match.style = "action " + match.style;
match.value = url;
match.comment = title;
if (iconurl) {
@ -927,7 +951,7 @@ Search.prototype = {
get _keywordQuery() {
// The keyword is the first word in the search string, with the parameters
// following it.
let searchString = this._originalSearchString;
let searchString = this._trimmedOriginalSearchString;
let queryString = "";
let queryIndex = searchString.indexOf(" ");
if (queryIndex != -1) {
@ -999,6 +1023,14 @@ Search.prototype = {
if (Prefs.defaultBehavior != DEFAULT_BEHAVIOR)
return false;
// Don't try to autofill if the search term includes any whitespace.
// This may confuse completeDefaultIndex cause the AUTOCOMPLETE_MATCH
// tokenizer ends up trimming the search string and returning a value
// that doesn't match it, or is even shorter.
if (/\s/.test(this._originalSearchString)) {
return false;
}
// Don't autoFill if the search term is recognized as a keyword, otherwise
// it will override default keywords behavior. Note that keywords are
// hashed on first use, so while the first query may delay a little bit,
@ -1008,14 +1040,6 @@ Search.prototype = {
return false;
}
// Don't try to autofill if the search term includes any whitespace.
// This may confuse completeDefaultIndex cause the AUTOCOMPLETE_MATCH
// tokenizer ends up trimming the search string and returning a value
// that doesn't match it, or is even shorter.
if (/\s/.test(this._searchString)) {
return false;
}
return true;
},
@ -1026,7 +1050,7 @@ Search.prototype = {
* database with and an object containing the params to bound.
*/
get _hostQuery() [
Prefs.autofillTyped ? SQL_TYPED_HOST_QUERY : SQL_TYPED_QUERY,
Prefs.autofillTyped ? SQL_TYPED_HOST_QUERY : SQL_HOST_QUERY,
{
query_type: QUERYTYPE_AUTOFILL_HOST,
searchString: this._searchString.toLowerCase()
@ -1040,7 +1064,7 @@ Search.prototype = {
* database with and an object containing the params to bound.
*/
get _urlQuery() [
Prefs.autofillTyped ? SQL_TYPED_HOST_QUERY : SQL_TYPED_QUERY,
Prefs.autofillTyped ? SQL_TYPED_URL_QUERY : SQL_URL_QUERY,
{
query_type: QUERYTYPE_AUTOFILL_URL,
searchString: this._autofillUrlSearchString,
@ -1117,7 +1141,8 @@ UnifiedComplete.prototype = {
yield SwitchToTabStorage.initDatabase(conn);
return conn;
}.bind(this)).then(null, Cu.reportError);
}.bind(this)).then(null, ex => { dump("Couldn't get database handle: " + ex + "\n");
Cu.reportError(ex); });
}
return this._promiseDatabase;
},
@ -1175,7 +1200,8 @@ UnifiedComplete.prototype = {
if (search == this._currentSearch) {
this.finishSearch(true);
}
}, Cu.reportError);
}, ex => { dump("Query failed: " + ex + "\n");
Cu.reportError(ex); });
},
stopSearch: function () {

View File

@ -15,6 +15,7 @@ XPCSHELL_TESTS_MANIFESTS += [
'migration/xpcshell.ini',
'network/xpcshell.ini',
'queries/xpcshell.ini',
'unifiedcomplete/xpcshell.ini',
'unit/xpcshell.ini',
'xpcshell.ini',
]

View File

@ -0,0 +1,271 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
// Import common head.
let (commonFile = do_get_file("../head_common.js", false)) {
let uri = Services.io.newFileURI(commonFile);
Services.scriptloader.loadSubScript(uri.spec, this);
}
// Put any other stuff relative to this test folder below.
function run_test() {
run_next_test();
}
function* cleanup() {
Services.prefs.clearUserPref("browser.urlbar.autocomplete.enabled");
Services.prefs.clearUserPref("browser.urlbar.autoFill");
Services.prefs.clearUserPref("browser.urlbar.autoFill.typed");
remove_all_bookmarks();
yield promiseClearHistory();
}
do_register_cleanup(cleanup);
/**
* @param aSearches
* Array of AutoCompleteSearch names.
*/
function AutoCompleteInput(aSearches) {
this.searches = aSearches;
}
AutoCompleteInput.prototype = {
popup: {
selectedIndex: -1,
invalidate: function () {},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup])
},
popupOpen: false,
disableAutoComplete: false,
completeDefaultIndex: true,
completeSelectedIndex: true,
forceComplete: false,
minResultsForPopup: 0,
maxRows: 0,
showCommentColumn: false,
showImageColumn: false,
timeout: 10,
searchParam: "",
get searchCount() {
return this.searches.length;
},
getSearchAt: function(aIndex) {
return this.searches[aIndex];
},
textValue: "",
// Text selection range
_selStart: 0,
_selEnd: 0,
get selectionStart() {
return this._selStart;
},
get selectionEnd() {
return this._selEnd;
},
selectTextRange: function(aStart, aEnd) {
this._selStart = aStart;
this._selEnd = aEnd;
},
onSearchBegin: function () {},
onSearchComplete: function () {},
onTextEntered: function() false,
onTextReverted: function() false,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput])
}
function* check_autocomplete(test) {
// At this point frecency could still be updating due to latest pages
// updates.
// This is not a problem in real life, but autocomplete tests should
// return reliable resultsets, thus we have to wait.
yield promiseAsyncUpdates();
// Make an AutoCompleteInput that uses our searches and confirms results.
let input = new AutoCompleteInput(["unifiedcomplete"]);
input.textValue = test.search;
if (test.searchParam)
input.searchParam = test.searchParam;
// Caret must be at the end for autoFill to happen.
let strLen = test.search.length;
input.selectTextRange(strLen, strLen);
Assert.equal(input.selectionStart, strLen, "Selection starts at end");
Assert.equal(input.selectionEnd, strLen, "Selection ends at the end");
let controller = Cc["@mozilla.org/autocomplete/controller;1"]
.getService(Ci.nsIAutoCompleteController);
controller.input = input;
let numSearchesStarted = 0;
input.onSearchBegin = () => {
do_log_info("onSearchBegin received");
numSearchesStarted++;
};
let deferred = Promise.defer();
input.onSearchComplete = () => {
do_log_info("onSearchComplete received");
deferred.resolve();
}
do_log_info("Searching for: '" + test.search + "'");
controller.startSearch(test.search);
yield deferred.promise;
// We should be running only one query.
Assert.equal(numSearchesStarted, 1, "Only one search started");
// Check to see the expected uris and titles match up (in any order)
if (test.matches) {
for (let i = 0; i < controller.matchCount; i++) {
let value = controller.getValueAt(i);
let comment = controller.getCommentAt(i);
do_log_info("Looking for '" + value + "', '" + comment + "' in expected results...");
let j;
for (j = 0; j < test.matches.length; j++) {
// Skip processed expected results
if (test.matches[j] == undefined)
continue;
let { uri, title, tags } = test.matches[j];
if (tags)
title += " \u2013 " + tags.sort().join(", ");
do_log_info("Checking against expected '" + uri.spec + "', '" + title + "'...");
// Got a match on both uri and title?
if (stripPrefix(uri.spec) == stripPrefix(value) && title == comment) {
do_log_info("Got a match at index " + j + "!");
// Make it undefined so we don't process it again
test.matches[j] = undefined;
if (uri.spec.startsWith("moz-action:")) {
let style = controller.getStyleAt(i);
Assert.ok(style.contains("action"));
}
break;
}
}
// We didn't hit the break, so we must have not found it
if (j == test.matches.length)
do_throw("Didn't find the current result ('" + value + "', '" + comment + "') in matches");
}
Assert.equal(controller.matchCount, test.matches.length,
"Got as many results as expected");
// If we expect results, make sure we got matches.
do_check_eq(controller.searchStatus, test.matches.length ?
Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH :
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH);
}
if (test.autofilled) {
// Check the autoFilled result.
Assert.equal(input.textValue, test.autofilled,
"Autofilled value is correct");
// Now force completion and check correct casing of the result.
// This ensures the controller is able to do its magic case-preserving
// stuff and correct replacement of the user's casing with result's one.
controller.handleEnter(false);
Assert.equal(input.textValue, test.completed,
"Completed value is correct");
}
}
function addBookmark(aBookmarkObj) {
Assert.ok(!!aBookmarkObj.uri, "Bookmark object contains an uri");
let parentId = aBookmarkObj.parentId ? aBookmarkObj.parentId
: PlacesUtils.unfiledBookmarksFolderId;
let itemId = PlacesUtils.bookmarks
.insertBookmark(parentId,
aBookmarkObj.uri,
PlacesUtils.bookmarks.DEFAULT_INDEX,
aBookmarkObj.title || "A bookmark");
if (aBookmarkObj.keyword) {
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, aBookmarkObj.keyword);
}
if (aBookmarkObj.tags) {
PlacesUtils.tagging.tagURI(aBookmarkObj.uri, aBookmarkObj.tags);
}
}
function addOpenPages(aUri, aCount=1) {
let ac = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
.getService(Ci.mozIPlacesAutoComplete);
for (let i = 0; i < aCount; i++) {
ac.registerOpenPage(aUri);
}
}
function removeOpenPages(aUri, aCount=1) {
let ac = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
.getService(Ci.mozIPlacesAutoComplete);
for (let i = 0; i < aCount; i++) {
ac.unregisterOpenPage(aUri);
}
}
function changeRestrict(aType, aChar) {
let branch = "browser.urlbar.";
// "title" and "url" are different from everything else, so special case them.
if (aType == "title" || aType == "url")
branch += "match.";
else
branch += "restrict.";
do_log_info("changing restrict for " + aType + " to '" + aChar + "'");
Services.prefs.setCharPref(branch + aType, aChar);
}
function resetRestrict(aType) {
let branch = "browser.urlbar.";
// "title" and "url" are different from everything else, so special case them.
if (aType == "title" || aType == "url")
branch += "match.";
else
branch += "restrict.";
Services.prefs.clearUserPref(branch + aType);
}
/**
* Strip prefixes from the URI that we don't care about for searching.
*
* @param spec
* The text to modify.
* @return the modified spec.
*/
function stripPrefix(spec)
{
["http://", "https://", "ftp://"].some(scheme => {
if (spec.startsWith(scheme)) {
spec = spec.slice(scheme.length);
return true;
}
return false;
});
if (spec.startsWith("www.")) {
spec = spec.slice(4);
}
return spec;
}

View File

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Test bug 416211 to make sure results that match the tag show the bookmark
* title instead of the page title.
*/
add_task(function* test_tag_match_has_bookmark_title() {
do_log_info("Make sure the tag match gives the bookmark title");
let uri = NetUtil.newURI("http://theuri/");
yield promiseAddVisits({ uri: uri, title: "Page title" });
addBookmark({ uri: uri,
title: "Bookmark title",
tags: [ "superTag" ]});
yield check_autocomplete({
search: "superTag",
matches: [ { uri: uri, title: "Bookmark title", tags: [ "superTag" ] } ]
});
yield cleanup();
});

View File

@ -0,0 +1,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Test autocomplete for non-English URLs that match the tag bug 416214. Also
* test bug 417441 by making sure escaped ascii characters like "+" remain
* escaped.
*
* - add a visit for a page with a non-English URL
* - add a tag for the page
* - search for the tag
* - test number of matches (should be exactly one)
* - make sure the url is decoded
*/
add_task(function* test_tag_match_url() {
do_log_info("Make sure tag matches return the right url as well as '+' remain escaped");
let uri1 = NetUtil.newURI("http://escaped/ユニコード");
let uri2 = NetUtil.newURI("http://asciiescaped/blocking-firefox3%2B");
yield promiseAddVisits([ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" } ]);
addBookmark({ uri: uri1,
title: "title",
tags: [ "superTag" ]});
addBookmark({ uri: uri2,
title: "title",
tags: [ "superTag" ]});
yield check_autocomplete({
search: "superTag",
matches: [ { uri: uri1, title: "title", tags: [ "superTag" ] },
{ uri: uri2, title: "title", tags: [ "superTag" ] } ]
});
yield cleanup();
});

View File

@ -0,0 +1,49 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 417798 to make sure javascript: URIs don't show up unless the
* user searches for javascript: explicitly.
*/
add_task(function* test_javascript_match() {
let uri1 = NetUtil.newURI("http://abc/def");
let uri2 = NetUtil.newURI("javascript:5");
yield promiseAddVisits([ { uri: uri1, title: "Title with javascript:" } ]);
addBookmark({ uri: uri2,
title: "Title with javascript:" });
do_log_info("Match non-javascript: with plain search");
yield check_autocomplete({
search: "a",
matches: [ { uri: uri1, title: "Title with javascript:" } ]
});
do_log_info("Match non-javascript: with almost javascript:");
yield check_autocomplete({
search: "javascript",
matches: [ { uri: uri1, title: "Title with javascript:" } ]
});
do_log_info("Match javascript:");
yield check_autocomplete({
search: "javascript:",
matches: [ { uri: uri1, title: "Title with javascript:" },
{ uri: uri2, title: "Title with javascript:" } ]
});
do_log_info("Match nothing with non-first javascript:");
yield check_autocomplete({
search: "5 javascript:",
matches: [ ]
});
do_log_info("Match javascript: with multi-word search");
yield check_autocomplete({
search: "javascript: 5",
matches: [ { uri: uri2, title: "Title with javascript:" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,65 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 418257 by making sure tags are returned with the title as part of
* the "comment" if there are tags even if we didn't match in the tags. They
* are separated from the title by a endash.
*/
add_task(function* test_javascript_match() {
let uri1 = NetUtil.newURI("http://page1");
let uri2 = NetUtil.newURI("http://page2");
let uri3 = NetUtil.newURI("http://page3");
let uri4 = NetUtil.newURI("http://page4");
yield promiseAddVisits([ { uri: uri1, title: "tagged" },
{ uri: uri2, title: "tagged" },
{ uri: uri3, title: "tagged" },
{ uri: uri4, title: "tagged" } ]);
addBookmark({ uri: uri1,
title: "tagged",
tags: [ "tag1" ] });
addBookmark({ uri: uri2,
title: "tagged",
tags: [ "tag1", "tag2" ] });
addBookmark({ uri: uri3,
title: "tagged",
tags: [ "tag1", "tag3" ] });
addBookmark({ uri: uri4,
title: "tagged",
tags: [ "tag1", "tag2", "tag3" ] });
do_log_info("Make sure tags come back in the title when matching tags");
yield check_autocomplete({
search: "page1 tag",
matches: [ { uri: uri1, title: "tagged", tags: [ "tag1" ] } ]
});
do_log_info("Check tags in title for page2");
yield check_autocomplete({
search: "page2 tag",
matches: [ { uri: uri2, title: "tagged", tags: [ "tag1", "tag2" ] } ]
});
do_log_info("Make sure tags appear even when not matching the tag");
yield check_autocomplete({
search: "page3",
matches: [ { uri: uri3, title: "tagged", tags: [ "tag1", "tag3" ] } ]
});
do_log_info("Multiple tags come in commas for page4");
yield check_autocomplete({
search: "page4",
matches: [ { uri: uri4, title: "tagged", tags: [ "tag1", "tag2", "tag3" ] } ]
});
do_log_info("Extra test just to make sure we match the title");
yield check_autocomplete({
search: "tag2",
matches: [ { uri: uri2, title: "tagged", tags: [ "tag1", "tag2" ] },
{ uri: uri4, title: "tagged", tags: [ "tag1", "tag2", "tag3" ] } ]
});
yield cleanup();
});

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 422277 to make sure bad escaped uris don't get escaped. This makes
* sure we don't hit an assertion for "not a UTF8 string".
*/
add_task(function* test_javascript_match() {
do_log_info("Bad escaped uri stays escaped");
let uri1 = NetUtil.newURI("http://site/%EAid");
yield promiseAddVisits([ { uri: uri1, title: "title" } ]);
yield check_autocomplete({
search: "site",
matches: [ { uri: uri1, title: "title" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,147 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Functional tests for inline autocomplete
add_task(function* test_disabling_autocomplete() {
do_log_info("Check disabling autocomplete disables autofill");
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", false);
yield promiseAddVisits({ uri: NetUtil.newURI("http://visit.mozilla.org"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "vis",
autofilled: "vis",
completed: "vis"
});
yield cleanup();
});
add_task(function* test_urls_order() {
do_log_info("Add urls, check for correct order");
let places = [{ uri: NetUtil.newURI("http://visit1.mozilla.org") },
{ uri: NetUtil.newURI("http://visit2.mozilla.org"),
transition: TRANSITION_TYPED }];
yield promiseAddVisits(places);
yield check_autocomplete({
search: "vis",
autofilled: "visit2.mozilla.org/",
completed: "visit2.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_ignore_prefix() {
do_log_info("Add urls, make sure www and http are ignored");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://www.visit1.mozilla.org"));
yield check_autocomplete({
search: "visit1",
autofilled: "visit1.mozilla.org/",
completed: "visit1.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_after_host() {
do_log_info("Autocompleting after an existing host completes to the url");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://www.visit3.mozilla.org"));
yield check_autocomplete({
search: "visit3.mozilla.org/",
autofilled: "visit3.mozilla.org/",
completed: "visit3.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_respect_www() {
do_log_info("Searching for www.me should yield www.me.mozilla.org/");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://www.me.mozilla.org"));
yield check_autocomplete({
search: "www.me",
autofilled: "www.me.mozilla.org/",
completed: "www.me.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_bookmark_first() {
do_log_info("With a bookmark and history, the query result should be the bookmark");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
addBookmark({ uri: NetUtil.newURI("http://bookmark1.mozilla.org/") });
yield promiseAddVisits(NetUtil.newURI("http://bookmark1.mozilla.org/foo"));
yield check_autocomplete({
search: "bookmark",
autofilled: "bookmark1.mozilla.org/",
completed: "bookmark1.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_full_path() {
do_log_info("Check to make sure we get the proper results with full paths");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
let places = [{ uri: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=delicious") },
{ uri: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=smokey") }];
yield promiseAddVisits(places);
yield check_autocomplete({
search: "smokey",
autofilled: "smokey.mozilla.org/",
completed: "smokey.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_complete_to_slash() {
do_log_info("Check to make sure we autocomplete to the following '/'");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
let places = [{ uri: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=delicious") },
{ uri: NetUtil.newURI("http://smokey.mozilla.org/foo/bar/baz?bacon=smokey") }];
yield promiseAddVisits(places);
yield check_autocomplete({
search: "smokey.mozilla.org/fo",
autofilled: "smokey.mozilla.org/foo/",
completed: "http://smokey.mozilla.org/foo/",
});
yield cleanup();
});
add_task(function* test_complete_to_slash_with_www() {
do_log_info("Check to make sure we autocomplete to the following '/'");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
let places = [{ uri: NetUtil.newURI("http://www.smokey.mozilla.org/foo/bar/baz?bacon=delicious") },
{ uri: NetUtil.newURI("http://www.smokey.mozilla.org/foo/bar/baz?bacon=smokey") }];
yield promiseAddVisits(places);
yield check_autocomplete({
search: "smokey.mozilla.org/fo",
autofilled: "smokey.mozilla.org/foo/",
completed: "http://www.smokey.mozilla.org/foo/",
});
yield cleanup();
});
add_task(function* test_complete_querystring() {
do_log_info("Check to make sure we autocomplete after ?");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious"));
yield check_autocomplete({
search: "smokey.mozilla.org/foo?",
autofilled: "smokey.mozilla.org/foo?bacon=delicious",
completed: "http://smokey.mozilla.org/foo?bacon=delicious",
});
yield cleanup();
});
add_task(function* test_complete_fragment() {
do_log_info("Check to make sure we autocomplete after #");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious#bar"));
yield check_autocomplete({
search: "smokey.mozilla.org/foo?bacon=delicious#bar",
autofilled: "smokey.mozilla.org/foo?bacon=delicious#bar",
completed: "http://smokey.mozilla.org/foo?bacon=delicious#bar",
});
yield cleanup();
});

View File

@ -0,0 +1,39 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Need to test that removing a page from autocomplete actually removes a page
* Description From Shawn Wilsher :sdwilsh 2009-02-18 11:29:06 PST
* We don't test the code path of onValueRemoved
* for the autocomplete implementation
* Bug 479089
*/
add_task(function* test_autocomplete_on_value_removed() {
let listener = Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"].
getService(Components.interfaces.nsIAutoCompleteSimpleResultListener);
let testUri = NetUtil.newURI("http://foo.mozilla.com/");
yield promiseAddVisits({
uri: testUri,
referrer: uri("http://mozilla.com/")
});
let query = PlacesUtils.history.getNewQuery();
let options = PlacesUtils.history.getNewQueryOptions();
// look for this uri only
query.uri = testUri;
let root = PlacesUtils.history.executeQuery(query, options).root;
root.containerOpen = true;
Assert.equal(root.childCount, 1);
// call the untested code path
listener.onValueRemoved(null, testUri.spec, true);
// make sure it is GONE from the DB
Assert.equal(root.childCount, 0);
// close the container
root.containerOpen = false;
});

View File

@ -0,0 +1,135 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(function* test_casing_1() {
do_log_info("Searching for cased entry 1");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "MOZ",
autofilled: "MOZilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_casing_2() {
do_log_info("Searching for cased entry 2");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/T",
autofilled: "mozilla.org/T",
completed: "mozilla.org/T"
});
yield cleanup();
});
add_task(function* test_casing_3() {
do_log_info("Searching for cased entry 3");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/T",
autofilled: "mozilla.org/Test/",
completed: "http://mozilla.org/Test/"
});
yield cleanup();
});
add_task(function* test_casing_4() {
do_log_info("Searching for cased entry 4");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mOzilla.org/t",
autofilled: "mOzilla.org/t",
completed: "mOzilla.org/t"
});
yield cleanup();
});
add_task(function* test_casing_5() {
do_log_info("Searching for cased entry 5");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mOzilla.org/T",
autofilled: "mOzilla.org/Test/",
completed: "http://mozilla.org/Test/"
});
yield cleanup();
});
add_task(function* test_untrimmed_casing() {
do_log_info("Searching for untrimmed cased entry");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "http://mOz",
autofilled: "http://mOzilla.org/",
completed: "http://mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untrimmed_www_casing() {
do_log_info("Searching for untrimmed cased entry with www");
yield promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "http://www.mOz",
autofilled: "http://www.mOzilla.org/",
completed: "http://www.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untrimmed_path_casing() {
do_log_info("Searching for untrimmed cased entry with path");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "http://mOzilla.org/t",
autofilled: "http://mOzilla.org/t",
completed: "http://mOzilla.org/t"
});
yield cleanup();
});
add_task(function* test_untrimmed_path_casing_2() {
do_log_info("Searching for untrimmed cased entry with path 2");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "http://mOzilla.org/T",
autofilled: "http://mOzilla.org/Test/",
completed: "http://mozilla.org/Test/"
});
yield cleanup();
});
add_task(function* test_untrimmed_path_www_casing() {
do_log_info("Searching for untrimmed cased entry with www and path");
yield promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "http://www.mOzilla.org/t",
autofilled: "http://www.mOzilla.org/t",
completed: "http://www.mOzilla.org/t"
});
yield cleanup();
});
add_task(function* test_untrimmed_path_www_casing_2() {
do_log_info("Searching for untrimmed cased entry with www and path 2");
yield promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/Test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "http://www.mOzilla.org/T",
autofilled: "http://www.mOzilla.org/Test/",
completed: "http://www.mozilla.org/Test/"
});
yield cleanup();
});

View File

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Inline should never return matches shorter than the search string, since
// that largely confuses completeDefaultIndex
add_task(function* test_not_autofill_ws_1() {
do_log_info("Do not autofill whitespaced entry 1");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org ",
autofilled: "mozilla.org ",
completed: "mozilla.org "
});
yield cleanup();
});
add_task(function* test_not_autofill_ws_2() {
do_log_info("Do not autofill whitespaced entry 2");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/ ",
autofilled: "mozilla.org/ ",
completed: "mozilla.org/ "
});
yield cleanup();
});
add_task(function* test_not_autofill_ws_3() {
do_log_info("Do not autofill whitespaced entry 3");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/link ",
autofilled: "mozilla.org/link ",
completed: "mozilla.org/link "
});
yield cleanup();
});
add_task(function* test_not_autofill_ws_4() {
do_log_info("Do not autofill whitespaced entry 4");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/link/ ",
autofilled: "mozilla.org/link/ ",
completed: "mozilla.org/link/ "
});
yield cleanup();
});
add_task(function* test_not_autofill_ws_5() {
do_log_info("Do not autofill whitespaced entry 5");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "moz illa ",
autofilled: "moz illa ",
completed: "moz illa "
});
yield cleanup();
});
add_task(function* test_not_autofill_ws_6() {
do_log_info("Do not autofill whitespaced entry 6");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: " mozilla",
autofilled: " mozilla",
completed: " mozilla"
});
yield cleanup();
});

View File

@ -0,0 +1,75 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim:set ts=2 sw=2 sts=2 et:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Tests bug 449406 to ensure that TRANSITION_DOWNLOAD, TRANSITION_EMBED and
* TRANSITION_FRAMED_LINK bookmarked uri's show up in the location bar.
*/
add_task(function* test_download_embed_bookmarks() {
let uri1 = NetUtil.newURI("http://download/bookmarked");
let uri2 = NetUtil.newURI("http://embed/bookmarked");
let uri3 = NetUtil.newURI("http://framed/bookmarked");
let uri4 = NetUtil.newURI("http://download");
let uri5 = NetUtil.newURI("http://embed");
let uri6 = NetUtil.newURI("http://framed");
yield promiseAddVisits([ { uri: uri1, title: "download-bookmark",
transition: TRANSITION_DOWNLOAD },
{ uri: uri2, title: "embed-bookmark",
transition: TRANSITION_EMBED },
{ uri: uri3, title: "framed-bookmark",
transition: TRANSITION_FRAMED_LINK},
{ uri: uri4, title: "download2",
transition: TRANSITION_DOWNLOAD },
{ uri: uri5, title: "embed2",
transition: TRANSITION_EMBED },
{ uri: uri6, title: "framed2",
transition: TRANSITION_FRAMED_LINK } ]);
addBookmark({ uri: uri1,
title: "download-bookmark" });
addBookmark({ uri: uri2,
title: "embed-bookmark" });
addBookmark({ uri: uri3,
title: "framed-bookmark" });
do_log_info("Searching for bookmarked download uri matches");
yield check_autocomplete({
search: "download-bookmark",
matches: [ { uri: uri1, title: "download-bookmark" } ]
});
do_log_info("Searching for bookmarked embed uri matches");
yield check_autocomplete({
search: "embed-bookmark",
matches: [ { uri: uri2, title: "embed-bookmark" } ]
});
do_log_info("Searching for bookmarked framed uri matches");
yield check_autocomplete({
search: "framed-bookmark",
matches: [ { uri: uri3, title: "framed-bookmark" } ]
});
do_log_info("Searching for download uri does not match");
yield check_autocomplete({
search: "download2",
matches: [ ]
});
do_log_info("Searching for embed uri does not match");
yield check_autocomplete({
search: "embed2",
matches: [ ]
});
do_log_info("Searching for framed uri does not match");
yield check_autocomplete({
search: "framed2",
matches: [ ]
});
yield cleanup();
});

View File

@ -0,0 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Ensure inline autocomplete doesn't return zero frecency pages.
add_task(function* test_dupe_urls() {
do_log_info("Searching for urls with dupes should only show one");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.org/?") });
yield check_autocomplete({
search: "moz",
autofilled: "mozilla.org/",
completed: "mozilla.org/",
matches: [ { uri: NetUtil.newURI("http://mozilla.org/"), title: "mozilla.org/" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,94 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 426864 that makes sure the empty search (drop down list) only
* shows typed pages from history.
*/
add_task(function* test_javascript_match() {
let uri1 = NetUtil.newURI("http://t.foo/0");
let uri2 = NetUtil.newURI("http://t.foo/1");
let uri3 = NetUtil.newURI("http://t.foo/2");
let uri4 = NetUtil.newURI("http://t.foo/3");
let uri5 = NetUtil.newURI("http://t.foo/4");
let uri6 = NetUtil.newURI("http://t.foo/5");
yield promiseAddVisits([ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" },
{ uri: uri3, title: "title",
transition: TRANSITION_TYPED},
{ uri: uri4, title: "title",
transition: TRANSITION_TYPED },
{ uri: uri6, title: "title",
transition: TRANSITION_TYPED } ]);
addBookmark({ uri: uri2,
title: "title" });
addBookmark({ uri: uri4,
title: "title" });
addBookmark({ uri: uri5,
title: "title" });
addBookmark({ uri: uri6,
title: "title" });
// Now remove page 6 from history, so it is an unvisited, typed bookmark.
PlacesUtils.history.removePage(uri6);
do_log_info("Match everything");
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "title" },
{ uri: uri5, title: "title" },
{ uri: uri6, title: "title" } ]
});
do_log_info("Match only typed history");
yield check_autocomplete({
search: "foo ^ ~",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "title" } ]
});
do_log_info("Drop-down empty search matches only typed history");
yield check_autocomplete({
search: "",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "title" } ]
});
do_log_info("Drop-down empty search matches everything");
Services.prefs.setIntPref("browser.urlbar.default.behavior.emptyRestriction", 0);
yield check_autocomplete({
search: "",
matches: [ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "title" },
{ uri: uri5, title: "title" },
{ uri: uri6, title: "title" } ]
});
do_log_info("Drop-down empty search matches only typed");
Services.prefs.setIntPref("browser.urlbar.default.behavior.emptyRestriction", 32);
yield check_autocomplete({
search: "",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "title" },
{ uri: uri6, title: "title" } ]
});
do_log_info("Drop-down empty search matches only typed history");
Services.prefs.clearUserPref("browser.urlbar.default.behavior.emptyRestriction");
yield check_autocomplete({
search: "",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "title" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,35 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 471903 to make sure searching in autocomplete can be turned on
* and off. Also test bug 463535 for pref changing search.
*/
add_task(function* test_enabled() {
let uri = NetUtil.newURI("http://url/0");
yield promiseAddVisits([ { uri: uri, title: "title" } ]);
do_log_info("plain search");
yield check_autocomplete({
search: "url",
matches: [ { uri: uri, title: "title" } ]
});
do_log_info("search disabled");
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", false);
yield check_autocomplete({
search: "url",
matches: [ ]
});
do_log_info("resume normal search");
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", true);
yield check_autocomplete({
search: "url",
matches: [ { uri: uri, title: "title" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 422698 to make sure searches with urls from the location bar
* correctly match itself when it contains escaped characters.
*/
add_task(function* test_escape() {
let uri1 = NetUtil.newURI("http://unescapeduri/");
let uri2 = NetUtil.newURI("http://escapeduri/%40/");
yield promiseAddVisits([ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" } ]);
do_log_info("Unescaped location matches itself");
yield check_autocomplete({
search: "http://unescapeduri/",
matches: [ { uri: uri1, title: "title" } ]
});
do_log_info("Escaped location matches itself");
yield check_autocomplete({
search: "http://escapeduri/%40/",
matches: [ { uri: uri2, title: "title" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 424509 to make sure searching for "h" doesn't match "http" of urls.
*/
add_task(function* test_escape() {
let uri1 = NetUtil.newURI("http://site/");
let uri2 = NetUtil.newURI("http://happytimes/");
yield promiseAddVisits([ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" } ]);
do_log_info("Searching for h matches site and not http://");
yield check_autocomplete({
search: "h",
matches: [ { uri: uri2, title: "title" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,77 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 392143 that puts keyword results into the autocomplete. Makes
* sure that multiple parameter queries get spaces converted to +, + converted
* to %2B, non-ascii become escaped, and pages in history that match the
* keyword uses the page's title.
*
* Also test for bug 249468 by making sure multiple keyword bookmarks with the
* same keyword appear in the list.
*/
add_task(function* test_keyword_searc() {
let uri1 = NetUtil.newURI("http://abc/?search=%s");
let uri2 = NetUtil.newURI("http://abc/?search=ThisPageIsInHistory");
yield promiseAddVisits([ { uri: uri1, title: "Generic page title" },
{ uri: uri2, title: "Generic page title" } ]);
addBookmark({ uri: uri1, title: "Keyword title", keyword: "key"});
do_log_info("Plain keyword query");
yield check_autocomplete({
search: "key term",
matches: [ { uri: NetUtil.newURI("http://abc/?search=term"), title: "Keyword title" } ]
});
do_log_info("Multi-word keyword query");
yield check_autocomplete({
search: "key multi word",
matches: [ { uri: NetUtil.newURI("http://abc/?search=multi+word"), title: "Keyword title" } ]
});
do_log_info("Keyword query with +");
yield check_autocomplete({
search: "key blocking+",
matches: [ { uri: NetUtil.newURI("http://abc/?search=blocking%2B"), title: "Keyword title" } ]
});
do_log_info("Unescaped term in query");
yield check_autocomplete({
search: "key ユニコード",
matches: [ { uri: NetUtil.newURI("http://abc/?search=ユニコード"), title: "Keyword title" } ]
});
do_log_info("Keyword that happens to match a page");
yield check_autocomplete({
search: "key ThisPageIsInHistory",
matches: [ { uri: NetUtil.newURI("http://abc/?search=ThisPageIsInHistory"), title: "Generic page title" } ]
});
do_log_info("Keyword without query (without space)");
yield check_autocomplete({
search: "key",
matches: [ { uri: NetUtil.newURI("http://abc/?search="), title: "Keyword title" } ]
});
do_log_info("Keyword without query (with space)");
yield check_autocomplete({
search: "key ",
matches: [ { uri: NetUtil.newURI("http://abc/?search="), title: "Keyword title" } ]
});
// This adds a second keyword so anything after this will match 2 keywords
let uri3 = NetUtil.newURI("http://xyz/?foo=%s");
yield promiseAddVisits([ { uri: uri3, title: "Generic page title" } ]);
addBookmark({ uri: uri3, title: "Keyword title", keyword: "key"});
do_log_info("Two keywords matched");
yield check_autocomplete({
search: "key twoKey",
matches: [ { uri: NetUtil.newURI("http://abc/?search=twoKey"), title: "Keyword title" },
{ uri: NetUtil.newURI("http://xyz/?foo=twoKey"), title: "Keyword title" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,68 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(function* test_non_keyword() {
do_log_info("Searching for non-keyworded entry should autoFill it");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
addBookmark({ uri: NetUtil.newURI("http://mozilla.org/test/") });
yield check_autocomplete({
search: "moz",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_keyword() {
do_log_info("Searching for keyworded entry should not autoFill it");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
addBookmark({ uri: NetUtil.newURI("http://mozilla.org/test/"), keyword: "moz" });
yield check_autocomplete({
search: "moz",
autofilled: "moz",
completed: "moz",
});
yield cleanup();
});
add_task(function* test_more_than_keyword() {
do_log_info("Searching for more than keyworded entry should autoFill it");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
addBookmark({ uri: NetUtil.newURI("http://mozilla.org/test/"), keyword: "moz" });
yield check_autocomplete({
search: "mozi",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_less_than_keyword() {
do_log_info("Searching for less than keyworded entry should autoFill it");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
addBookmark({ uri: NetUtil.newURI("http://mozilla.org/test/"), keyword: "moz" });
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "mozilla.org/",
});
yield cleanup();
});
add_task(function* test_keyword_casing() {
do_log_info("Searching for keyworded entry is case-insensitive");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED });
addBookmark({ uri: NetUtil.newURI("http://mozilla.org/test/"), keyword: "moz" });
yield check_autocomplete({
search: "MoZ",
autofilled: "MoZ",
completed: "MoZ"
});
yield cleanup();
});

View File

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 451760 which allows matching only at the beginning of urls or
* titles to simulate Firefox 2 functionality.
*/
add_task(function* test_match_beginning() {
let uri1 = NetUtil.newURI("http://x.com/y");
let uri2 = NetUtil.newURI("https://y.com/x");
yield promiseAddVisits([ { uri: uri1, title: "a b" },
{ uri: uri2, title: "b a" } ]);
do_log_info("Match at the beginning of titles");
Services.prefs.setIntPref("browser.urlbar.matchBehavior", 3);
yield check_autocomplete({
search: "a",
matches: [ { uri: uri1, title: "a b" } ]
});
do_log_info("Match at the beginning of titles");
yield check_autocomplete({
search: "b",
matches: [ { uri: uri2, title: "b a" } ]
});
do_log_info("Match at the beginning of urls");
yield check_autocomplete({
search: "x",
matches: [ { uri: uri1, title: "a b" } ]
});
do_log_info("Match at the beginning of urls");
yield check_autocomplete({
search: "y",
matches: [ { uri: uri2, title: "b a" } ]
});
do_log_info("Sanity check that matching anywhere finds more");
Services.prefs.setIntPref("browser.urlbar.matchBehavior", 1);
yield check_autocomplete({
search: "a",
matches: [ { uri: uri1, title: "a b" },
{ uri: uri2, title: "b a" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,66 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 401869 to allow multiple words separated by spaces to match in
* the page title, page url, or bookmark title to be considered a match. All
* terms must match but not all terms need to be in the title, etc.
*
* Test bug 424216 by making sure bookmark titles are always shown if one is
* available. Also bug 425056 makes sure matches aren't found partially in the
* page title and partially in the bookmark.
*/
add_task(function* test_match_beginning() {
let uri1 = NetUtil.newURI("http://a.b.c/d-e_f/h/t/p");
let uri2 = NetUtil.newURI("http://d.e.f/g-h_i/h/t/p");
let uri3 = NetUtil.newURI("http://g.h.i/j-k_l/h/t/p");
let uri4 = NetUtil.newURI("http://j.k.l/m-n_o/h/t/p");
yield promiseAddVisits([ { uri: uri1, title: "f(o)o b<a>r" },
{ uri: uri2, title: "b(a)r b<a>z" },
{ uri: uri3, title: "f(o)o b<a>r" },
{ uri: uri4, title: "f(o)o b<a>r" } ]);
addBookmark({ uri: uri3, title: "f(o)o b<a>r" });
addBookmark({ uri: uri4, title: "b(a)r b<a>z" });
do_log_info("Match 2 terms all in url");
yield check_autocomplete({
search: "c d",
matches: [ { uri: uri1, title: "f(o)o b<a>r" } ]
});
do_log_info("Match 1 term in url and 1 term in title");
yield check_autocomplete({
search: "b e",
matches: [ { uri: uri1, title: "f(o)o b<a>r" },
{ uri: uri2, title: "b(a)r b<a>z" } ]
});
do_log_info("Match 3 terms all in title; display bookmark title if matched");
yield check_autocomplete({
search: "b a z",
matches: [ { uri: uri2, title: "b(a)r b<a>z" },
{ uri: uri4, title: "b(a)r b<a>z" } ]
});
do_log_info("Match 2 terms in url and 1 in title; make sure bookmark title is used for search");
yield check_autocomplete({
search: "k f t",
matches: [ { uri: uri3, title: "f(o)o b<a>r" } ]
});
do_log_info("Match 3 terms in url and 1 in title");
yield check_autocomplete({
search: "d i g z",
matches: [ { uri: uri2, title: "b(a)r b<a>z" } ]
});
do_log_info("Match nothing");
yield check_autocomplete({
search: "m o z i",
matches: [ ]
});
yield cleanup();
});

View File

@ -0,0 +1,57 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(function* test_no_slash() {
do_log_info("Searching for host match without slash should match host");
yield promiseAddVisits({ uri: NetUtil.newURI("http://file.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("file:///c:/test.html"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "file",
autofilled: "file.org/",
completed: "file.org/"
});
yield cleanup();
});
add_task(function* test_w_slash() {
do_log_info("Searching match with slash at the end should do nothing");
yield promiseAddVisits({ uri: NetUtil.newURI("http://file.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("file:///c:/test.html"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "file.org/",
autofilled: "file.org/",
completed: "file.org/"
});
yield cleanup();
});
add_task(function* test_middle() {
do_log_info("Searching match with slash in the middle should match url");
yield promiseAddVisits({ uri: NetUtil.newURI("http://file.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("file:///c:/test.html"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "file.org/t",
autofilled: "file.org/test/",
completed: "http://file.org/test/"
});
yield cleanup();
});
add_task(function* test_nonhost() {
do_log_info("Searching for non-host match without slash should not match url");
yield promiseAddVisits({ uri: NetUtil.newURI("file:///c:/test.html"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "file",
autofilled: "file",
completed: "file"
});
yield cleanup();
});

View File

@ -0,0 +1,419 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test for bug 395161 that allows special searches that restrict results to
* history/bookmark/tagged items and title/url matches.
*
* Test 485122 by making sure results don't have tags when restricting result
* to just history either by default behavior or dynamic query restrict.
*/
add_task(function* test_special_searches() {
let uri1 = NetUtil.newURI("http://url/");
let uri2 = NetUtil.newURI("http://url/2");
let uri3 = NetUtil.newURI("http://foo.bar/");
let uri4 = NetUtil.newURI("http://foo.bar/2");
let uri5 = NetUtil.newURI("http://url/star");
let uri6 = NetUtil.newURI("http://url/star/2");
let uri7 = NetUtil.newURI("http://foo.bar/star");
let uri8 = NetUtil.newURI("http://foo.bar/star/2");
let uri9 = NetUtil.newURI("http://url/tag");
let uri10 = NetUtil.newURI("http://url/tag/2");
let uri11 = NetUtil.newURI("http://foo.bar/tag");
let uri12 = NetUtil.newURI("http://foo.bar/tag/2");
yield promiseAddVisits([ { uri: uri1, title: "title", transition: TRANSITION_TYPED },
{ uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar", transition: TRANSITION_TYPED },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title", transition: TRANSITION_TYPED } ]);
addBookmark( { uri: uri5, title: "title" } );
addBookmark( { uri: uri6, title: "foo.bar" } );
addBookmark( { uri: uri7, title: "title" } );
addBookmark( { uri: uri8, title: "foo.bar" } );
addBookmark( { uri: uri9, title: "title", tags: [ "foo.bar" ] } );
addBookmark( { uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] } );
addBookmark( { uri: uri11, title: "title", tags: [ "foo.bar" ] } );
addBookmark( { uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } );
// Test restricting searches
do_log_info("History restrict");
yield check_autocomplete({
search: "^",
matches: [ { uri: uri1, title: "title" },
{ uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("Star restrict");
yield check_autocomplete({
search: "*",
matches: [ { uri: uri5, title: "title" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar"] },
{ uri: uri12, title: "foo.bar", tags: ["foo.bar"] } ]
});
do_log_info("Tag restrict");
yield check_autocomplete({
search: "+",
matches: [ { uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
// Test specials as any word position
do_log_info("Special as first word");
yield check_autocomplete({
search: "^ foo bar",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("Special as middle word");
yield check_autocomplete({
search: "foo ^ bar",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("Special as last word");
yield check_autocomplete({
search: "foo bar ^",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
// Test restricting and matching searches with a term
do_log_info("foo ^ -> history");
yield check_autocomplete({
search: "foo ^",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("foo | -> history (change pref)");
changeRestrict("history", "|");
yield check_autocomplete({
search: "foo |",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("foo * -> is star");
resetRestrict("history");
yield check_autocomplete({
search: "foo *",
matches: [ { uri: uri6, title: "foo.bar" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo | -> is star (change pref)");
changeRestrict("bookmark", "|");
yield check_autocomplete({
search: "foo |",
matches: [ { uri: uri6, title: "foo.bar" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo # -> in title");
resetRestrict("bookmark");
yield check_autocomplete({
search: "foo #",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo | -> in title (change pref)");
changeRestrict("title", "|");
yield check_autocomplete({
search: "foo |",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo @ -> in url");
resetRestrict("title");
yield check_autocomplete({
search: "foo @",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo | -> in url (change pref)");
changeRestrict("url", "|");
yield check_autocomplete({
search: "foo |",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo + -> is tag");
resetRestrict("url");
yield check_autocomplete({
search: "foo +",
matches: [ { uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo | -> is tag (change pref)");
changeRestrict("tag", "|");
yield check_autocomplete({
search: "foo |",
matches: [ { uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo ~ -> is typed");
resetRestrict("tag");
yield check_autocomplete({
search: "foo ~",
matches: [ { uri: uri4, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo | -> is typed (change pref)");
changeRestrict("typed", "|");
yield check_autocomplete({
search: "foo |",
matches: [ { uri: uri4, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
// Test various pairs of special searches
do_log_info("foo ^ * -> history, is star");
resetRestrict("typed");
yield check_autocomplete({
search: "foo ^ *",
matches: [ { uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo ^ # -> history, in title");
yield check_autocomplete({
search: "foo ^ #",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("foo ^ @ -> history, in url");
yield check_autocomplete({
search: "foo ^ @",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("foo ^ + -> history, is tag");
yield check_autocomplete({
search: "foo ^ +",
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo ^ ~ -> history, is typed");
yield check_autocomplete({
search: "foo ^ ~",
matches: [ { uri: uri4, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("foo * # -> is star, in title");
yield check_autocomplete({
search: "foo * #",
matches: [ { uri: uri6, title: "foo.bar" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo * @ -> is star, in url");
yield check_autocomplete({
search: "foo * @",
matches: [ { uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo * + -> same as +");
yield check_autocomplete({
search: "foo * +",
matches: [ { uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo * ~ -> is star, is typed");
yield check_autocomplete({
search: "foo * ~",
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo # @ -> in title, in url");
yield check_autocomplete({
search: "foo # @",
matches: [ { uri: uri4, title: "foo.bar" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo # + -> in title, is tag");
yield check_autocomplete({
search: "foo # +",
matches: [ { uri: uri9, title: "title", tags: [ "foo.bar" ] },
{ uri: uri10, title: "foo.bar", tags: [ "foo.bar" ] },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo # ~ -> in title, is typed");
yield check_autocomplete({
search: "foo # ~",
matches: [ { uri: uri4, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo @ + -> in url, is tag");
yield check_autocomplete({
search: "foo @ +",
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo @ ~ -> in url, is typed");
yield check_autocomplete({
search: "foo @ ~",
matches: [ { uri: uri4, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo + ~ -> is tag, is typed");
yield check_autocomplete({
search: "foo + ~",
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
// Test default usage by setting certain bits of default.behavior to 1
do_log_info("foo -> default history");
Services.prefs.setIntPref("browser.urlbar.default.behavior", 1);
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri2, title: "foo.bar" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title" } ]
});
do_log_info("foo -> default history, is star");
Services.prefs.setIntPref("browser.urlbar.default.behavior", 3);
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri6, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo -> default history, is star, is typed");
Services.prefs.setIntPref("browser.urlbar.default.behavior", 35);
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
do_log_info("foo -> default history, is star, in url");
Services.prefs.setIntPref("browser.urlbar.default.behavior", 19);
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
});
// Change the default to be less restrictive to make sure we find more
do_log_info("foo -> default is star, in url");
Services.prefs.setIntPref("browser.urlbar.default.behavior", 18);
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
do_log_info("foo -> default in url");
Services.prefs.setIntPref("browser.urlbar.default.behavior", 16);
yield check_autocomplete({
search: "foo",
matches: [ { uri: uri3, title: "title" },
{ uri: uri4, title: "foo.bar" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "foo.bar" },
{ uri: uri11, title: "title", tags: [ "foo.bar" ] },
{ uri: uri12, title: "foo.bar", tags: [ "foo.bar" ] } ]
});
yield cleanup();
});

View File

@ -0,0 +1,149 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 424717 to make sure searching with an existing location like
* http://site/ also matches https://site/ or ftp://site/. Same thing for
* ftp://site/ and https://site/.
*
* Test bug 461483 to make sure a search for "w" doesn't match the "www." from
* site subdomains.
*/
add_task(function* test_swap_protocol() {
let uri1 = NetUtil.newURI("http://www.site/");
let uri2 = NetUtil.newURI("http://site/");
let uri3 = NetUtil.newURI("ftp://ftp.site/");
let uri4 = NetUtil.newURI("ftp://site/");
let uri5 = NetUtil.newURI("https://www.site/");
let uri6 = NetUtil.newURI("https://site/");
let uri7 = NetUtil.newURI("http://woohoo/");
let uri8 = NetUtil.newURI("http://wwwwwwacko/");
yield promiseAddVisits([ { uri: uri1, title: "title" },
{ uri: uri2, title: "title" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "title" },
{ uri: uri5, title: "title" },
{ uri: uri6, title: "title" },
{ uri: uri7, title: "title" },
{ uri: uri8, title: "title" } ]);
let allMatches = [
{ uri: uri1, title: "title" },
{ uri: uri2, title: "title" },
{ uri: uri3, title: "title" },
{ uri: uri4, title: "title" },
{ uri: uri5, title: "title" },
{ uri: uri6, title: "title" }
];
Services.prefs.setBoolPref("browser.urlbar.autoFill", "false");
do_log_info("http://www.site matches all site");
yield check_autocomplete({
search: "http://www.site",
matches: allMatches
});
/*
do_log_info("http://site matches all site");
yield check_autocomplete({
search: "http://site",
matches: allMatches
});
do_log_info("ftp://ftp.site matches itself");
yield check_autocomplete({
search: "ftp://ftp.site",
matches: { uri: uri3, title: "title"}
});
do_log_info("ftp://site matches all site");
yield check_autocomplete({
search: "ftp://site",
matches: allMatches
});
do_log_info("https://www.site matches all site");
yield check_autocomplete({
search: "https://www.site",
matches: allMatches
});
do_log_info("https://site matches all site");
yield check_autocomplete({
search: "https://site",
matches: allMatches
});
do_log_info("www.site matches all site");
yield check_autocomplete({
search: "www.site",
matches: allMatches
});
do_log_info("w matches none of www.");
yield check_autocomplete({
search: "w",
matches: [ { uri: uri7, title: "title" },
{ uri: uri8, title: "title" } ]
});
do_log_info("http://w matches none of www.");
yield check_autocomplete({
search: "http://w",
matches: [ { uri: uri7, title: "title" },
{ uri: uri8, title: "title" } ]
});
do_log_info("http://w matches none of www.");
yield check_autocomplete({
search: "http://www.w",
matches: [ { uri: uri7, title: "title" },
{ uri: uri8, title: "title" } ]
});
do_log_info("ww matches none of www.");
yield check_autocomplete({
search: "ww",
matches: [ { uri: uri8, title: "title" } ]
});
do_log_info("ww matches none of www.");
yield check_autocomplete({
search: "ww",
matches: [ { uri: uri8, title: "title" } ]
});
do_log_info("http://ww matches none of www.");
yield check_autocomplete({
search: "http://ww",
matches: [ { uri: uri8, title: "title" } ]
});
do_log_info("http://www.ww matches none of www.");
yield check_autocomplete({
search: "http://www.ww",
matches: [ { uri: uri8, title: "title" } ]
});
do_log_info("www matches none of www.");
yield check_autocomplete({
search: "www",
matches: [ { uri: uri8, title: "title" } ]
});
do_log_info("http://www matches none of www.");
yield check_autocomplete({
search: "http://www",
matches: [ { uri: uri8, title: "title" } ]
});
do_log_info("http://www.www matches none of www.");
yield check_autocomplete({
search: "http://www.www",
matches: [ { uri: uri8, title: "title" } ]
});
*/
yield cleanup();
});

View File

@ -0,0 +1,96 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* vim:set ts=2 sw=2 sts=2 et:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let gTabRestrictChar = "%";
add_task(function* test_tab_matches() {
let uri1 = NetUtil.newURI("http://abc.com/");
let uri2 = NetUtil.newURI("http://xyz.net/");
let uri3 = NetUtil.newURI("about:mozilla");
let uri4 = NetUtil.newURI("data:text/html,test");
yield promiseAddVisits([ { uri: uri1, title: "ABC rocks" },
{ uri: uri2, title: "xyz.net - we're better than ABC" } ]);
addOpenPages(uri1, 1);
// Pages that cannot be registered in history.
addOpenPages(uri3, 1);
addOpenPages(uri4, 1);
do_log_info("single result, that is also a tab match");
yield check_autocomplete({
search: "abc.com",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" } ]
});
do_log_info("two results, one tab match");
yield check_autocomplete({
search: "abc",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" },
{ uri: uri2, title: "xyz.net - we're better than ABC" } ]
});
do_log_info("two results, both tab matches");
addOpenPages(uri2, 1);
yield check_autocomplete({
search: "abc",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" },
{ uri: NetUtil.newURI("moz-action:switchtab,http://xyz.net/"), title: "xyz.net - we're better than ABC" } ]
});
do_log_info("two results, both tab matches, one has multiple tabs");
addOpenPages(uri2, 5);
yield check_autocomplete({
search: "abc",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" },
{ uri: NetUtil.newURI("moz-action:switchtab,http://xyz.net/"), title: "xyz.net - we're better than ABC" } ]
});
do_log_info("two results, no tab matches");
removeOpenPages(uri1, 1);
removeOpenPages(uri2, 6);
yield check_autocomplete({
search: "abc",
searchParam: "enable-actions",
matches: [ { uri: uri1, title: "ABC rocks" },
{ uri: uri2, title: "xyz.net - we're better than ABC" } ]
});
do_log_info("tab match search with restriction character");
addOpenPages(uri1, 1);
yield check_autocomplete({
search: gTabRestrictChar + " abc",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" } ]
});
do_log_info("tab match with not-addable pages");
yield check_autocomplete({
search: "mozilla",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,about:mozilla"), title: "about:mozilla" } ]
});
do_log_info("tab match with not-addable pages and restriction character");
yield check_autocomplete({
search: gTabRestrictChar + " mozilla",
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,about:mozilla"), title: "about:mozilla" } ]
});
do_log_info("tab match with not-addable pages and only restriction character");
yield check_autocomplete({
search: gTabRestrictChar,
searchParam: "enable-actions",
matches: [ { uri: NetUtil.newURI("moz-action:switchtab,http://abc.com/"), title: "ABC rocks" },
{ uri: NetUtil.newURI("moz-action:switchtab,about:mozilla"), title: "about:mozilla" },
{ uri: NetUtil.newURI("moz-action:switchtab,data:text/html,test"), title: "data:text/html,test" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,298 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
add_task(function* test_untrimmed_secure_www() {
do_log_info("Searching for untrimmed https://www entry");
yield promiseAddVisits({ uri: NetUtil.newURI("https://www.mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "https://www.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untrimmed_secure_www_path() {
do_log_info("Searching for untrimmed https://www entry with path");
yield promiseAddVisits({ uri: NetUtil.newURI("https://www.mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/t",
autofilled: "mozilla.org/test/",
completed: "https://www.mozilla.org/test/"
});
yield cleanup();
});
add_task(function* test_untrimmed_secure() {
do_log_info("Searching for untrimmed https:// entry");
yield promiseAddVisits({ uri: NetUtil.newURI("https://mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "https://mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untrimmed_secure_path() {
do_log_info("Searching for untrimmed https:// entry with path");
yield promiseAddVisits({ uri: NetUtil.newURI("https://mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/t",
autofilled: "mozilla.org/test/",
completed: "https://mozilla.org/test/"
});
yield cleanup();
});
add_task(function* test_untrimmed_www() {
do_log_info("Searching for untrimmed http://www entry");
yield promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "www.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untrimmed_www_path() {
do_log_info("Searching for untrimmed http://www entry with path");
yield promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/t",
autofilled: "mozilla.org/test/",
completed: "http://www.mozilla.org/test/"
});
yield cleanup();
});
add_task(function* test_untrimmed_ftp() {
do_log_info("Searching for untrimmed ftp:// entry");
yield promiseAddVisits({ uri: NetUtil.newURI("ftp://mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "ftp://mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untrimmed_ftp_path() {
do_log_info("Searching for untrimmed ftp:// entry with path");
yield promiseAddVisits({ uri: NetUtil.newURI("ftp://mozilla.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/t",
autofilled: "mozilla.org/test/",
completed: "ftp://mozilla.org/test/"
});
yield cleanup();
});
add_task(function* test_priority_1() {
do_log_info("Ensuring correct priority 1");
yield promiseAddVisits([{ uri: NetUtil.newURI("https://www.mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("https://mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("ftp://mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://www.mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_periority_2() {
do_log_info( "Ensuring correct priority 2");
yield promiseAddVisits([{ uri: NetUtil.newURI("https://mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("ftp://mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://www.mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_periority_3() {
do_log_info("Ensuring correct priority 3");
yield promiseAddVisits([{ uri: NetUtil.newURI("ftp://mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://www.mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_periority_4() {
do_log_info("Ensuring correct priority 4");
yield promiseAddVisits([{ uri: NetUtil.newURI("http://www.mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.org/test/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_priority_5() {
do_log_info("Ensuring correct priority 5");
yield promiseAddVisits([{ uri: NetUtil.newURI("ftp://mozilla.org/test/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("ftp://www.mozilla.org/test/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "ftp://mozilla.org/"
});
yield cleanup();
});
add_task(function* test_priority_6() {
do_log_info("Ensuring correct priority 6");
yield promiseAddVisits([{ uri: NetUtil.newURI("http://www.mozilla.org/test1/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://www.mozilla.org/test2/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.org/",
completed: "www.mozilla.org/"
});
yield cleanup();
});
add_task(function* test_longer_domain() {
do_log_info("Ensuring longer domain can't match");
// The .co should be preferred, but should not get the https from the .com.
// The .co domain must be added later to activate the trigger bug.
yield promiseAddVisits([{ uri: NetUtil.newURI("https://mozilla.com/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.co/"),
transition: TRANSITION_TYPED },
{ uri: NetUtil.newURI("http://mozilla.co/"),
transition: TRANSITION_TYPED }]);
yield check_autocomplete({
search: "mo",
autofilled: "mozilla.co/",
completed: "mozilla.co/"
});
yield cleanup();
});
add_task(function* test_escaped_chars() {
do_log_info("Searching for URL with characters that are normally escaped");
yield promiseAddVisits({ uri: NetUtil.newURI("https://www.mozilla.org/啊-test"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "https://www.mozilla.org/啊-test",
autofilled: "https://www.mozilla.org/啊-test",
completed: "https://www.mozilla.org/啊-test"
});
yield cleanup();
});
add_task(function* test_unsecure_secure() {
do_log_info("Don't return unsecure URL when searching for secure ones");
yield promiseAddVisits({ uri: NetUtil.newURI("http://test.moz.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "https://test.moz.org/t",
autofilled: "https://test.moz.org/test/",
completed: "https://test.moz.org/test/"
});
yield cleanup();
});
add_task(function* test_unsecure_secure_domain() {
do_log_info("Don't return unsecure domain when searching for secure ones");
yield promiseAddVisits({ uri: NetUtil.newURI("http://test.moz.org/test/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "https://test.moz",
autofilled: "https://test.moz.org/",
completed: "https://test.moz.org/"
});
yield cleanup();
});
add_task(function* test_untyped_www() {
do_log_info("Untyped is not accounted for www");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits({ uri: NetUtil.newURI("http://www.moz.org/test/") });
yield check_autocomplete({
search: "mo",
autofilled: "moz.org/",
completed: "moz.org/"
});
yield cleanup();
});
add_task(function* test_untyped_ftp() {
do_log_info("Untyped is not accounted for ftp");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits({ uri: NetUtil.newURI("ftp://moz.org/test/") });
yield check_autocomplete({
search: "mo",
autofilled: "moz.org/",
completed: "moz.org/"
});
yield cleanup();
});
add_task(function* test_untyped_secure() {
do_log_info("Untyped is not accounted for https");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits({ uri: NetUtil.newURI("https://moz.org/test/") });
yield check_autocomplete({
search: "mo",
autofilled: "moz.org/",
completed: "moz.org/"
});
yield cleanup();
});
add_task(function* test_untyped_secure_www() {
do_log_info("Untyped is not accounted for https://www");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits({ uri: NetUtil.newURI("https://www.moz.org/test/") });
yield check_autocomplete({
search: "mo",
autofilled: "moz.org/",
completed: "moz.org/"
});
yield cleanup();
});

View File

@ -0,0 +1,78 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// First do searches with typed behavior forced to false, so later tests will
// ensure autocomplete is able to dinamically switch behavior.
add_task(function* test_domain() {
do_log_info("Searching for domain should autoFill it");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://mozilla.org/link/"));
yield check_autocomplete({
search: "moz",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_url() {
do_log_info("Searching for url should autoFill it");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits(NetUtil.newURI("http://mozilla.org/link/"));
yield check_autocomplete({
search: "mozilla.org/li",
autofilled: "mozilla.org/link/",
completed: "http://mozilla.org/link/"
});
yield cleanup();
});
// Now do searches with typed behavior forced to true.
add_task(function* test_untyped_domain() {
do_log_info("Searching for non-typed domain should not autoFill it");
yield promiseAddVisits(NetUtil.newURI("http://mozilla.org/link/"));
yield check_autocomplete({
search: "moz",
autofilled: "moz",
completed: "moz"
});
yield cleanup();
});
add_task(function* test_typed_domain() {
do_log_info("Searching for typed domain should autoFill it");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/typed/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "moz",
autofilled: "mozilla.org/",
completed: "mozilla.org/"
});
yield cleanup();
});
add_task(function* test_untyped_url() {
do_log_info("Searching for non-typed url should not autoFill it");
yield promiseAddVisits(NetUtil.newURI("http://mozilla.org/link/"));
yield check_autocomplete({
search: "mozilla.org/li",
autofilled: "mozilla.org/li",
completed: "mozilla.org/li"
});
yield cleanup();
});
add_task(function* test_typed_url() {
do_log_info("Searching for typed url should autoFill it");
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/link/"),
transition: TRANSITION_TYPED });
yield check_autocomplete({
search: "mozilla.org/li",
autofilled: "mozilla.org/link/",
completed: "http://mozilla.org/link/"
});
yield cleanup();
});

View File

@ -0,0 +1,171 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Test bug 393678 to make sure matches against the url, title, tags are only
* made on word boundaries instead of in the middle of words.
*
* Make sure we don't try matching one after a CamelCase because the upper-case
* isn't really a word boundary. (bug 429498)
*
* Bug 429531 provides switching between "must match on word boundary" and "can
* match," so leverage "must match" pref for checking word boundary logic and
* make sure "can match" matches anywhere.
*/
let katakana = ["\u30a8", "\u30c9"]; // E, Do
let ideograph = ["\u4efb", "\u5929", "\u5802"]; // Nin Ten Do
add_task(function* test_escape() {
let uri1 = NetUtil.newURI("http://matchme/");
let uri2 = NetUtil.newURI("http://dontmatchme/");
let uri3 = NetUtil.newURI("http://title/1");
let uri4 = NetUtil.newURI("http://title/2");
let uri5 = NetUtil.newURI("http://tag/1");
let uri6 = NetUtil.newURI("http://tag/2");
let uri7 = NetUtil.newURI("http://crazytitle/");
let uri8 = NetUtil.newURI("http://katakana/");
let uri9 = NetUtil.newURI("http://ideograph/");
let uri10 = NetUtil.newURI("http://camel/pleaseMatchMe/");
yield promiseAddVisits([ { uri: uri1, title: "title1" },
{ uri: uri2, title: "title1" },
{ uri: uri3, title: "matchme2" },
{ uri: uri4, title: "dontmatchme3" },
{ uri: uri5, title: "title1" },
{ uri: uri6, title: "title1" },
{ uri: uri7, title: "!@#$%^&*()_+{}|:<>?word" },
{ uri: uri8, title: katakana.join("") },
{ uri: uri9, title: ideograph.join("") },
{ uri: uri10, title: "title1" } ]);
addBookmark( { uri: uri5, title: "title1", tags: [ "matchme2" ] } );
addBookmark( { uri: uri6, title: "title1", tags: [ "dontmatchme3" ] } );
// match only on word boundaries
Services.prefs.setIntPref("browser.urlbar.matchBehavior", 2);
do_log_info("Match 'match' at the beginning or after / or on a CamelCase");
yield check_autocomplete({
search: "match",
matches: [ { uri: uri1, title: "title1" },
{ uri: uri3, title: "matchme2" },
{ uri: uri5, title: "title1", tags: [ "matchme2" ] },
{ uri: uri10, title: "title1" } ]
});
do_log_info("Match 'dont' at the beginning or after /");
yield check_autocomplete({
search: "dont",
matches: [ { uri: uri2, title: "title1" },
{ uri: uri4, title: "dontmatchme3" },
{ uri: uri6, title: "title1", tags: [ "dontmatchme3" ] } ]
});
do_log_info("Match 'match' at the beginning or after / or on a CamelCase");
yield check_autocomplete({
search: "2",
matches: [ { uri: uri3, title: "matchme2" },
{ uri: uri4, title: "dontmatchme3" },
{ uri: uri5, title: "title1", tags: [ "matchme2" ] },
{ uri: uri6, title: "title1", tags: [ "dontmatchme3" ] } ]
});
do_log_info("Match 't' at the beginning or after /");
yield check_autocomplete({
search: "t",
matches: [ { uri: uri1, title: "title1" },
{ uri: uri2, title: "title1" },
{ uri: uri3, title: "matchme2" },
{ uri: uri4, title: "dontmatchme3" },
{ uri: uri5, title: "title1", tags: [ "matchme2" ] },
{ uri: uri6, title: "title1", tags: [ "dontmatchme3" ] },
{ uri: uri10, title: "title1" } ]
});
do_log_info("Match 'word' after many consecutive word boundaries");
yield check_autocomplete({
search: "word",
matches: [ { uri: uri7, title: "!@#$%^&*()_+{}|:<>?word" } ]
});
do_log_info("Match a word boundary '/' for everything");
yield check_autocomplete({
search: "/",
matches: [ { uri: uri1, title: "title1" },
{ uri: uri2, title: "title1" },
{ uri: uri3, title: "matchme2" },
{ uri: uri4, title: "dontmatchme3" },
{ uri: uri5, title: "title1", tags: [ "matchme2" ] },
{ uri: uri6, title: "title1", tags: [ "dontmatchme3" ] },
{ uri: uri7, title: "!@#$%^&*()_+{}|:<>?word" },
{ uri: uri8, title: katakana.join("") },
{ uri: uri9, title: ideograph.join("") },
{ uri: uri10, title: "title1" } ]
});
do_log_info("Match word boundaries '()_+' that are among word boundaries");
yield check_autocomplete({
search: "()_+",
matches: [ { uri: uri7, title: "!@#$%^&*()_+{}|:<>?word" } ]
});
do_log_info("Katakana characters form a string, so match the beginning");
yield check_autocomplete({
search: katakana[0],
matches: [ { uri: uri8, title: katakana.join("") } ]
});
/*
do_log_info("Middle of a katakana word shouldn't be matched");
yield check_autocomplete({
search: katakana[1],
matches: [ ]
});
*/
do_log_info("Ideographs are treated as words so 'nin' is one word");
yield check_autocomplete({
search: ideograph[0],
matches: [ { uri: uri9, title: ideograph.join("") } ]
});
do_log_info("Ideographs are treated as words so 'ten' is another word");
yield check_autocomplete({
search: ideograph[1],
matches: [ { uri: uri9, title: ideograph.join("") } ]
});
do_log_info("Ideographs are treated as words so 'do' is yet another word");
yield check_autocomplete({
search: ideograph[2],
matches: [ { uri: uri9, title: ideograph.join("") } ]
});
do_log_info("Extra negative assert that we don't match in the middle");
yield check_autocomplete({
search: "ch",
matches: [ ]
});
do_log_info("Don't match one character after a camel-case word boundary (bug 429498)");
yield check_autocomplete({
search: "atch",
matches: [ ]
});
// match against word boundaries and anywhere
Services.prefs.setIntPref("browser.urlbar.matchBehavior", 1);
yield check_autocomplete({
search: "tch",
matches: [ { uri: uri1, title: "title1" },
{ uri: uri2, title: "title1" },
{ uri: uri3, title: "matchme2" },
{ uri: uri4, title: "dontmatchme3" },
{ uri: uri5, title: "title1", tags: [ "matchme2" ] },
{ uri: uri6, title: "title1", tags: [ "dontmatchme3" ] },
{ uri: uri10, title: "title1" } ]
});
yield cleanup();
});

View File

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Ensure inline autocomplete doesn't return zero frecency pages.
add_task(function* test_zzero_frec_domain() {
do_log_info("Searching for zero frecency domain should not autoFill it");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/framed_link/"),
transition: TRANSITION_FRAMED_LINK });
yield check_autocomplete({
search: "moz",
autofilled: "moz",
completed: "moz"
});
yield cleanup();
});
add_task(function* test_zzero_frec_url() {
do_log_info("Searching for zero frecency url should not autoFill it");
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
yield promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/framed_link/"),
transition: TRANSITION_FRAMED_LINK });
yield check_autocomplete({
search: "mozilla.org/f",
autofilled: "mozilla.org/f",
completed: "mozilla.org/f"
});
yield cleanup();
});

View File

@ -0,0 +1,31 @@
[DEFAULT]
head = head_autocomplete.js
tail =
[test_416211.js]
[test_416214.js]
[test_417798.js]
[test_418257.js]
[test_422277.js]
[test_autocomplete_functional.js]
[test_autocomplete_on_value_removed_479089.js]
[test_casing.js]
[test_do_not_trim.js]
[test_download_embed_bookmarks.js]
[test_dupe_urls.js]
[test_empty_search.js]
[test_enabled.js]
[test_escape_self.js]
[test_ignore_protocol.js]
[test_keyword_search.js]
[test_keywords.js]
[test_match_beginning.js]
[test_multi_word_search.js]
[test_queryurl.js]
[test_special_search.js]
[test_swap_protocol.js]
[test_tabmatches.js]
[test_trimming.js]
[test_typed.js]
[test_word_boundary_search.js]
[test_zero_frecency.js]

View File

@ -151,23 +151,11 @@ XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
});
function getOriginActivationType(origin) {
// access from moz-safe-about scheme will throw exception in getManifestPrefname
try {
var prefname = SocialServiceInternal.getManifestPrefname(origin);
} catch(e) {
// if this is an about uri, treat it as a directory
let originUri = Services.io.newURI(origin, null, null);
if (originUri.scheme == "moz-safe-about") {
return "internal";
}
throw e;
// if this is an about uri, treat it as a directory
let originUri = Services.io.newURI(origin, null, null);
if (originUri.scheme == "moz-safe-about") {
return "internal";
}
if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING)
return 'builtin';
let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
if (whitelist.indexOf(origin) >= 0)
return 'whitelist';
let directories = Services.prefs.getCharPref("social.directories").split(',');
if (directories.indexOf(origin) >= 0)
@ -380,10 +368,11 @@ this.SocialService = {
throw new Error("not allowed to set SocialService.enabled");
},
// Adds and activates a builtin provider. The provider may or may not have
// previously been added. onDone is always called - with null if no such
// provider exists, or the activated provider on success.
addBuiltinProvider: function addBuiltinProvider(origin, onDone) {
// Enables a provider, the manifest must already exist in prefs. The provider
// may or may not have previously been added. onDone is always called
// - with null if no such provider exists, or the activated provider on
// success.
enableProvider: function enableProvider(origin, onDone) {
if (SocialServiceInternal.providers[origin]) {
schedule(function() {
onDone(SocialServiceInternal.providers[origin]);
@ -425,9 +414,9 @@ this.SocialService = {
// Removes a provider with the given origin, and notifies when the removal is
// complete.
removeProvider: function removeProvider(origin, onDone) {
disableProvider: function disableProvider(origin, onDone) {
if (!(origin in SocialServiceInternal.providers))
throw new Error("SocialService.removeProvider: no provider with origin " + origin + " exists!");
throw new Error("SocialService.disableProvider: no provider with origin " + origin + " exists!");
let provider = SocialServiceInternal.providers[origin];
let manifest = SocialService.getManifestByOrigin(origin);
@ -527,8 +516,6 @@ this.SocialService = {
data.origin = principal.origin;
// iconURL and name are required
// iconURL may be a different origin (CDN or data url support) if this is
// a whitelisted or directory listed provider
let providerHasFeatures = [url for (url of featureURLs) if (data[url])].length > 0;
if (!providerHasFeatures) {
Cu.reportError("SocialService.manifestFromData manifest missing required urls.");
@ -650,23 +637,6 @@ this.SocialService = {
installer = new AddonInstaller(sourceURI, manifest, installCallback);
this._showInstallNotification(aDOMDocument, installer);
break;
case "builtin":
// for builtin, we already have a manifest, but it can be overridden
// we need to return the manifest in the installcallback, so fetch
// it if we have it. If there is no manifest data for the builtin,
// the install request MUST be from the provider, otherwise we have
// no way to know what provider we're trying to enable. This is
// primarily an issue for "version zero" providers that did not
// send the manifest with the dom event for activation.
if (!manifest) {
let prefname = getPrefnameFromOrigin(installOrigin);
manifest = Services.prefs.getDefaultBranch(null)
.getComplexValue(prefname, Ci.nsISupportsString).data;
manifest = JSON.parse(manifest);
// ensure we override a builtin manifest by having a different value in it
if (manifest.builtin)
delete manifest.builtin;
}
case "internal":
// double check here since "builtin" falls through this as well.
aBypassUserEnable = installType == "internal" && manifest.oneclick;
@ -678,8 +648,6 @@ this.SocialService = {
installer.install();
return;
}
// otherwise fall through to the install below which presents the panel
case "whitelist":
// a manifest is required, we'll catch a missing manifest below.
if (!manifest)
throw new Error("Cannot install provider without manifest data");
@ -774,9 +742,10 @@ function SocialProvider(input) {
this.errorState = null;
this.frecency = 0;
let activationType = getOriginActivationType(input.origin);
this.blessed = activationType == "builtin" ||
activationType == "whitelist";
// this provider has localStorage access in the worker if listed in the
// whitelist
let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
this.blessed = whitelist.indexOf(this.origin) >= 0;
try {
this.domain = etld.getBaseDomainFromHost(originUri.host);
@ -1055,7 +1024,7 @@ var SocialAddonProvider = {
if (ActiveProviders.has(manifest.origin)) {
let addon = new AddonWrapper(manifest);
if (addon.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
SocialService.removeProvider(manifest.origin);
SocialService.disableProvider(manifest.origin);
}
}
} catch(e) {
@ -1252,9 +1221,9 @@ AddonWrapper.prototype = {
if (val == this.userDisabled)
return val;
if (val) {
SocialService.removeProvider(this.manifest.origin);
SocialService.disableProvider(this.manifest.origin);
} else if (!this.appDisabled) {
SocialService.addBuiltinProvider(this.manifest.origin);
SocialService.enableProvider(this.manifest.origin);
}
return val;
},
@ -1263,7 +1232,7 @@ AddonWrapper.prototype = {
let prefName = getPrefnameFromOrigin(this.manifest.origin);
if (Services.prefs.prefHasUserValue(prefName)) {
if (ActiveProviders.has(this.manifest.origin)) {
SocialService.removeProvider(this.manifest.origin, function() {
SocialService.disableProvider(this.manifest.origin, function() {
SocialAddonProvider.removeAddon(this, aCallback);
}.bind(this));
} else {

View File

@ -16,7 +16,7 @@ function test() {
SocialService.addProvider(manifest, function (p) {
provider = p;
runTests(tests, undefined, undefined, function () {
SocialService.removeProvider(p.origin, function() {
SocialService.disableProvider(p.origin, function() {
ok(!provider.enabled, "removing an enabled provider should have disabled the provider");
let port = provider.getWorkerPort();
ok(!port, "should not be able to get a port after removing the provider");
@ -74,7 +74,7 @@ let tests = {
port2.onmessage = function(e) {
if (e.data.topic == "test-initialization-complete") {
ok(true, "second provider initialized");
SocialService.removeProvider(provider2.origin, function() {
SocialService.disableProvider(provider2.origin, function() {
next();
});
}

View File

@ -23,7 +23,7 @@ function test() {
waitForExplicitFinish();
let cbPostTest = function(cb) {
SocialService.removeProvider(TEST_PROVIDER_ORIGIN, function() {cb()});
SocialService.disableProvider(TEST_PROVIDER_ORIGIN, function() {cb()});
};
replaceAlertsService();
registerCleanupFunction(restoreAlertsService);

View File

@ -22,7 +22,7 @@ function test() {
p.enabled = true;
provider = p;
runTests(tests, undefined, undefined, function () {
SocialService.removeProvider(provider.origin, finish);
SocialService.disableProvider(provider.origin, finish);
});
});
}

View File

@ -51,8 +51,8 @@ function testAddProviders(manifests, next) {
function testRemoveProviders(manifests, next) {
do_check_true(SocialService.enabled);
yield SocialService.removeProvider(manifests[0].origin, next);
yield SocialService.removeProvider(manifests[1].origin, next);
yield SocialService.disableProvider(manifests[0].origin, next);
yield SocialService.disableProvider(manifests[1].origin, next);
do_check_false(SocialService.enabled);
}
@ -106,7 +106,7 @@ function testAddRemoveProvider(manifests, next) {
do_check_neq(providersAfter.indexOf(newProvider), -1);
// Now remove the provider
yield SocialService.removeProvider(newProvider.origin, next);
yield SocialService.disableProvider(newProvider.origin, next);
providersAfter = yield SocialService.getProviderList(next);
do_check_eq(providersAfter.length, originalProviders.length);
do_check_eq(providersAfter.indexOf(newProvider), -1);

View File

@ -205,7 +205,7 @@ static void WriteSubmissionEvent(SubmissionResult result,
string localId = GetDumpLocalID();
string fpath = gEventsPath + UI_DIR_SEPARATOR + localId + "-submission";
ofstream* f = UIOpenWrite(fpath.c_str());
ofstream* f = UIOpenWrite(fpath.c_str(), false, true);
time_t tm;
time(&tm);

View File

@ -141,7 +141,9 @@ bool UIFileExists(const std::string& path);
bool UIMoveFile(const std::string& oldfile, const std::string& newfile);
bool UIDeleteFile(const std::string& oldfile);
std::ifstream* UIOpenRead(const std::string& filename);
std::ofstream* UIOpenWrite(const std::string& filename, bool append=false);
std::ofstream* UIOpenWrite(const std::string& filename,
bool append=false,
bool binary=false);
void UIPruneSavedDumps(const std::string& directory);
#ifdef _MSC_VER

View File

@ -439,9 +439,19 @@ std::ifstream* UIOpenRead(const string& filename)
return new std::ifstream(filename.c_str(), std::ios::in);
}
std::ofstream* UIOpenWrite(const string& filename, bool append) // append=false
std::ofstream* UIOpenWrite(const string& filename,
bool append, // append=false
bool binary) // binary=false
{
return new std::ofstream(filename.c_str(),
append ? std::ios::out | std::ios::app
: std::ios::out);
std::ios_base::openmode mode = std::ios::out;
if (append) {
mode = mode | std::ios::app;
}
if (binary) {
mode = mode | std::ios::binary;
}
return new std::ofstream(filename.c_str(), mode);
}

View File

@ -887,9 +887,19 @@ std::ifstream* UIOpenRead(const string& filename)
return new std::ifstream(filename.c_str(), std::ios::in);
}
std::ofstream* UIOpenWrite(const string& filename, bool append) // append=false
std::ofstream* UIOpenWrite(const string& filename,
bool append, // append=false
bool binary) // binary=false
{
return new std::ofstream(filename.c_str(),
append ? std::ios::out | std::ios::app
: std::ios::out);
std::ios_base::openmode mode = std::ios::out;
if (append) {
mode = mode | std::ios::app;
}
if (binary) {
mode = mode | std::ios::binary;
}
return new std::ofstream(filename.c_str(), mode);
}

View File

@ -1452,24 +1452,30 @@ ifstream* UIOpenRead(const string& filename)
return file;
}
ofstream* UIOpenWrite(const string& filename, bool append) // append=false
ofstream* UIOpenWrite(const string& filename,
bool append, // append=false
bool binary) // binary=false
{
// adapted from breakpad's src/common/windows/http_upload.cc
std::ios_base::openmode mode = ios::out;
if (append) {
mode = mode | ios::app;
}
if (binary) {
mode = mode | ios::binary;
}
// The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
// wchar_t* filename, so use _wfopen directly in that case. For VC8 and
// later, _wfopen has been deprecated in favor of _wfopen_s, which does
// not exist in earlier versions, so let the ifstream open the file itself.
// For VC8 and later, _wfopen has been deprecated in favor of _wfopen_s,
// which does not exist in earlier versions, so let the ifstream open the
// file itself.
#if _MSC_VER >= 1400 // MSVC 2005/8
ofstream* file = new ofstream();
file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app
: ios::out);
file->open(UTF8ToWide(filename).c_str(), mode);
#elif defined(_MSC_VER)
ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(),
append ? L"a" : L"w"));
#error "Compiling with your version of MSVC is no longer supported."
#else // GCC
ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
append ? ios::out | ios::app : ios::out);
mode);
#endif // _MSC_VER >= 1400
return file;

View File

@ -191,7 +191,11 @@ InlineSpellChecker.prototype = {
if (curlang == sortedList[i].id) {
item.setAttribute("checked", "true");
} else {
var callback = function(me, val) { return function(evt) { me.selectDictionary(val, me.menu.ownerDocument.defaultView); } };
var callback = function(me, val) {
return function(evt) {
me.selectDictionary(val, menu.ownerDocument.defaultView);
}
};
item.addEventListener("command", callback(this, i), true);
}
if (insertBefore)

View File

@ -128,6 +128,39 @@ functionality will not be enabled or you will be prompted for your
Bugzilla credentials when they are needed.
'''.lstrip()
BZPOST_MINIMUM_VERSION = StrictVersion('3.0')
BZPOST_INFO = '''
The bzpost extension automatically records the URLs of pushed commits to
referenced Bugzilla bugs after push.
Would you like to activate bzpost
'''.strip()
FIREFOXTREE_MINIMUM_VERSION = StrictVersion('3.0')
FIREFOXTREE_INFO = '''
The firefoxtree extension makes interacting with the multiple Firefox
repositories easier:
* Aliases for common trees are pre-defined. e.g. `hg pull central`
* Pulling from known Firefox trees will create "remote refs" appearing as
tags. e.g. pulling from fx-team will produce a "fx-team" tag.
* The `hg fxheads` command will list the heads of all pulled Firefox repos
for easy reference.
* `hg push` will limit itself to pushing a single head when pushing to
Firefox repos.
* A pre-push hook will prevent you from pushing multiple heads to known
Firefox repos. This acts quicker than a server-side hook.
The firefoxtree extension is *strongly* recommended if you:
a) aggregate multiple Firefox repositories into a single local repo
b) perform head/bookmark-based development (as opposed to mq)
Would you like to activate firefoxtree
'''.strip()
class MercurialSetupWizard(object):
"""Command-line wizard to help users configure Mercurial."""
@ -233,6 +266,12 @@ class MercurialSetupWizard(object):
'projects',
path=p)
if hg_version >= BZPOST_MINIMUM_VERSION:
self.prompt_external_extension(c, 'bzpost', BZPOST_INFO)
if hg_version >= FIREFOXTREE_MINIMUM_VERSION:
self.prompt_external_extension(c, 'firefoxtree', FIREFOXTREE_INFO)
if 'mq' in c.extensions:
self.prompt_external_extension(c, 'mqext', MQEXT_INFO,
os.path.join(self.ext_dir, 'mqext'))
@ -261,7 +300,7 @@ class MercurialSetupWizard(object):
print('Configured qnew to set patch author by default.')
print('')
if 'reviewboard' in c.extensions:
if 'reviewboard' in c.extensions or 'bzpost' in c.extensions:
bzuser, bzpass = c.get_bugzilla_credentials()
if not bzuser or not bzpass:
@ -346,7 +385,11 @@ class MercurialSetupWizard(object):
# in a directory with the same name as the extension and thus also
# flagging the version-control-tools repo as needing an update.
if name not in c.extensions:
print(name)
print('=' * len(name))
print('')
if not self._prompt_yn(prompt_text):
print('')
return
print('Activated %s extension.\n' % name)
if not path: