зеркало из https://github.com/mozilla/pjs.git
Bug 480350 - Show currently loaded URIs in location bar autocomplete results, allow switching to the tab. r=gavin,mak sr=mconnor
This commit is contained in:
Родитель
3ad3d7614a
Коммит
1cfd933bbd
|
@ -240,6 +240,7 @@ pref("browser.urlbar.delay", 50);
|
|||
pref("browser.urlbar.restrict.history", "^");
|
||||
pref("browser.urlbar.restrict.bookmark", "*");
|
||||
pref("browser.urlbar.restrict.tag", "+");
|
||||
pref("browser.urlbar.restrict.openpage", "%");
|
||||
pref("browser.urlbar.restrict.typed", "~");
|
||||
pref("browser.urlbar.match.title", "#");
|
||||
pref("browser.urlbar.match.url", "@");
|
||||
|
@ -247,7 +248,8 @@ pref("browser.urlbar.match.url", "@");
|
|||
// The default behavior for the urlbar can be configured to use any combination
|
||||
// of the restrict or match filters with each additional filter restricting
|
||||
// more (intersection). Add the following values to set the behavior as the
|
||||
// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed
|
||||
// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
|
||||
// 64: javascript, 128: tabs
|
||||
// E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
|
||||
// 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
|
||||
pref("browser.urlbar.default.behavior", 0);
|
||||
|
|
|
@ -51,6 +51,26 @@ toolbar[mode="icons"] > #reload-button[displaystop] {
|
|||
-moz-binding: url(chrome://browser/content/urlbarBindings.xml#urlbar);
|
||||
}
|
||||
|
||||
/* Some child nodes want to be ordered based on the locale's direction, while
|
||||
everything else should be ltr. */
|
||||
#urlbar:-moz-locale-dir(rtl) > .autocomplete-textbox-container > .textbox-input-box {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
#urlbar html|*.autocomplete-textbox {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* For results that are actions, their description text is shown instead of
|
||||
the URL - this needs to follow the locale's direction, unlike URLs. */
|
||||
richlistitem[type="action"]:-moz-locale-dir(rtl) > .ac-url-box {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
#urlbar:not([actiontype]) > #urlbar-display {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#wrapper-urlbar-container > #urlbar-container > #urlbar {
|
||||
-moz-user-input: disabled;
|
||||
cursor: -moz-grab;
|
||||
|
|
|
@ -3805,7 +3805,6 @@ var XULBrowserWindow = {
|
|||
overLink: "",
|
||||
startTime: 0,
|
||||
statusText: "",
|
||||
lastURI: null,
|
||||
isBusy: false,
|
||||
|
||||
_progressCollapseTimer: 0,
|
||||
|
@ -3869,7 +3868,6 @@ var XULBrowserWindow = {
|
|||
delete this.statusTextField;
|
||||
delete this.securityButton;
|
||||
delete this.statusText;
|
||||
delete this.lastURI;
|
||||
},
|
||||
|
||||
setJSStatus: function (status) {
|
||||
|
@ -4084,7 +4082,6 @@ var XULBrowserWindow = {
|
|||
nBox.removeTransientNotifications();
|
||||
}
|
||||
}
|
||||
selectedBrowser.lastURI = aLocationURI;
|
||||
|
||||
// Disable menu entries for images, enable otherwise
|
||||
if (content.document && mimeTypeIsTextBased(content.document.contentType))
|
||||
|
@ -7589,3 +7586,42 @@ var LightWeightThemeWebInstaller = {
|
|||
node.baseURI);
|
||||
}
|
||||
}
|
||||
|
||||
function switchToTabHavingURI(aURI) {
|
||||
function switchIfURIInWindow(aWindow) {
|
||||
if (!("gBrowser" in aWindow))
|
||||
return false;
|
||||
let browsers = aWindow.gBrowser.browsers;
|
||||
for (let i = 0; i < browsers.length; i++) {
|
||||
let browser = browsers[i];
|
||||
if (browser.currentURI.equals(aURI)) {
|
||||
gURLBar.handleRevert();
|
||||
aWindow.focus();
|
||||
aWindow.gBrowser.tabContainer.selectedIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This can be passed either nsIURI or a string.
|
||||
if (!(aURI instanceof Ci.nsIURI))
|
||||
aURI = makeURI(aURI);
|
||||
|
||||
// Prioritise this window.
|
||||
if (switchIfURIInWindow(window))
|
||||
return true;
|
||||
|
||||
let winEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let browserWin = winEnum.getNext();
|
||||
// Skip closed (but not yet destroyed) windows,
|
||||
// and the current window (which was checked earlier).
|
||||
if (browserWin.closed || browserWin == window)
|
||||
continue;
|
||||
if (switchIfURIInWindow(browserWin))
|
||||
return true;
|
||||
}
|
||||
// No opened tab has that url.
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -404,6 +404,7 @@
|
|||
noneplaceholder="&urlbar.none.emptyText;"
|
||||
type="autocomplete"
|
||||
autocompletesearch="history"
|
||||
autocompletesearchparam="enable-actions"
|
||||
autocompletepopup="PopupAutoCompleteRichResult"
|
||||
completeselectedindex="true"
|
||||
tabscrolling="true"
|
||||
|
@ -442,6 +443,7 @@
|
|||
</hbox>
|
||||
</hbox>
|
||||
</box>
|
||||
<label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
|
||||
<hbox id="urlbar-icons">
|
||||
<button type="menu"
|
||||
style="-moz-user-focus: none"
|
||||
|
|
|
@ -96,6 +96,10 @@
|
|||
Components.classes["@mozilla.org/browser/favicon-service;1"]
|
||||
.getService(Components.interfaces.nsIFaviconService);
|
||||
</field>
|
||||
<field name="mBrowserHistory" readonly="true">
|
||||
Components.classes["@mozilla.org/browser/nav-history-service;1"]
|
||||
.getService(Components.interfaces.nsIBrowserHistory);
|
||||
</field>
|
||||
<field name="mTabBox" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
|
||||
</field>
|
||||
|
@ -452,15 +456,30 @@
|
|||
// changing location, clear out the missing plugins list
|
||||
this.mBrowser.missingPlugins = null;
|
||||
|
||||
if (this.mBlank)
|
||||
return;
|
||||
var browserHistory = this.mTabBrowser.mBrowserHistory;
|
||||
if ("lastURI" in this.mBrowser && this.mBrowser.lastURI)
|
||||
browserHistory.unregisterOpenPage(this.mBrowser.lastURI);
|
||||
browserHistory.registerOpenPage(aLocation);
|
||||
|
||||
if (this.mTabBrowser.mCurrentTab == this.mTab) {
|
||||
for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
|
||||
let p = this.mTabBrowser.mProgressListeners[i];
|
||||
if (!this.mBlank) {
|
||||
if (this.mTabBrowser.mCurrentTab == this.mTab) {
|
||||
for (let i = 0; i < this.mTabBrowser.mProgressListeners.length; i++) {
|
||||
let p = this.mTabBrowser.mProgressListeners[i];
|
||||
if (p)
|
||||
try {
|
||||
p.onLocationChange(aWebProgress, aRequest, aLocation);
|
||||
} catch (e) {
|
||||
// don't inhibit other listeners
|
||||
Components.utils.reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
|
||||
let p = this.mTabBrowser.mTabsProgressListeners[i];
|
||||
if (p)
|
||||
try {
|
||||
p.onLocationChange(aWebProgress, aRequest, aLocation);
|
||||
p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
|
||||
} catch (e) {
|
||||
// don't inhibit other listeners
|
||||
Components.utils.reportError(e);
|
||||
|
@ -468,16 +487,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.mTabBrowser.mTabsProgressListeners.length; i++) {
|
||||
let p = this.mTabBrowser.mTabsProgressListeners[i];
|
||||
if (p)
|
||||
try {
|
||||
p.onLocationChange(this.mBrowser, aWebProgress, aRequest, aLocation);
|
||||
} catch (e) {
|
||||
// don't inhibit other listeners
|
||||
Components.utils.reportError(e);
|
||||
}
|
||||
}
|
||||
this.mBrowser.lastURI = aLocation;
|
||||
|
||||
},
|
||||
|
||||
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
|
||||
|
@ -1419,6 +1430,10 @@
|
|||
filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
|
||||
this.mTabListeners[aTab._tPos].destroy();
|
||||
|
||||
let closedBrowser = this.getBrowserForTab(aTab);
|
||||
if (closedBrowser.currentURI)
|
||||
this.mBrowserHistory.unregisterOpenPage(closedBrowser.currentURI);
|
||||
|
||||
// We are no longer the primary content area.
|
||||
browser.setAttribute("type", "content-targetable");
|
||||
|
||||
|
@ -2260,7 +2275,9 @@
|
|||
<destructor>
|
||||
<![CDATA[
|
||||
for (var i = 0; i < this.mTabListeners.length; ++i) {
|
||||
this.getBrowserAtIndex(i).webProgress.removeProgressListener(this.mTabFilters[i]);
|
||||
let browser = this.getBrowserAtIndex(i);
|
||||
this.mBrowserHistory.unregisterOpenPage(browser.currentURI);
|
||||
browser.webProgress.removeProgressListener(this.mTabFilters[i]);
|
||||
this.mTabFilters[i].removeProgressListener(this.mTabListeners[i]);
|
||||
this.mTabFilters[i] = null;
|
||||
this.mTabListeners[i].destroy();
|
||||
|
|
|
@ -150,6 +150,7 @@ _BROWSER_FILES = \
|
|||
alltabslistener.html \
|
||||
zoom_test.html \
|
||||
dummy_page.html \
|
||||
browser_tabMatchesInAwesomebar.js \
|
||||
$(NULL)
|
||||
|
||||
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim:set ts=2 sw=2 sts=2 et:
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Places Test Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Blair McBride <bmcbride@mozilla.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const TEST_URL_BASES = [
|
||||
"http://example.org/browser/browser/base/content/test/dummy_page.html#tabmatch",
|
||||
"http://example.org/browser/browser/base/content/test/moz.png#tabmatch"
|
||||
];
|
||||
|
||||
var gPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
|
||||
getService(Ci.nsIPrivateBrowsingService);
|
||||
|
||||
|
||||
var gTabWaitCount = 0;
|
||||
var gTabCounter = 0;
|
||||
|
||||
var gTestSteps = [
|
||||
function() {
|
||||
info("Running step 1");
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let tab = gBrowser.addTab();
|
||||
loadTab(tab, TEST_URL_BASES[0] + (++gTabCounter));
|
||||
}
|
||||
},
|
||||
function() {
|
||||
info("Running step 2");
|
||||
gBrowser.selectTabAtIndex(1);
|
||||
gBrowser.removeCurrentTab();
|
||||
gBrowser.selectTabAtIndex(1);
|
||||
gBrowser.removeCurrentTab();
|
||||
for (let i = 1; i < gBrowser.tabs.length; i++)
|
||||
loadTab(gBrowser.tabs[i], TEST_URL_BASES[1] + (++gTabCounter));
|
||||
},
|
||||
function() {
|
||||
info("Running step 3");
|
||||
for (let i = 1; i < gBrowser.tabs.length; i++)
|
||||
loadTab(gBrowser.tabs[i], TEST_URL_BASES[0] + gTabCounter);
|
||||
},
|
||||
function() {
|
||||
info("Running step 4");
|
||||
let ps = Services.prefs;
|
||||
ps.setBoolPref("browser.privatebrowsing.keep_current_session", true);
|
||||
ps.setBoolPref("browser.tabs.warnOnClose", false);
|
||||
|
||||
gPrivateBrowsing.privateBrowsingEnabled = true;
|
||||
|
||||
executeSoon(function() {
|
||||
ensure_opentabs_match_db();
|
||||
nextStep();
|
||||
});
|
||||
},
|
||||
function() {
|
||||
info("Running step 5");
|
||||
gPrivateBrowsing.privateBrowsingEnabled = false;
|
||||
|
||||
executeSoon(function() {
|
||||
let ps = Services.prefs;
|
||||
try {
|
||||
ps.clearUserPref("browser.privatebrowsing.keep_current_session");
|
||||
} catch (ex) {}
|
||||
try {
|
||||
ps.clearUserPref("browser.tabs.warnOnClose");
|
||||
} catch (ex) {}
|
||||
|
||||
ensure_opentabs_match_db();
|
||||
nextStep()
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
nextStep();
|
||||
}
|
||||
|
||||
function loadTab(tab, url) {
|
||||
tab.linkedBrowser.addEventListener("load", function (event) {
|
||||
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
if (--gTabWaitCount > 0)
|
||||
return;
|
||||
is(gTabWaitCount, 0,
|
||||
"sanity check, gTabWaitCount should not be decremented below 0");
|
||||
|
||||
try {
|
||||
ensure_opentabs_match_db();
|
||||
} catch (e) {
|
||||
ok(false, "exception from ensure_openpages_match_db: " + e);
|
||||
}
|
||||
|
||||
executeSoon(nextStep);
|
||||
}, true);
|
||||
gTabWaitCount++;
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
}
|
||||
|
||||
function nextStep() {
|
||||
if (gTestSteps.length == 0) {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.selectTabAtIndex(1);
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
|
||||
waitForClearHistory(finish);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var stepFunc = gTestSteps.shift();
|
||||
stepFunc();
|
||||
}
|
||||
|
||||
function ensure_opentabs_match_db() {
|
||||
var tabs = {};
|
||||
|
||||
var winEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
while (winEnum.hasMoreElements()) {
|
||||
let browserWin = winEnum.getNext();
|
||||
// skip closed-but-not-destroyed windows
|
||||
if (browserWin.closed)
|
||||
continue;
|
||||
|
||||
for (let i = 0; i < browserWin.gBrowser.tabContainer.childElementCount; i++) {
|
||||
let browser = browserWin.gBrowser.getBrowserAtIndex(i);
|
||||
let url = browser.currentURI.spec;
|
||||
if (!(url in tabs))
|
||||
tabs[url] = 1;
|
||||
else
|
||||
tabs[url]++;
|
||||
}
|
||||
}
|
||||
|
||||
var db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.DBConnection;
|
||||
|
||||
try {
|
||||
var stmt = db.createStatement(
|
||||
"SELECT IFNULL(p_t.url, p.url) AS url, open_count, place_id " +
|
||||
"FROM moz_openpages_temp " +
|
||||
"LEFT JOIN moz_places p ON p.id=place_id " +
|
||||
"LEFT JOIN moz_places_temp p_t ON p_t.id=place_id");
|
||||
} catch (e) {
|
||||
ok(false, "error creating db statement: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
var dbtabs = [];
|
||||
try {
|
||||
while (stmt.executeStep()) {
|
||||
ok(stmt.row.url in tabs,
|
||||
"url is in db, should be in tab: " + stmt.row.url);
|
||||
is(tabs[stmt.row.url], stmt.row.open_count,
|
||||
"db count (" + stmt.row.open_count + ") " +
|
||||
"should match actual open tab count " +
|
||||
"(" + tabs[stmt.row.url] + "): " + stmt.row.url);
|
||||
dbtabs.push(stmt.row.url);
|
||||
}
|
||||
} finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
for (let url in tabs) {
|
||||
// ignore URLs that should never be in the places db
|
||||
if (!is_expected_in_db(url))
|
||||
continue;
|
||||
ok(dbtabs.indexOf(url) > -1,
|
||||
"tab is open (" + tabs[url] + " times) and should recorded in db: " + url);
|
||||
}
|
||||
}
|
||||
|
||||
function is_expected_in_db(url) {
|
||||
var uri = Services.io.newURI(url, null, null);
|
||||
return PlacesUtils.history.canAddURI(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears history invoking callback when done.
|
||||
*/
|
||||
function waitForClearHistory(aCallback) {
|
||||
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
|
||||
|
||||
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
|
||||
getService(Ci.nsINavHistoryService);
|
||||
hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
|
||||
}
|
|
@ -61,8 +61,8 @@
|
|||
this.inputField.addEventListener("mousedown", this, false);
|
||||
this.inputField.addEventListener("mousemove", this, false);
|
||||
this.inputField.addEventListener("mouseout", this, false);
|
||||
this.inputField.addEventListener("overflow", this, false);
|
||||
this.inputField.addEventListener("underflow", this, false);
|
||||
this.inputField.addEventListener("overflow", this, false);
|
||||
this.inputField.addEventListener("underflow", this, false);
|
||||
]]></constructor>
|
||||
|
||||
<destructor><![CDATA[
|
||||
|
@ -72,10 +72,45 @@
|
|||
this.inputField.removeEventListener("mousedown", this, false);
|
||||
this.inputField.removeEventListener("mousemove", this, false);
|
||||
this.inputField.removeEventListener("mouseout", this, false);
|
||||
this.inputField.removeEventListener("overflow", this, false);
|
||||
this.inputField.removeEventListener("underflow", this, false);
|
||||
this.inputField.removeEventListener("overflow", this, false);
|
||||
this.inputField.removeEventListener("underflow", this, false);
|
||||
]]></destructor>
|
||||
|
||||
<field name="_value"></field>
|
||||
|
||||
<!--
|
||||
onBeforeValueGet is called by the base-binding's .value getter.
|
||||
It can return an object with a "value" property, to override the
|
||||
return value of the getter.
|
||||
-->
|
||||
<method name="onBeforeValueGet">
|
||||
<body><![CDATA[
|
||||
if (this.hasAttribute("actiontype"))
|
||||
return {value: this._value};
|
||||
return null;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
onBeforeValueSet is called by the base-binding's .value setter.
|
||||
It should return the value that the setter should use.
|
||||
-->
|
||||
<method name="onBeforeValueSet">
|
||||
<parameter name="aValue"/>
|
||||
<body><![CDATA[
|
||||
this._value = aValue;
|
||||
var returnValue = aValue;
|
||||
var action = this._parseActionUrl(aValue);
|
||||
if (action) {
|
||||
returnValue = action.param;
|
||||
this.setAttribute("actiontype", action.type);
|
||||
} else {
|
||||
this.removeAttribute("actiontype");
|
||||
}
|
||||
return returnValue;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="handleRevert">
|
||||
<body><![CDATA[
|
||||
var isScrolling = this.popupOpen;
|
||||
|
@ -108,6 +143,13 @@
|
|||
if (!url)
|
||||
return;
|
||||
|
||||
var action = this._parseActionUrl(url);
|
||||
if (action) {
|
||||
if (action.type == "switchtab")
|
||||
switchToTabHavingURI(action.param);
|
||||
return;
|
||||
}
|
||||
|
||||
this.value = url;
|
||||
gBrowser.userTypedValue = url;
|
||||
try {
|
||||
|
@ -216,12 +258,12 @@
|
|||
return [url, postData.value];
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<field name="_contentIsCropped">false</field>
|
||||
|
||||
<field name="_contentIsCropped">false</field>
|
||||
|
||||
<method name="_initURLTooltip">
|
||||
<body><![CDATA[
|
||||
if (this.focused || !this._contentIsCropped)
|
||||
if (this.focused || !this._contentIsCropped)
|
||||
return;
|
||||
if (this._tooltipTimer)
|
||||
clearTimeout(this._tooltipTimer);
|
||||
|
@ -389,13 +431,13 @@
|
|||
case "mouseout":
|
||||
this._hideURLTooltip();
|
||||
break;
|
||||
case "overflow":
|
||||
this._contentIsCropped = true;
|
||||
break;
|
||||
case "underflow":
|
||||
this._contentIsCropped = false;
|
||||
this._hideURLTooltip();
|
||||
break;
|
||||
case "overflow":
|
||||
this._contentIsCropped = true;
|
||||
break;
|
||||
case "underflow":
|
||||
this._contentIsCropped = false;
|
||||
this._hideURLTooltip();
|
||||
break;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
@ -442,6 +484,18 @@
|
|||
this.placeholder = this.getAttribute(type + "placeholder");
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="_parseActionUrl">
|
||||
<parameter name="aUrl"/>
|
||||
<body><![CDATA[
|
||||
if (!/^moz-action:/.test(aUrl))
|
||||
return null;
|
||||
|
||||
// url is in the format moz-action:ACTION,PARAM
|
||||
let [, action, param] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
|
||||
return {type: action, param: param};
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
|
@ -613,6 +667,15 @@
|
|||
this.closePopup();
|
||||
controller.handleEscape();
|
||||
|
||||
// Check if this is meant to be an action
|
||||
let action = this.mInput._parseActionUrl(url);
|
||||
if (action) {
|
||||
if (action.type == "switchtab")
|
||||
url = action.param;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// respect the usual clicking subtleties
|
||||
openUILink(url, aEvent);
|
||||
}
|
||||
|
@ -640,4 +703,5 @@
|
|||
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
||||
|
|
|
@ -207,6 +207,7 @@
|
|||
<!ENTITY urlbar.history.emptyText "Search History">
|
||||
<!ENTITY urlbar.none.emptyText "Type a Web address">
|
||||
<!ENTITY urlbar.accesskey "d">
|
||||
<!ENTITY urlbar.switchToTab.label "Switch to tab:">
|
||||
|
||||
<!--
|
||||
Comment duplicated from browser-sets.inc:
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 293 B |
|
@ -800,6 +800,17 @@ toolbar[iconsize="small"] #fullscreen-button {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: -2px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-end: 3px;
|
||||
color: GrayText;
|
||||
-moz-border-end: 1px solid #AAA;
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
#PopupAutoComplete,
|
||||
#PopupAutoCompleteRichResult {
|
||||
direction: ltr !important;
|
||||
|
@ -998,6 +1009,10 @@ toolbar[iconsize="small"] #fullscreen-button {
|
|||
color: -moz-nativehyperlinktext;
|
||||
}
|
||||
|
||||
richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
|
||||
list-style-image: url("chrome://browser/skin/actionicon-tab.png");
|
||||
}
|
||||
|
||||
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
|
||||
color: GrayText;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ browser.jar:
|
|||
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
|
||||
skin/classic/browser/aboutSessionRestore-window-icon.png
|
||||
skin/classic/browser/aboutCertError.css (aboutCertError.css)
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
* skin/classic/browser/browser.css (browser.css)
|
||||
* skin/classic/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/browser/fullscreen-video.css
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 293 B |
|
@ -868,6 +868,17 @@ toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarke
|
|||
width: 10px;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: -2px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-end: 3px;
|
||||
color: GrayText;
|
||||
-moz-border-end: 1px solid #AAA;
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
#PopupAutoCompleteRichResult {
|
||||
direction: ltr !important;
|
||||
margin-top: 2px;
|
||||
|
@ -929,6 +940,10 @@ richlistitem[selected="true"][current="true"] > hbox > .ac-result-type-bookmark,
|
|||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
|
||||
list-style-image: url("chrome://browser/skin/actionicon-tab.png");
|
||||
}
|
||||
|
||||
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
|
||||
color: GrayText;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ browser.jar:
|
|||
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
|
||||
skin/classic/browser/aboutSessionRestore-window-icon.png
|
||||
skin/classic/browser/aboutCertError.css (aboutCertError.css)
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
* skin/classic/browser/browser.css (browser.css)
|
||||
* skin/classic/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/browser/feed-icons.png
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 293 B |
|
@ -610,6 +610,17 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru
|
|||
-moz-box-align: stretch;
|
||||
}
|
||||
|
||||
#urlbar-display {
|
||||
margin-top: -2px;
|
||||
margin-bottom: -2px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 2px;
|
||||
-moz-padding-end: 3px;
|
||||
color: GrayText;
|
||||
-moz-border-end: 1px solid #AAA;
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
/* identity box */
|
||||
|
||||
#identity-box {
|
||||
|
@ -766,6 +777,10 @@ toolbar:not([iconsize="small"])[mode="icons"] #forward-button:not([disabled="tru
|
|||
color: #006600;
|
||||
}
|
||||
|
||||
richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
|
||||
list-style-image: url("chrome://browser/skin/actionicon-tab.png");
|
||||
}
|
||||
|
||||
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
|
||||
color: GrayText;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ browser.jar:
|
|||
* skin/classic/browser/aboutSessionRestore.css (aboutSessionRestore.css)
|
||||
skin/classic/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon.png)
|
||||
skin/classic/browser/aboutCertError.css (aboutCertError.css)
|
||||
skin/classic/browser/actionicon-tab.png
|
||||
* skin/classic/browser/browser.css (browser.css)
|
||||
* skin/classic/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/browser/fullscreen-video.css
|
||||
|
@ -93,6 +94,7 @@ browser.jar:
|
|||
* skin/classic/aero/browser/aboutSessionRestore.css (aboutSessionRestore.css)
|
||||
skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
|
||||
skin/classic/aero/browser/aboutCertError.css (aboutCertError.css)
|
||||
skin/classic/aero/browser/actionicon-tab.png (actionicon-tab.png)
|
||||
* skin/classic/aero/browser/browser.css (browser-aero.css)
|
||||
* skin/classic/aero/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/aero/browser/fullscreen-video.css
|
||||
|
|
|
@ -107,4 +107,13 @@ interface mozIPlacesAutoComplete : nsISupports
|
|||
* Search javascript: URLs.
|
||||
*/
|
||||
const long BEHAVIOR_JAVASCRIPT = 1 << 6;
|
||||
|
||||
/**
|
||||
* Search for pages that have been marked as being opened, such as a tab
|
||||
* in a tabbrowser, via:
|
||||
* nsIBrowserHistory.registerOpenPage(url)
|
||||
*
|
||||
* @see nsIBrowserHistory.idl
|
||||
*/
|
||||
const long BEHAVIOR_OPENPAGE = 1 << 7;
|
||||
};
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
#include "nsISupports.idl"
|
||||
#include "nsIGlobalHistory2.idl"
|
||||
|
||||
[scriptable, uuid(124a8db3-59da-48ac-a563-6dcf58e035b4)]
|
||||
[scriptable, uuid(540aca25-1e01-467f-b24c-df89cbe40f8d)]
|
||||
interface nsIBrowserHistory : nsIGlobalHistory2
|
||||
{
|
||||
/**
|
||||
|
@ -156,4 +156,18 @@ interface nsIBrowserHistory : nsIGlobalHistory2
|
|||
* the user (for example by clicking on it).
|
||||
*/
|
||||
void markPageAsFollowedLink(in nsIURI aURI);
|
||||
|
||||
/**
|
||||
* Mark a page as being currently open.
|
||||
*/
|
||||
void registerOpenPage(in nsIURI aURI);
|
||||
|
||||
/**
|
||||
* Mark a page as no longer being open (either by closing the window or tab,
|
||||
* or by navigating away from that page).
|
||||
*
|
||||
* Note that when Private Browsing mode is entered/exited, pages need to be
|
||||
* manually unregistered/registered.
|
||||
*/
|
||||
void unregisterOpenPage(in nsIURI aURI);
|
||||
};
|
||||
|
|
|
@ -991,6 +991,13 @@ nsNavHistory::InitTempTables()
|
|||
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// moz_openpages_temp
|
||||
rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_OPENPAGES_TEMP);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDBConn->ExecuteSimpleSQL(CREATE_REMOVEOPENPAGE_CLEANUP_TRIGGER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1217,6 +1224,27 @@ nsNavHistory::InitStatements()
|
|||
"WHERE url = ?2"),
|
||||
getter_AddRefs(mDBSetPlaceTitle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mDBRegisterOpenPage
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT OR REPLACE INTO moz_openpages_temp (place_id, open_count) "
|
||||
"VALUES (?1, "
|
||||
"IFNULL("
|
||||
"(SELECT open_count + 1 FROM moz_openpages_temp WHERE place_id = ?1), "
|
||||
"1"
|
||||
")"
|
||||
")"),
|
||||
getter_AddRefs(mDBRegisterOpenPage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mDBUnregisterOpenPage
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_openpages_temp "
|
||||
"SET open_count = open_count - 1 "
|
||||
"WHERE place_id = ?1"),
|
||||
getter_AddRefs(mDBUnregisterOpenPage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// mDBVisitsForFrecency
|
||||
// NOTE: This is not limited to visits with "visit_type NOT IN (0,4,7,8)"
|
||||
// because otherwise mDBVisitsForFrecency would return no visits
|
||||
|
@ -5035,6 +5063,72 @@ nsNavHistory::MarkPageAsFollowedLink(nsIURI *aURI)
|
|||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistory::RegisterOpenPage(nsIURI* aURI)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
|
||||
NS_ENSURE_ARG(aURI);
|
||||
|
||||
// Don't add any pages while in Private Browsing mode, so as to avoid leaking
|
||||
// information about other windows that might otherwise stay hidden
|
||||
// and private.
|
||||
if (InPrivateBrowsingMode())
|
||||
return NS_OK;
|
||||
|
||||
PRBool canAdd = PR_FALSE;
|
||||
nsresult rv = CanAddURI(aURI, &canAdd);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt64 placeId;
|
||||
// Note: If the URI has never been added to history (but can be added),
|
||||
// LAZY_ADD will cause this to add an orphan page, until the visit is added.
|
||||
rv = GetUrlIdFor(aURI, &placeId, canAdd);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (placeId == 0)
|
||||
return NS_OK;
|
||||
|
||||
mozStorageStatementScoper scoper(mDBRegisterOpenPage);
|
||||
|
||||
rv = mDBRegisterOpenPage->BindInt64Parameter(0, placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDBRegisterOpenPage->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistory::UnregisterOpenPage(nsIURI* aURI)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
|
||||
NS_ENSURE_ARG(aURI);
|
||||
|
||||
// Entering Private Browsing mode will unregister all open pages, therefore
|
||||
// there shouldn't be anything in the moz_openpages_temp table. So we can stop
|
||||
// now without doing any unnecessary work.
|
||||
if (InPrivateBrowsingMode())
|
||||
return NS_OK;
|
||||
|
||||
PRInt64 placeId;
|
||||
nsresult rv = GetUrlIdFor(aURI, &placeId, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (placeId == 0)
|
||||
return NS_OK;
|
||||
|
||||
mozStorageStatementScoper scoper(mDBUnregisterOpenPage);
|
||||
|
||||
rv = mDBUnregisterOpenPage->BindInt64Parameter(0, placeId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mDBUnregisterOpenPage->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// nsNavHistory::SetCharsetForURI
|
||||
//
|
||||
// Sets the character-set for a URI.
|
||||
|
@ -8154,6 +8248,8 @@ nsNavHistory::FinalizeStatements() {
|
|||
mDBUpdateFrecencyAndHidden,
|
||||
mDBGetPlaceVisitStats,
|
||||
mDBFullVisitCount,
|
||||
mDBRegisterOpenPage,
|
||||
mDBUnregisterOpenPage,
|
||||
};
|
||||
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) {
|
||||
|
|
|
@ -427,6 +427,8 @@ protected:
|
|||
nsCOMPtr<mozIStorageStatement> mDBGetTags; // used by GetTags
|
||||
nsCOMPtr<mozIStorageStatement> mDBGetItemsWithAnno; // used by AutoComplete::StartSearch and FilterResultSet
|
||||
nsCOMPtr<mozIStorageStatement> mDBSetPlaceTitle; // used by SetPageTitleInternal
|
||||
nsCOMPtr<mozIStorageStatement> mDBRegisterOpenPage; // used by RegisterOpenPage
|
||||
nsCOMPtr<mozIStorageStatement> mDBUnregisterOpenPage; // used by UnregisterOpenPage
|
||||
|
||||
// these are used by VisitIdToResultNode for making new result nodes from IDs
|
||||
// Consumers need to use the getters since these statements are lazily created
|
||||
|
|
|
@ -94,6 +94,7 @@ const kQueryIndexVisitCount = 6;
|
|||
const kQueryIndexTyped = 7;
|
||||
const kQueryIndexPlaceId = 8;
|
||||
const kQueryIndexQueryType = 9;
|
||||
const kQueryIndexOpenPageCount = 10;
|
||||
|
||||
// AutoComplete query type constants. Describes the various types of queries
|
||||
// that we can process.
|
||||
|
@ -217,9 +218,11 @@ function nsPlacesAutoComplete()
|
|||
// Note: h.frecency is only selected because we need it for ordering.
|
||||
function sql_base_fragment(aTableName) {
|
||||
return "SELECT h.url, h.title, f.url, " + kBookTagSQLFragment + ", " +
|
||||
"h.visit_count, h.typed, h.id, :query_type, h.frecency " +
|
||||
"h.visit_count, h.typed, h.id, :query_type, t.open_count, " +
|
||||
"h.frecency " +
|
||||
"FROM " + aTableName + " h " +
|
||||
"LEFT OUTER JOIN moz_favicons f ON f.id = h.favicon_id " +
|
||||
"LEFT OUTER JOIN moz_openpages_temp t ON t.place_id = h.id " +
|
||||
"WHERE h.frecency <> 0 " +
|
||||
"AND AUTOCOMPLETE_MATCH(:searchString, h.url, " +
|
||||
"IFNULL(bookmark, h.title), tags, " +
|
||||
|
@ -291,6 +294,13 @@ function nsPlacesAutoComplete()
|
|||
);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_openPagesQuery", function() {
|
||||
let replacementText = "AND t.open_count > 0";
|
||||
return this._db.createStatement(
|
||||
SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g")
|
||||
);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_typedQuery", function() {
|
||||
let replacementText = "AND h.typed = 1";
|
||||
return this._db.createStatement(
|
||||
|
@ -311,7 +321,7 @@ function nsPlacesAutoComplete()
|
|||
kBookTagSQLFragment + ", " +
|
||||
"IFNULL(h_t.visit_count, h.visit_count) AS c_visit_count, " +
|
||||
"IFNULL(h_t.typed, h.typed) AS c_typed, " +
|
||||
"IFNULL(h_t.id, h.id), :query_type, rank " +
|
||||
"IFNULL(h_t.id, h.id), :query_type, t.open_count, rank " +
|
||||
"FROM ( " +
|
||||
"SELECT ROUND(MAX(((i.input = :search_string) + " +
|
||||
"(SUBSTR(i.input, 1, LENGTH(:search_string)) = :search_string)) * " +
|
||||
|
@ -323,6 +333,7 @@ function nsPlacesAutoComplete()
|
|||
"LEFT JOIN moz_places h ON h.id = i.place_id " +
|
||||
"LEFT JOIN moz_places_temp h_t ON h_t.id = i.place_id " +
|
||||
"LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " +
|
||||
"LEFT JOIN moz_openpages_temp t ON t.place_id = i.place_id " +
|
||||
"WHERE c_url NOTNULL " +
|
||||
"AND AUTOCOMPLETE_MATCH(:searchString, c_url, " +
|
||||
"IFNULL(bookmark, c_title), tags, " +
|
||||
|
@ -343,12 +354,13 @@ function nsPlacesAutoComplete()
|
|||
best_favicon_for_revhost("moz_places") + "), b.parent, " +
|
||||
"b.title, NULL, IFNULL(h_t.visit_count, h.visit_count), " +
|
||||
"IFNULL(h_t.typed, h.typed), COALESCE(h_t.id, h.id, b.fk), " +
|
||||
":query_type " +
|
||||
":query_type, t.open_count " +
|
||||
"FROM moz_keywords k " +
|
||||
"JOIN moz_bookmarks b ON b.keyword_id = k.id " +
|
||||
"LEFT JOIN moz_places AS h ON h.url = search_url " +
|
||||
"LEFT JOIN moz_places_temp AS h_t ON h_t.url = search_url " +
|
||||
"LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " +
|
||||
"LEFT JOIN moz_openpages_temp t ON t.place_id = IFNULL(h_t.id, h.id) " +
|
||||
"WHERE LOWER(k.keyword) = LOWER(:keyword) " +
|
||||
"ORDER BY IFNULL(h_t.frecency, h.frecency) DESC"
|
||||
);
|
||||
|
@ -387,6 +399,9 @@ nsPlacesAutoComplete.prototype = {
|
|||
this._currentSearchString =
|
||||
this._fixupSearchText(this._originalSearchString.toLowerCase());
|
||||
|
||||
var searchParamParts = aSearchParam.split(" ");
|
||||
this._enableActions = searchParamParts.indexOf("enable-actions") != -1;
|
||||
|
||||
this._listener = aListener;
|
||||
let result = Cc["@mozilla.org/autocomplete/simple-result;1"].
|
||||
createInstance(Ci.nsIAutoCompleteSimpleResult);
|
||||
|
@ -519,6 +534,7 @@ nsPlacesAutoComplete.prototype = {
|
|||
"_historyQuery",
|
||||
"_bookmarkQuery",
|
||||
"_tagsQuery",
|
||||
"_openPagesQuery",
|
||||
"_typedQuery",
|
||||
"_adaptiveQuery",
|
||||
"_keywordQuery",
|
||||
|
@ -598,6 +614,7 @@ nsPlacesAutoComplete.prototype = {
|
|||
delete this._usedPlaceIds;
|
||||
delete this._pendingQuery;
|
||||
this._secondPass = false;
|
||||
this._enableActions = false;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -680,13 +697,14 @@ nsPlacesAutoComplete.prototype = {
|
|||
this._restrictBookmarkToken = safeGetter("restrict.bookmark", "*");
|
||||
this._restrictTypedToken = safeGetter("restrict.typed", "~");
|
||||
this._restrictTagToken = safeGetter("restrict.tag", "+");
|
||||
this._restrictOpenPageToken = safeGetter("restrict.openpage", "%");
|
||||
this._matchTitleToken = safeGetter("match.title", "#");
|
||||
this._matchURLToken = safeGetter("match.url", "@");
|
||||
this._defaultBehavior = safeGetter("default.behavior", 0);
|
||||
// Further restrictions to apply for "empty searches" (i.e. searches for "").
|
||||
// By default we use (HISTORY | TYPED) = 33.
|
||||
// By default we use (HISTORY | TYPED | OPENPAGE) = 161.
|
||||
this._emptySearchDefaultBehavior = this._defaultBehavior |
|
||||
safeGetter("default.behavior.emptyRestriction", 33);
|
||||
safeGetter("default.behavior.emptyRestriction", 161);
|
||||
|
||||
// Validate matchBehavior; default to MATCH_BOUNDARY_ANYWHERE.
|
||||
if (this._matchBehavior != MATCH_ANYWHERE &&
|
||||
|
@ -727,6 +745,11 @@ nsPlacesAutoComplete.prototype = {
|
|||
case this._restrictTagToken:
|
||||
this._setBehavior("tag");
|
||||
break;
|
||||
case this._restrictOpenPageToken:
|
||||
if (!this._enableActions)
|
||||
continue;
|
||||
this._setBehavior("openpage");
|
||||
break;
|
||||
case this._matchTitleToken:
|
||||
this._setBehavior("title");
|
||||
break;
|
||||
|
@ -780,6 +803,7 @@ nsPlacesAutoComplete.prototype = {
|
|||
this._hasBehavior("bookmark") ? this._bookmarkQuery :
|
||||
this._hasBehavior("typed") ? this._typedQuery :
|
||||
this._hasBehavior("history") ? this._historyQuery :
|
||||
this._hasBehavior("openpage") ? this._openPagesQuery :
|
||||
this._defaultQuery;
|
||||
|
||||
// Bind the needed parameters to the query so consumers can use it.
|
||||
|
@ -880,6 +904,7 @@ nsPlacesAutoComplete.prototype = {
|
|||
let entryBookmarkTitle = entryParentId ?
|
||||
aRow.getResultByIndex(kQueryIndexBookmarkTitle) : null;
|
||||
let entryTags = aRow.getResultByIndex(kQueryIndexTags) || "";
|
||||
let openPageCount = aRow.getResultByIndex(kQueryIndexOpenPageCount) || 0;
|
||||
|
||||
// Always prefer the bookmark title unless it is empty
|
||||
let title = entryBookmarkTitle || entryTitle;
|
||||
|
@ -926,8 +951,22 @@ nsPlacesAutoComplete.prototype = {
|
|||
style = "favicon";
|
||||
}
|
||||
|
||||
// And finally add this to our results.
|
||||
this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style);
|
||||
// If actions aren't enabled, avoid doing any additional work.
|
||||
if (!this._enableActions) {
|
||||
this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add a special entry for an open-page match.
|
||||
if ((this._hasBehavior("openpage") || this._hasBehavior("everything")) &&
|
||||
openPageCount > 0)
|
||||
this._addToResults(entryId, "moz-action:switchtab," + escapedEntryURL, title, entryFavicon, "action");
|
||||
|
||||
// If restricting to only open-page matches, there should only be the
|
||||
// switch-to-tab results.
|
||||
if (!this._onlyHasBehavior("openpage"))
|
||||
this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -981,15 +1020,32 @@ nsPlacesAutoComplete.prototype = {
|
|||
* Determines if the specified AutoComplete behavior is set.
|
||||
*
|
||||
* @param aType
|
||||
* The behavior type to test for.
|
||||
* The behavior type to test for, or "everything" to test if no
|
||||
* specific behavior has been set.
|
||||
* @return true if the behavior is set, false otherwise.
|
||||
*/
|
||||
_hasBehavior: function PAC_hasBehavior(aType)
|
||||
{
|
||||
if (aType == "everything")
|
||||
return this._behavior == 0;
|
||||
return (this._behavior &
|
||||
Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the specified AutoComplete behavior is the only behavior set.
|
||||
*
|
||||
* @param aType
|
||||
* The behavior type to test for.
|
||||
* @return true if the behavior is set and no other behaviors are set,
|
||||
* false otherwise.
|
||||
*/
|
||||
_onlyHasBehavior: function PAC_onlyHasBehavior(aType)
|
||||
{
|
||||
return (this._behavior ==
|
||||
Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables the desired AutoComplete behavior.
|
||||
*
|
||||
|
|
|
@ -177,4 +177,11 @@
|
|||
")" \
|
||||
)
|
||||
|
||||
#define CREATE_MOZ_OPENPAGES_TEMP NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMP TABLE moz_openpages_temp (" \
|
||||
" place_id INTEGER PRIMARY KEY" \
|
||||
", open_count INTEGER" \
|
||||
")" \
|
||||
)
|
||||
|
||||
#endif // __nsPlacesTables_h__
|
||||
|
|
|
@ -94,8 +94,8 @@
|
|||
|
||||
/**
|
||||
* This trigger allows for the deletion of a record in moz_places_view. It
|
||||
* removes any entry in the temporary table, and any entry in the permanent
|
||||
* table as well.
|
||||
* removes any entry in the temporary table, the permanent table, and any
|
||||
* associated entry in moz_openpages_temp.
|
||||
*/
|
||||
#define CREATE_PLACES_VIEW_DELETE_TRIGGER NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMPORARY TRIGGER moz_places_view_delete_trigger " \
|
||||
|
@ -106,6 +106,8 @@
|
|||
"WHERE id = OLD.id; " \
|
||||
"DELETE FROM moz_places " \
|
||||
"WHERE id = OLD.id; " \
|
||||
"DELETE FROM moz_openpages_temp " \
|
||||
"WHERE place_id = OLD.id; " \
|
||||
"END" \
|
||||
)
|
||||
|
||||
|
@ -258,4 +260,18 @@
|
|||
#define CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER \
|
||||
CREATE_TEMP_SYNC_TRIGGER_BASE("moz_historyvisits", MOZ_HISTORYVISITS_COLUMNS)
|
||||
|
||||
|
||||
/**
|
||||
* This trigger removes a row from moz_openpages_temp when open_count reaches 0.
|
||||
*/
|
||||
#define CREATE_REMOVEOPENPAGE_CLEANUP_TRIGGER NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMPORARY TRIGGER moz_openpages_temp_afterupdate_trigger " \
|
||||
"AFTER UPDATE OF open_count ON moz_openpages_temp FOR EACH ROW " \
|
||||
"WHEN NEW.open_count = 0 " \
|
||||
"BEGIN " \
|
||||
"DELETE FROM moz_openpages_temp " \
|
||||
"WHERE place_id = NEW.place_id;" \
|
||||
"END" \
|
||||
)
|
||||
|
||||
#endif // __nsPlacesTriggers_h__
|
||||
|
|
|
@ -99,6 +99,9 @@ function ensure_results(aSearch, aExpected)
|
|||
|
||||
controller.input = input;
|
||||
|
||||
if (typeof kSearchParam == "string")
|
||||
input.searchParam = kSearchParam;
|
||||
|
||||
let numSearchesStarted = 0;
|
||||
input.onSearchBegin = function() {
|
||||
numSearchesStarted++;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim:set ts=2 sw=2 sts=2 et:
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Places Test Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Blair McBride <bmcbride@mozilla.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
let gTabRestrictChar = prefs.getCharPref("browser.urlbar.restrict.openpage");
|
||||
|
||||
let kSearchParam = "enable-actions";
|
||||
|
||||
let kURIs = [
|
||||
"http://abc.com/",
|
||||
"moz-action:switchtab,http://abc.com/",
|
||||
"http://xyz.net/",
|
||||
"moz-action:switchtab,http://xyz.net/"
|
||||
];
|
||||
|
||||
let kTitles = [
|
||||
"ABC rocks",
|
||||
"xyz.net - we're better than ABC"
|
||||
];
|
||||
|
||||
addPageBook(0, 0);
|
||||
gPages[1] = [1, 0];
|
||||
addPageBook(2, 1);
|
||||
gPages[3] = [3, 1];
|
||||
|
||||
addOpenPages(0, 1);
|
||||
|
||||
|
||||
let gTests = [
|
||||
["0: single result, that is also a tab match",
|
||||
"abc.com", [0,1]],
|
||||
["1: two results, one tab match",
|
||||
"abc", [0,1,2]],
|
||||
["2: two results, both tab matches",
|
||||
"abc", [0,1,2,3],
|
||||
function() {
|
||||
addOpenPages(2, 1);
|
||||
}],
|
||||
["3: two results, both tab matches, one has multiple tabs",
|
||||
"abc", [0,1,2,3],
|
||||
function() {
|
||||
addOpenPages(2, 5);
|
||||
}],
|
||||
["4: two results, no tab matches",
|
||||
"abc", [0,2],
|
||||
function() {
|
||||
removeOpenPages(0, 1);
|
||||
removeOpenPages(2, 6);
|
||||
}],
|
||||
["5: tab match search with restriction character",
|
||||
gTabRestrictChar + " abc", [1],
|
||||
function() {
|
||||
addOpenPages(0, 1);
|
||||
}]
|
||||
];
|
||||
|
||||
|
||||
function addOpenPages(aUri, aCount) {
|
||||
let num = aCount || 1;
|
||||
for (let i = 0; i < num; i++) {
|
||||
bhist.registerOpenPage(toURI(kURIs[aUri]));
|
||||
}
|
||||
}
|
||||
|
||||
function removeOpenPages(aUri, aCount) {
|
||||
let num = aCount || 1;
|
||||
for (let i = 0; i < num; i++) {
|
||||
bhist.unregisterOpenPage(toURI(kURIs[aUri]));
|
||||
}
|
||||
}
|
|
@ -42,6 +42,11 @@
|
|||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % actionsDTD SYSTEM "chrome://global/locale/actions.dtd">
|
||||
%actionsDTD;
|
||||
]>
|
||||
|
||||
<bindings id="autocompleteBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
|
@ -253,10 +258,21 @@
|
|||
|
||||
<!-- =================== PUBLIC MEMBERS =================== -->
|
||||
|
||||
<property name="value"
|
||||
onget="return this.inputField.value;">
|
||||
<property name="value">
|
||||
<getter><![CDATA[
|
||||
if (typeof this.onBeforeValueGet == "function") {
|
||||
var result = this.onBeforeValueGet();
|
||||
if (result)
|
||||
return result.value;
|
||||
}
|
||||
return this.inputField.value;
|
||||
]]></getter>
|
||||
<setter><![CDATA[
|
||||
this.mIgnoreInput = true;
|
||||
|
||||
if (typeof this.onBeforeValueSet == "function")
|
||||
val = this.onBeforeValueSet(val);
|
||||
|
||||
this.inputField.value = val;
|
||||
this.mIgnoreInput = false;
|
||||
var event = document.createEvent('Events');
|
||||
|
@ -516,11 +532,23 @@
|
|||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="resetActionType">
|
||||
<body><![CDATA[
|
||||
if (this.mIgnoreInput)
|
||||
return;
|
||||
this.removeAttribute("actiontype");
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="input"
|
||||
action="if (!this.mIgnoreInput && this.mController.input == this) this.mController.handleText(true);"/>
|
||||
<handler event="input"><![CDATA[
|
||||
if (!this.mIgnoreInput && this.mController.input == this) {
|
||||
this.mController.handleText(true);
|
||||
}
|
||||
this.resetActionType();
|
||||
]]></handler>
|
||||
|
||||
<handler event="keypress" phase="capturing"
|
||||
action="return this.onKeyPress(event);"/>
|
||||
|
@ -1135,7 +1163,7 @@
|
|||
|
||||
<binding id="autocomplete-richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
|
||||
<content>
|
||||
<xul:hbox align="center">
|
||||
<xul:hbox align="center" class="ac-title-box">
|
||||
<xul:image xbl:inherits="src=image" class="ac-site-icon"/>
|
||||
<xul:hbox anonid="title-box" class="ac-title" flex="1"
|
||||
onunderflow="_doUnderflow('_title');">
|
||||
|
@ -1152,6 +1180,7 @@
|
|||
</xul:hbox>
|
||||
<xul:hbox align="center" class="ac-url-box">
|
||||
<xul:spacer class="ac-site-icon"/>
|
||||
<xul:image class="ac-action-icon"/>
|
||||
<xul:hbox anonid="url-box" class="ac-url" flex="1"
|
||||
onunderflow="_doUnderflow('_url');">
|
||||
<xul:description anonid="url" class="ac-normal-text ac-url-text" xbl:inherits="selected"/>
|
||||
|
@ -1325,12 +1354,19 @@
|
|||
<method name="_setUpDescription">
|
||||
<parameter name="aDescriptionElement"/>
|
||||
<parameter name="aText"/>
|
||||
<parameter name="aNoEmphasis"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Get rid of all previous text
|
||||
while (aDescriptionElement.hasChildNodes())
|
||||
aDescriptionElement.removeChild(aDescriptionElement.firstChild);
|
||||
|
||||
// If aNoEmphasis is specified, don't add any emphasis
|
||||
if (aNoEmphasis) {
|
||||
aDescriptionElement.appendChild(document.createTextNode(aText));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the indices that separate match and non-match text
|
||||
let search = this.getAttribute("text");
|
||||
let tokens = this._getSearchTokens(search);
|
||||
|
@ -1372,6 +1408,10 @@
|
|||
var title = this.getAttribute("title");
|
||||
var type = this.getAttribute("type");
|
||||
|
||||
this.removeAttribute("actiontype");
|
||||
|
||||
var setupUrl = true;
|
||||
|
||||
// If we have a tag match, show the tags and icon
|
||||
if (type == "tag") {
|
||||
// Configure the extra box for tags display
|
||||
|
@ -1413,6 +1453,15 @@
|
|||
|
||||
// Don't emphasize keyword searches in the title or url
|
||||
this.setAttribute("text", "");
|
||||
} else if (type == "action") {
|
||||
let [,action, param] = url.match(/^moz-action:([^,]+),(.*)$/);
|
||||
this.setAttribute("actiontype", action);
|
||||
url = param;
|
||||
this._extraBox.hidden = true;
|
||||
this._titleBox.flex = 1;
|
||||
let desc = "]]>&action.switchToTab.label;<![CDATA[";
|
||||
this._setUpDescription(this._url, desc, true);
|
||||
setupUrl = false;
|
||||
} else {
|
||||
// Hide the title's extra box if we don't need extra stuff
|
||||
this._extraBox.hidden = true;
|
||||
|
@ -1429,7 +1478,8 @@
|
|||
|
||||
// Emphasize the matching search terms for the description
|
||||
this._setUpDescription(this._title, title);
|
||||
this._setUpDescription(this._url, url);
|
||||
if (setupUrl)
|
||||
this._setUpDescription(this._url, url);
|
||||
|
||||
// Set up overflow on a timeout because the contents of the box
|
||||
// might not have a width yet even though we just changed them
|
||||
|
|
|
@ -623,6 +623,9 @@
|
|||
|
||||
<field name="mIconURL">null</field>
|
||||
|
||||
<!-- This is managed by the tabbrowser -->
|
||||
<field name="lastURI">null</field>
|
||||
|
||||
<field name="mDestroyed">false</field>
|
||||
|
||||
<constructor>
|
||||
|
@ -699,6 +702,8 @@
|
|||
// The feeds cache can keep the document inside this browser alive.
|
||||
this.feeds = null;
|
||||
|
||||
this.lastURI = null;
|
||||
|
||||
this.removeEventListener("pageshow", this.onPageShow, true);
|
||||
this.removeEventListener("pagehide", this.onPageHide, true);
|
||||
this.removeEventListener("DOMPopupBlocked", this.onPopupBlocked, true);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<!ENTITY action.switchToTab.label "Switch to tab">
|
|
@ -6,6 +6,7 @@
|
|||
locale/@AB_CD@/global/aboutAbout.dtd (%chrome/global/aboutAbout.dtd)
|
||||
locale/@AB_CD@/global/aboutRights.dtd (%chrome/global/aboutRights.dtd)
|
||||
locale/@AB_CD@/global/aboutRights.properties (%chrome/global/aboutRights.properties)
|
||||
locale/@AB_CD@/global/actions.dtd (%chrome/global/actions.dtd)
|
||||
locale/@AB_CD@/global/appPicker.dtd (%chrome/global/appPicker.dtd)
|
||||
locale/@AB_CD@/global/brand.dtd (generic/chrome/global/brand.dtd)
|
||||
+ locale/@AB_CD@/global/browser.properties (%chrome/global/browser.properties)
|
||||
|
|
Загрузка…
Ссылка в новой задаче