Merge fx-team to m-c. a=merge
@ -1455,7 +1455,7 @@ pref("devtools.browserconsole.filter.secwarn", true);
|
||||
pref("devtools.webconsole.fontSize", 0);
|
||||
|
||||
// Number of usages of the web console or scratchpad.
|
||||
// If this is less than 10, then pasting code into the web console or scratchpad is disabled
|
||||
// If this is less than 5, then pasting code into the web console or scratchpad is disabled
|
||||
pref("devtools.selfxss.count", 0);
|
||||
|
||||
// Persistent logging: |true| if you want the Web Console to keep all of the
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/webRTC-indicator.css" type="text/css"?>
|
||||
|
||||
<window id="main-window"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
|
@ -317,6 +317,7 @@
|
||||
openUILinkIn(url, "current", {
|
||||
allowThirdPartyFixup: true,
|
||||
disallowInheritPrincipal: !mayInheritPrincipal,
|
||||
allowPinnedTabHostChange: true,
|
||||
postData: postData
|
||||
});
|
||||
}
|
||||
|
@ -185,6 +185,7 @@ function whereToOpenLink( e, ignoreButton, ignoreAlt )
|
||||
* referrerURI (nsIURI)
|
||||
* relatedToCurrent (boolean)
|
||||
* skipTabAnimation (boolean)
|
||||
* allowPinnedTabHostChange (boolean)
|
||||
*/
|
||||
function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
|
||||
var params;
|
||||
@ -220,6 +221,7 @@ function openLinkIn(url, where, params) {
|
||||
var aInitiatingDoc = params.initiatingDoc;
|
||||
var aIsPrivate = params.private;
|
||||
var aSkipTabAnimation = params.skipTabAnimation;
|
||||
var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
|
||||
|
||||
if (where == "save") {
|
||||
if (!aInitiatingDoc) {
|
||||
@ -288,7 +290,8 @@ function openLinkIn(url, where, params) {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (where == "current" && w.gBrowser.selectedTab.pinned) {
|
||||
if (where == "current" && w.gBrowser.selectedTab.pinned &&
|
||||
!aAllowPinnedTabHostChange) {
|
||||
try {
|
||||
// nsIURI.host can throw for non-nsStandardURL nsIURIs.
|
||||
if (!uriObj || (!uriObj.schemeIs("javascript") &&
|
||||
|
@ -25,8 +25,9 @@ function init(event) {
|
||||
popup.addEventListener("command", onPopupMenuCommand);
|
||||
}
|
||||
|
||||
document.getElementById("firefoxButton")
|
||||
.addEventListener("click", onFirefoxButtonClick);
|
||||
let fxButton = document.getElementById("firefoxButton");
|
||||
fxButton.addEventListener("click", onFirefoxButtonClick);
|
||||
fxButton.addEventListener("mousedown", PositionHandler);
|
||||
|
||||
updateIndicatorState();
|
||||
}
|
||||
@ -65,9 +66,10 @@ function updateIndicatorState() {
|
||||
screenShareButton.removeAttribute("tooltiptext");
|
||||
}
|
||||
|
||||
// Resize and center the window.
|
||||
// Resize and ensure the window position is correct
|
||||
// (sizeToContent messes with our position).
|
||||
window.sizeToContent();
|
||||
window.moveTo((screen.availWidth - document.documentElement.clientWidth) / 2, 0);
|
||||
PositionHandler.adjustPosition();
|
||||
}
|
||||
|
||||
function updateWindowAttr(attr, value) {
|
||||
@ -90,7 +92,8 @@ function onPopupMenuShowing(event) {
|
||||
|
||||
if (activeStreams.length == 1) {
|
||||
webrtcUI.showSharingDoorhanger(activeStreams[0], type);
|
||||
return false;
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let stream of activeStreams) {
|
||||
@ -100,8 +103,6 @@ function onPopupMenuShowing(event) {
|
||||
item.stream = stream;
|
||||
popup.appendChild(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function onPopupMenuHiding(event) {
|
||||
@ -121,3 +122,55 @@ function onFirefoxButtonClick(event) {
|
||||
let activeStreams = webrtcUI.getActiveStreams(true, true, true);
|
||||
activeStreams[0].browser.ownerDocument.defaultView.focus();
|
||||
}
|
||||
|
||||
let PositionHandler = {
|
||||
positionCustomized: false,
|
||||
threshold: 10,
|
||||
adjustPosition: function() {
|
||||
if (!this.positionCustomized) {
|
||||
// Center the window horizontally on the screen (not the available area).
|
||||
window.moveTo((screen.width - document.documentElement.clientWidth) / 2, 0);
|
||||
} else {
|
||||
// This will ensure we're at y=0.
|
||||
this.setXPosition(window.screenX);
|
||||
}
|
||||
},
|
||||
setXPosition: function(desiredX) {
|
||||
// Ensure the indicator isn't moved outside the available area of the screen.
|
||||
let desiredX = Math.max(desiredX, screen.availLeft);
|
||||
let maxX =
|
||||
screen.availLeft + screen.availWidth - document.documentElement.clientWidth;
|
||||
window.moveTo(Math.min(desiredX, maxX), 0);
|
||||
},
|
||||
handleEvent: function(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "mousedown":
|
||||
if (aEvent.button != 0 || aEvent.defaultPrevented)
|
||||
return;
|
||||
|
||||
this._startMouseX = aEvent.screenX;
|
||||
this._startWindowX = window.screenX;
|
||||
this._deltaX = this._startMouseX - this._startWindowX;
|
||||
|
||||
window.addEventListener("mousemove", this);
|
||||
window.addEventListener("mouseup", this);
|
||||
break;
|
||||
|
||||
case "mousemove":
|
||||
let moveOffset = Math.abs(aEvent.screenX - this._startMouseX);
|
||||
if (this._dragFullyStarted || moveOffset > this.threshold) {
|
||||
this.setXPosition(aEvent.screenX - this._deltaX);
|
||||
this._dragFullyStarted = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "mouseup":
|
||||
this._dragFullyStarted = false;
|
||||
window.removeEventListener("mousemove", this);
|
||||
window.removeEventListener("mouseup", this);
|
||||
this.positionCustomized =
|
||||
Math.abs(this._startWindowX - window.screenX) >= this.threshold;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -32,8 +32,8 @@ this.Translation = {
|
||||
|
||||
serviceUnavailable: false,
|
||||
|
||||
supportedSourceLanguages: ["zh", "de", "en", "fr", "ja", "ko", "pt", "ru", "es"],
|
||||
supportedTargetLanguages: ["zh", "de", "en", "fr", "ja", "ko", "pt", "ru", "es"],
|
||||
supportedSourceLanguages: ["de", "en", "es", "fr", "ja", "ko", "pt", "ru", "zh"],
|
||||
supportedTargetLanguages: ["de", "en", "es", "fr", "ja", "ko", "pl", "pt", "ru", "tr", "vi", "zh"],
|
||||
|
||||
_defaultTargetLanguage: "",
|
||||
get defaultTargetLanguage() {
|
||||
|
@ -166,12 +166,17 @@
|
||||
let bundle = Cc["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://global/locale/languageNames.properties");
|
||||
let sortByLocalizedName = function(aList) {
|
||||
return aList.map(code => [code, bundle.GetStringFromName(code)])
|
||||
.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
};
|
||||
|
||||
// Fill the lists of supported source languages.
|
||||
let detectedLanguage = this._getAnonElt("detectedLanguage");
|
||||
let fromLanguage = this._getAnonElt("fromLanguage");
|
||||
for (let code of Translation.supportedSourceLanguages) {
|
||||
let name = bundle.GetStringFromName(code);
|
||||
let sourceLanguages =
|
||||
sortByLocalizedName(Translation.supportedSourceLanguages);
|
||||
for (let [code, name] of sourceLanguages) {
|
||||
detectedLanguage.appendItem(name, code);
|
||||
fromLanguage.appendItem(name, code);
|
||||
}
|
||||
@ -183,8 +188,10 @@
|
||||
|
||||
// Fill the list of supported target languages.
|
||||
let toLanguage = this._getAnonElt("toLanguage");
|
||||
for (let code of Translation.supportedTargetLanguages)
|
||||
toLanguage.appendItem(bundle.GetStringFromName(code), code);
|
||||
let targetLanguages =
|
||||
sortByLocalizedName(Translation.supportedTargetLanguages);
|
||||
for (let [code, name] of targetLanguages)
|
||||
toLanguage.appendItem(name, code);
|
||||
|
||||
if (aTranslation.translatedTo)
|
||||
toLanguage.value = aTranslation.translatedTo;
|
||||
|
@ -88,6 +88,8 @@ HTMLBreadcrumbs.prototype = {
|
||||
|
||||
this.container.addEventListener("mousedown", this, true);
|
||||
this.container.addEventListener("keypress", this, true);
|
||||
this.container.addEventListener("mouseover", this, true);
|
||||
this.container.addEventListener("mouseleave", this, true);
|
||||
|
||||
// We will save a list of already displayed nodes in this array.
|
||||
this.nodeHierarchy = [];
|
||||
@ -373,6 +375,17 @@ HTMLBreadcrumbs.prototype = {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (event.type == "mouseover") {
|
||||
let target = event.originalTarget;
|
||||
if (target.tagName == "button") {
|
||||
target.onBreadcrumbsHover();
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == "mouseleave") {
|
||||
this.inspector.toolbox.highlighterUtils.unhighlight();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -392,6 +405,8 @@ HTMLBreadcrumbs.prototype = {
|
||||
this.empty();
|
||||
this.container.removeEventListener("mousedown", this, true);
|
||||
this.container.removeEventListener("keypress", this, true);
|
||||
this.container.removeEventListener("mouseover", this, true);
|
||||
this.container.removeEventListener("mouseleave", this, true);
|
||||
this.container = null;
|
||||
|
||||
this.separators.remove();
|
||||
@ -486,6 +501,10 @@ HTMLBreadcrumbs.prototype = {
|
||||
this.selection.setNodeFront(aNode, "breadcrumbs");
|
||||
};
|
||||
|
||||
button.onBreadcrumbsHover = () => {
|
||||
this.inspector.toolbox.highlighterUtils.highlightNodeFront(aNode);
|
||||
};
|
||||
|
||||
button.onclick = (function _onBreadcrumbsRightClick(event) {
|
||||
button.focus();
|
||||
if (event.button == 2) {
|
||||
|
@ -20,6 +20,7 @@ support-files =
|
||||
head.js
|
||||
|
||||
[browser_inspector_breadcrumbs.js]
|
||||
[browser_inspector_breadcrumbs_highlight_hover.js]
|
||||
[browser_inspector_delete-selected-node-01.js]
|
||||
[browser_inspector_delete-selected-node-02.js]
|
||||
[browser_inspector_delete-selected-node-03.js]
|
||||
|
@ -0,0 +1,32 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that hovering over nodes on the breadcrumb buttons in the inspector shows the highlighter over
|
||||
// those nodes
|
||||
let test = asyncTest(function*() {
|
||||
info("Loading the test document and opening the inspector");
|
||||
yield addTab("data:text/html;charset=utf-8,<h1>foo</h1><span>bar</span>");
|
||||
let {toolbox, inspector} = yield openInspector();
|
||||
info("Selecting the test node");
|
||||
yield selectNode("span", inspector);
|
||||
let bcButtons = inspector.breadcrumbs["container"];
|
||||
|
||||
let onNodeHighlighted = toolbox.once("node-highlight");
|
||||
let button = bcButtons.childNodes[1];
|
||||
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"}, button.ownerDocument.defaultView);
|
||||
yield onNodeHighlighted;
|
||||
ok(isHighlighting(), "The highlighter is shown on a markup container hover");
|
||||
is(getHighlitNode(), getNode("body"), "The highlighter highlights the right node");
|
||||
|
||||
let onNodeHighlighted = toolbox.once("node-highlight");
|
||||
let button = bcButtons.childNodes[2];
|
||||
EventUtils.synthesizeMouseAtCenter(button, {type: "mousemove"}, button.ownerDocument.defaultView);
|
||||
yield onNodeHighlighted;
|
||||
ok(isHighlighting(), "The highlighter is shown on a markup container hover");
|
||||
is(getHighlitNode(), getNode("span"), "The highlighter highlights the right node");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
@ -52,11 +52,11 @@ function consoleOpened(HUD) {
|
||||
WebConsoleUtils.usageCount = 0;
|
||||
is(WebConsoleUtils.usageCount, 0, "Test for usage count getter")
|
||||
// Input some commands to check if usage counting is working
|
||||
for(let i = 0; i <= 5; i++){
|
||||
for(let i = 0; i <= 3; i++){
|
||||
jsterm.setInputValue(i);
|
||||
jsterm.execute();
|
||||
}
|
||||
is(WebConsoleUtils.usageCount, 6, "Usage count incremented")
|
||||
is(WebConsoleUtils.usageCount, 4, "Usage count incremented")
|
||||
WebConsoleUtils.usageCount = 0;
|
||||
updateEditUIVisibility();
|
||||
|
||||
|
@ -513,8 +513,11 @@ function getGlobalIndicator() {
|
||||
let field = "_" + aName.toLowerCase();
|
||||
if (aState && !this[field]) {
|
||||
let menu = this._hiddenDoc.createElement("menu");
|
||||
let uri = "chrome://browser/skin/webRTC-" + aName.toLowerCase() + "-black-16.png";
|
||||
menu.setAttribute("image", uri);
|
||||
menu.setAttribute("id", "webRTC-sharing" + aName + "-menu");
|
||||
|
||||
// The CSS will only be applied if the menu is actually inserted in the DOM.
|
||||
this._hiddenDoc.documentElement.appendChild(menu);
|
||||
|
||||
this._statusBar.addItem(menu);
|
||||
|
||||
let menupopup = this._hiddenDoc.createElement("menupopup");
|
||||
@ -528,6 +531,7 @@ function getGlobalIndicator() {
|
||||
}
|
||||
else if (this[field] && !aState) {
|
||||
this._statusBar.removeItem(this[field]);
|
||||
this[field].remove();
|
||||
this[field] = null
|
||||
}
|
||||
},
|
||||
|
@ -70,10 +70,10 @@ browser.jar:
|
||||
skin/classic/browser/urlbar-arrow.png
|
||||
skin/classic/browser/webRTC-shareDevice-16.png
|
||||
skin/classic/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png)
|
||||
skin/classic/browser/webRTC-shareMicrophone-16.png
|
||||
skin/classic/browser/webRTC-shareMicrophone-64.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16.png (../shared/webrtc/webRTC-sharingMicrophone-16.png)
|
||||
skin/classic/browser/webRTC-shareScreen-16.png (../shared/webrtc/webRTC-shareScreen-16.png)
|
||||
skin/classic/browser/webRTC-shareScreen-64.png (../shared/webrtc/webRTC-shareScreen-64.png)
|
||||
skin/classic/browser/webRTC-sharingScreen-16.png (../shared/webrtc/webRTC-sharingScreen-16.png)
|
||||
|
Before Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 1.2 KiB |
@ -117,23 +117,27 @@ browser.jar:
|
||||
skin/classic/browser/webRTC-shareDevice-16@2x.png
|
||||
skin/classic/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/browser/webRTC-shareDevice-64@2x.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16@2x.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png)
|
||||
skin/classic/browser/webRTC-sharingDevice-16@2x.png (../shared/webrtc/webRTC-sharingDevice-16@2x.png)
|
||||
skin/classic/browser/webRTC-shareMicrophone-16.png
|
||||
skin/classic/browser/webRTC-shareMicrophone-16@2x.png
|
||||
skin/classic/browser/webRTC-shareMicrophone-64.png
|
||||
skin/classic/browser/webRTC-shareMicrophone-64@2x.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16@2x.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16.png (../shared/webrtc/webRTC-sharingMicrophone-16.png)
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16@2x.png (../shared/webrtc/webRTC-sharingMicrophone-16@2x.png)
|
||||
skin/classic/browser/webRTC-shareScreen-16.png (../shared/webrtc/webRTC-shareScreen-16.png)
|
||||
skin/classic/browser/webRTC-shareScreen-16@2x.png (../shared/webrtc/webRTC-shareScreen-16@2x.png)
|
||||
skin/classic/browser/webRTC-shareScreen-64.png (../shared/webrtc/webRTC-shareScreen-64.png)
|
||||
skin/classic/browser/webRTC-shareScreen-64@2x.png (../shared/webrtc/webRTC-shareScreen-64@2x.png)
|
||||
skin/classic/browser/webRTC-sharingScreen-16.png (../shared/webrtc/webRTC-sharingScreen-16.png)
|
||||
skin/classic/browser/webRTC-sharingScreen-16@2x.png (../shared/webrtc/webRTC-sharingScreen-16@2x.png)
|
||||
skin/classic/browser/webRTC-camera-black-16.png
|
||||
skin/classic/browser/webRTC-microphone-black-16.png
|
||||
skin/classic/browser/webRTC-screen-black-16.png
|
||||
skin/classic/browser/webRTC-sharingDevice-menubar.png
|
||||
skin/classic/browser/webRTC-sharingDevice-menubar@2x.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-menubar.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-menubar@2x.png
|
||||
skin/classic/browser/webRTC-sharingScreen-menubar.png
|
||||
skin/classic/browser/webRTC-sharingScreen-menubar@2x.png
|
||||
skin/classic/browser/webRTC-indicator.css
|
||||
skin/classic/browser/loop/toolbar.png (loop/toolbar.png)
|
||||
skin/classic/browser/loop/toolbar@2x.png (loop/toolbar@2x.png)
|
||||
skin/classic/browser/loop/toolbar-inverted.png (loop/toolbar-inverted.png)
|
||||
|
Before Width: | Height: | Size: 15 KiB |
35
browser/themes/osx/webRTC-indicator.css
Normal 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/. */
|
||||
|
||||
#webRTC-sharingCamera-menu {
|
||||
list-style-image: url("chrome://browser/skin/webRTC-sharingDevice-menubar.png");
|
||||
}
|
||||
|
||||
#webRTC-sharingMicrophone-menu {
|
||||
list-style-image: url("chrome://browser/skin/webRTC-sharingMicrophone-menubar.png");
|
||||
}
|
||||
|
||||
#webRTC-sharingScreen-menu {
|
||||
list-style-image: url("chrome://browser/skin/webRTC-sharingScreen-menubar.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#webRTC-sharingCamera-menu {
|
||||
list-style-image: url("chrome://browser/skin/webRTC-sharingDevice-menubar@2x.png");
|
||||
}
|
||||
|
||||
#webRTC-sharingMicrophone-menu {
|
||||
list-style-image: url("chrome://browser/skin/webRTC-sharingMicrophone-menubar@2x.png");
|
||||
}
|
||||
|
||||
#webRTC-sharingScreen-menu {
|
||||
list-style-image: url("chrome://browser/skin/webRTC-sharingScreen-menubar@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
#webRTC-sharingCamera-menu > menupopup,
|
||||
#webRTC-sharingMicrophone-menu > menupopup,
|
||||
#webRTC-sharingScreen-menu > menupopup {
|
||||
list-style-image: none; /* don't inherit into menu items */
|
||||
}
|
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 716 B |
BIN
browser/themes/osx/webRTC-sharingDevice-menubar.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
browser/themes/osx/webRTC-sharingDevice-menubar@2x.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.6 KiB |
BIN
browser/themes/osx/webRTC-sharingMicrophone-menubar.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
browser/themes/osx/webRTC-sharingMicrophone-menubar@2x.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
browser/themes/osx/webRTC-sharingScreen-menubar.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
browser/themes/osx/webRTC-sharingScreen-menubar@2x.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.8 KiB |
@ -109,3 +109,8 @@ window[sharingscreen][sharingvideo] > #shareSeparator,
|
||||
window[sharingscreen][sharingaudio] > #shareSeparator {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
:-moz-any(#audioVideoButton, #screenShareButton,
|
||||
#firefoxButton):-moz-focusring > .button-box {
|
||||
border: none;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
browser/themes/shared/webrtc/webRTC-sharingDevice-16.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
browser/themes/shared/webrtc/webRTC-sharingDevice-16@2x.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
browser/themes/shared/webrtc/webRTC-sharingMicrophone-16.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
browser/themes/shared/webrtc/webRTC-sharingMicrophone-16@2x.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 3.9 KiB |
@ -91,10 +91,10 @@ browser.jar:
|
||||
skin/classic/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png)
|
||||
skin/classic/browser/webRTC-shareDevice-16.png
|
||||
skin/classic/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png)
|
||||
skin/classic/browser/webRTC-shareMicrophone-16.png
|
||||
skin/classic/browser/webRTC-shareMicrophone-64.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16.png
|
||||
skin/classic/browser/webRTC-sharingMicrophone-16.png (../shared/webrtc/webRTC-sharingMicrophone-16.png)
|
||||
skin/classic/browser/webRTC-shareScreen-16.png (../shared/webrtc/webRTC-shareScreen-16.png)
|
||||
skin/classic/browser/webRTC-shareScreen-64.png (../shared/webrtc/webRTC-shareScreen-64.png)
|
||||
skin/classic/browser/webRTC-sharingScreen-16.png (../shared/webrtc/webRTC-sharingScreen-16.png)
|
||||
@ -510,10 +510,10 @@ browser.jar:
|
||||
skin/classic/aero/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png)
|
||||
skin/classic/aero/browser/webRTC-shareDevice-16.png
|
||||
skin/classic/aero/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/aero/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/aero/browser/webRTC-sharingDevice-16.png (../shared/webrtc/webRTC-sharingDevice-16.png)
|
||||
skin/classic/aero/browser/webRTC-shareMicrophone-16.png
|
||||
skin/classic/aero/browser/webRTC-shareMicrophone-64.png
|
||||
skin/classic/aero/browser/webRTC-sharingMicrophone-16.png
|
||||
skin/classic/aero/browser/webRTC-sharingMicrophone-16.png (../shared/webrtc/webRTC-sharingMicrophone-16.png)
|
||||
skin/classic/aero/browser/webRTC-shareScreen-16.png (../shared/webrtc/webRTC-shareScreen-16.png)
|
||||
skin/classic/aero/browser/webRTC-shareScreen-64.png (../shared/webrtc/webRTC-shareScreen-64.png)
|
||||
skin/classic/aero/browser/webRTC-sharingScreen-16.png (../shared/webrtc/webRTC-sharingScreen-16.png)
|
||||
|
Before Width: | Height: | Size: 404 B |
Before Width: | Height: | Size: 1.2 KiB |
@ -10,9 +10,21 @@ const Cc = Components.classes;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const kEntities = { "geolocation": "geolocation",
|
||||
"desktop-notification": "desktopNotification",
|
||||
"contacts": "contacts" };
|
||||
const kEntities = {
|
||||
"contacts": "contacts",
|
||||
"desktop-notification": "desktopNotification",
|
||||
"device-storage:music": "deviceStorageMusic",
|
||||
"device-storage:pictures": "deviceStoragePictures",
|
||||
"device-storage:sdcard": "deviceStorageSdcard",
|
||||
"device-storage:videos": "deviceStorageVideos",
|
||||
"geolocation": "geolocation",
|
||||
};
|
||||
|
||||
// For these types, prompt for permission if action is unknown.
|
||||
const PROMPT_FOR_UNKNOWN = [
|
||||
"desktop-notification",
|
||||
"geolocation",
|
||||
];
|
||||
|
||||
function ContentPermissionPrompt() {}
|
||||
|
||||
@ -21,18 +33,19 @@ ContentPermissionPrompt.prototype = {
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
|
||||
|
||||
handleExistingPermission: function handleExistingPermission(request, type, isApp) {
|
||||
handleExistingPermission: function handleExistingPermission(request, type, denyUnknown) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[type])) {
|
||||
if (denyUnknown && result == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
@ -71,7 +84,10 @@ ContentPermissionPrompt.prototype = {
|
||||
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
|
||||
|
||||
// Returns true if the request was handled
|
||||
if (this.handleExistingPermission(request, perm.type, isApp))
|
||||
let access = (perm.access && perm.access !== "unused") ?
|
||||
(perm.type + "-" + perm.access) : perm.type;
|
||||
if (this.handleExistingPermission(request, access,
|
||||
/* denyUnknown */ isApp || PROMPT_FOR_UNKNOWN.indexOf(perm.type) < 0))
|
||||
return;
|
||||
|
||||
let chromeWin = this.getChromeForRequest(request);
|
||||
|
@ -116,6 +116,35 @@ contacts.ask=Allow %S to access your contacts?
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
contacts.dontAskAgain=Don't ask again for this site
|
||||
|
||||
# Device Storage API
|
||||
deviceStorageMusic.allow=Allow
|
||||
deviceStorageMusic.dontAllow=Don't allow
|
||||
deviceStorageMusic.ask=Allow %S access to your music?
|
||||
# LOCALIZATION NOTE (deviceStorageMusic.dontAskAgain): This label appears next to a
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
deviceStorageMusic.dontAskAgain=Don't ask again for this site
|
||||
|
||||
deviceStoragePictures.allow=Allow
|
||||
deviceStoragePictures.dontAllow=Don't allow
|
||||
deviceStoragePictures.ask=Allow %S access to your images?
|
||||
# LOCALIZATION NOTE (deviceStoragePictures.dontAskAgain): This label appears next to a
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
deviceStoragePictures.dontAskAgain=Don't ask again for this site
|
||||
|
||||
deviceStorageSdcard.allow=Allow
|
||||
deviceStorageSdcard.dontAllow=Don't allow
|
||||
deviceStorageSdcard.ask=Allow %S access to external storage?
|
||||
# LOCALIZATION NOTE (deviceStorageSdcard.dontAskAgain): This label appears next to a
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
deviceStorageSdcard.dontAskAgain=Don't ask again for this site
|
||||
|
||||
deviceStorageVideos.allow=Allow
|
||||
deviceStorageVideos.dontAllow=Don't allow
|
||||
deviceStorageVideos.ask=Allow %S access to your videos?
|
||||
# LOCALIZATION NOTE (deviceStorageVideos.dontAskAgain): This label appears next to a
|
||||
# checkbox to indicate whether or not the user wants to make a permanent decision.
|
||||
deviceStorageVideos.dontAskAgain=Don't ask again for this site
|
||||
|
||||
# New Tab Popup
|
||||
# LOCALIZATION NOTE (newtabpopup, newprivatetabpopup): Semicolon-separated list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
|
@ -739,6 +739,12 @@ Database::InitSchema(bool* aDatabaseMigrated)
|
||||
|
||||
// Firefox 24 uses schema version 23.
|
||||
|
||||
if (currentSchemaVersion < 24) {
|
||||
rv = MigrateV24Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
// Firefox 34 uses schema version 24.
|
||||
|
||||
// Schema Upgrades must add migration code here.
|
||||
|
||||
rv = UpdateBookmarkRootTitles();
|
||||
@ -961,6 +967,13 @@ Database::InitTempTriggers()
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_TYPED_TRIGGER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_FOREIGNCOUNT_AFTERDELETE_TRIGGER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_FOREIGNCOUNT_AFTERINSERT_TRIGGER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mMainConn->ExecuteSimpleSQL(CREATE_FOREIGNCOUNT_AFTERUPDATE_TRIGGER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1915,6 +1928,36 @@ Database::MigrateV23Up()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::MigrateV24Up()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Add a foreign_count column to moz_places
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT foreign_count FROM moz_places"
|
||||
), getter_AddRefs(stmt));
|
||||
if (NS_FAILED(rv)) {
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"ALTER TABLE moz_places ADD COLUMN foreign_count INTEGER DEFAULT 0 NOT NULL"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Adjust counts for all the rows
|
||||
nsCOMPtr<mozIStorageStatement> updateStmt;
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places SET foreign_count = "
|
||||
"(SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id) "
|
||||
), getter_AddRefs(updateStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mozStorageStatementScoper updateScoper(updateStmt);
|
||||
rv = updateStmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Database::Shutdown()
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
// This is the schema version. Update it at any schema change and add a
|
||||
// corresponding migrateVxx method below.
|
||||
#define DATABASE_SCHEMA_VERSION 23
|
||||
#define DATABASE_SCHEMA_VERSION 24
|
||||
|
||||
// Fired after Places inited.
|
||||
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
|
||||
@ -276,6 +276,7 @@ protected:
|
||||
nsresult MigrateV21Up();
|
||||
nsresult MigrateV22Up();
|
||||
nsresult MigrateV23Up();
|
||||
nsresult MigrateV24Up();
|
||||
|
||||
nsresult UpdateBookmarkRootTitles();
|
||||
nsresult CheckAndUpdateGUIDs();
|
||||
|
@ -682,6 +682,12 @@ this.PlacesDBUtils = {
|
||||
")");
|
||||
cleanupStatements.push(fixRedirectsHidden);
|
||||
|
||||
// L.4 recalculate foreign_count.
|
||||
let fixForeignCount = DBConn.createAsyncStatement(
|
||||
"UPDATE moz_places SET foreign_count = " +
|
||||
"(SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id )");
|
||||
cleanupStatements.push(fixForeignCount);
|
||||
|
||||
// MAINTENANCE STATEMENTS SHOULD GO ABOVE THIS POINT!
|
||||
|
||||
return cleanupStatements;
|
||||
|
160
toolkit/components/places/PlacesSearchAutocompleteProvider.jsm
Normal file
@ -0,0 +1,160 @@
|
||||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Provides functions to handle search engine URLs in the browser history.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "PlacesSearchAutocompleteProvider" ];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified";
|
||||
|
||||
const SearchAutocompleteProviderInternal = {
|
||||
/**
|
||||
* Array of objects in the format returned by findMatchByToken.
|
||||
*/
|
||||
priorityMatches: null,
|
||||
|
||||
initialize: function () {
|
||||
return new Promise((resolve, reject) => {
|
||||
Services.search.init(status => {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
reject(new Error("Unable to initialize search service."));
|
||||
}
|
||||
|
||||
try {
|
||||
// The initial loading of the search engines must succeed.
|
||||
this._refresh();
|
||||
|
||||
Services.obs.addObserver(this, SEARCH_ENGINE_TOPIC, true);
|
||||
|
||||
this.initialized = true;
|
||||
resolve();
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
initialized: false,
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
switch (data) {
|
||||
case "engine-added":
|
||||
case "engine-changed":
|
||||
case "engine-removed":
|
||||
this._refresh();
|
||||
}
|
||||
},
|
||||
|
||||
_refresh: function () {
|
||||
this.priorityMatches = [];
|
||||
|
||||
// The search engines will always be processed in the order returned by the
|
||||
// search service, which can be defined by the user.
|
||||
Services.search.getVisibleEngines().forEach(e => this._addEngine(e));
|
||||
},
|
||||
|
||||
_addEngine: function (engine) {
|
||||
let token = engine.getResultDomain();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.priorityMatches.push({
|
||||
token: token,
|
||||
// The searchForm property returns a simple URL for the search engine, but
|
||||
// we may need an URL which includes an affiliate code (bug 990799).
|
||||
url: engine.searchForm,
|
||||
engineName: engine.name,
|
||||
iconUrl: engine.iconURI ? engine.iconURI.spec : null,
|
||||
});
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
}
|
||||
|
||||
let gInitializationPromise = null;
|
||||
|
||||
this.PlacesSearchAutocompleteProvider = Object.freeze({
|
||||
/**
|
||||
* Starts initializing the component and returns a promise that is resolved or
|
||||
* rejected when initialization finished. The same promise is returned if
|
||||
* this function is called multiple times.
|
||||
*/
|
||||
ensureInitialized: function () {
|
||||
if (!gInitializationPromise) {
|
||||
gInitializationPromise = SearchAutocompleteProviderInternal.initialize();
|
||||
}
|
||||
return gInitializationPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Matches a given string to an item that should be included by URL search
|
||||
* components, like autocomplete in the address bar.
|
||||
*
|
||||
* @param searchToken
|
||||
* String containing the first part of the matching domain name.
|
||||
*
|
||||
* @return An object with the following properties, or undefined if the token
|
||||
* does not match any relevant URL:
|
||||
* {
|
||||
* token: The full string used to match the search term to the URL.
|
||||
* url: The URL to navigate to if the match is selected.
|
||||
* engineName: The display name of the search engine.
|
||||
* iconUrl: Icon associated to the match, or null if not available.
|
||||
* }
|
||||
*/
|
||||
findMatchByToken: Task.async(function* (searchToken) {
|
||||
yield this.ensureInitialized();
|
||||
|
||||
// Match at the beginning for now. In the future, an "options" argument may
|
||||
// allow the matching behavior to be tuned.
|
||||
return SearchAutocompleteProviderInternal.priorityMatches
|
||||
.find(m => m.token.startsWith(searchToken));
|
||||
}),
|
||||
|
||||
/**
|
||||
* Synchronously determines if the provided URL represents results from a
|
||||
* search engine, and provides details about the match.
|
||||
*
|
||||
* @param url
|
||||
* String containing the URL to parse.
|
||||
*
|
||||
* @return An object with the following properties, or null if the URL does
|
||||
* not represent a search result:
|
||||
* {
|
||||
* engineName: The display name of the search engine.
|
||||
* terms: The originally sought terms extracted from the URI.
|
||||
* }
|
||||
*
|
||||
* @remarks The asynchronous ensureInitialized function must be called before
|
||||
* this synchronous method can be used.
|
||||
*
|
||||
* @note This API function needs to be synchronous because it is called inside
|
||||
* a row processing callback of Sqlite.jsm, in UnifiedComplete.js.
|
||||
*/
|
||||
parseSubmissionURL: function (url) {
|
||||
if (!SearchAutocompleteProviderInternal.initialized) {
|
||||
throw new Error("The component has not been initialized.");
|
||||
}
|
||||
|
||||
let parseUrlResult = Services.search.parseSubmissionURL(url);
|
||||
return parseUrlResult.engine && {
|
||||
engineName: parseUrlResult.engine.name,
|
||||
terms: parseUrlResult.terms,
|
||||
};
|
||||
},
|
||||
});
|
@ -1,142 +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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "PriorityUrlProvider" ];
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
|
||||
/**
|
||||
* Provides search engines matches to the PriorityUrlProvider through the
|
||||
* search engines definitions handled by the Search Service.
|
||||
*/
|
||||
const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified";
|
||||
|
||||
let SearchEnginesProvider = {
|
||||
init: function () {
|
||||
this._engines = new Map();
|
||||
let deferred = Promise.defer();
|
||||
Services.search.init(rv => {
|
||||
if (Components.isSuccessCode(rv)) {
|
||||
Services.search.getVisibleEngines().forEach(this._addEngine, this);
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(new Error("Unable to initialize search service."));
|
||||
}
|
||||
});
|
||||
Services.obs.addObserver(this, SEARCH_ENGINE_TOPIC, true);
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
observe: function (engine, topic, verb) {
|
||||
let engine = engine.QueryInterface(Ci.nsISearchEngine);
|
||||
switch (verb) {
|
||||
case "engine-added":
|
||||
this._addEngine(engine);
|
||||
break;
|
||||
case "engine-changed":
|
||||
if (engine.hidden) {
|
||||
this._removeEngine(engine);
|
||||
} else {
|
||||
this._addEngine(engine);
|
||||
}
|
||||
break;
|
||||
case "engine-removed":
|
||||
this._removeEngine(engine);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_addEngine: function (engine) {
|
||||
if (this._engines.has(engine.name)) {
|
||||
return;
|
||||
}
|
||||
let token = engine.getResultDomain();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
let match = { token: token,
|
||||
// TODO (bug 990799): searchForm should provide an usable
|
||||
// url with affiliate code, if available.
|
||||
url: engine.searchForm,
|
||||
title: engine.name,
|
||||
iconUrl: engine.iconURI ? engine.iconURI.spec : null,
|
||||
reason: "search" }
|
||||
this._engines.set(engine.name, match);
|
||||
PriorityUrlProvider.addMatch(match);
|
||||
},
|
||||
|
||||
_removeEngine: function (engine) {
|
||||
if (!this._engines.has(engine.name)) {
|
||||
return;
|
||||
}
|
||||
this._engines.delete(engine.name);
|
||||
PriorityUrlProvider.removeMatchByToken(engine.getResultDomain());
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
}
|
||||
|
||||
/**
|
||||
* The PriorityUrlProvider allows to match a given string to a list of
|
||||
* urls that should have priority in url search components, like autocomplete.
|
||||
* Each returned match is an object with the following properties:
|
||||
* - token: string used to match the search term to the url
|
||||
* - url: url string represented by the match
|
||||
* - title: title describing the match, or an empty string if not available
|
||||
* - iconUrl: url of the icon associated to the match, or null if not available
|
||||
* - reason: a string describing the origin of the match, for example if it
|
||||
* represents a search engine, it will be "search".
|
||||
*/
|
||||
let matches = new Map();
|
||||
|
||||
let initialized = false;
|
||||
function promiseInitialized() {
|
||||
if (initialized) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Task.spawn(function* () {
|
||||
try {
|
||||
yield SearchEnginesProvider.init();
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
initialized = true;
|
||||
});
|
||||
}
|
||||
|
||||
this.PriorityUrlProvider = Object.freeze({
|
||||
addMatch: function (match) {
|
||||
matches.set(match.token, match);
|
||||
},
|
||||
|
||||
removeMatchByToken: function (token) {
|
||||
matches.delete(token);
|
||||
},
|
||||
|
||||
getMatch: function (searchToken) {
|
||||
return Task.spawn(function* () {
|
||||
yield promiseInitialized();
|
||||
for (let [token, match] of matches.entries()) {
|
||||
// Match at the beginning for now. In future an aOptions argument may
|
||||
// allow to control the matching behavior.
|
||||
if (token.startsWith(searchToken)) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}.bind(this));
|
||||
}
|
||||
});
|
@ -22,25 +22,25 @@ const DEFAULT_BEHAVIOR = 0;
|
||||
const PREF_BRANCH = "browser.urlbar.";
|
||||
|
||||
// Prefs are defined as [pref name, default value].
|
||||
const PREF_ENABLED = [ "autocomplete.enabled", true ];
|
||||
const PREF_AUTOFILL = [ "autoFill", true ];
|
||||
const PREF_AUTOFILL_TYPED = [ "autoFill.typed", true ];
|
||||
const PREF_AUTOFILL_PRIORITY = [ "autoFill.priority", true ];
|
||||
const PREF_DELAY = [ "delay", 50 ];
|
||||
const PREF_BEHAVIOR = [ "matchBehavior", MATCH_BOUNDARY_ANYWHERE ];
|
||||
const PREF_DEFAULT_BEHAVIOR = [ "default.behavior", DEFAULT_BEHAVIOR ];
|
||||
const PREF_EMPTY_BEHAVIOR = [ "default.behavior.emptyRestriction",
|
||||
Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
|
||||
Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED ];
|
||||
const PREF_FILTER_JS = [ "filter.javascript", true ];
|
||||
const PREF_MAXRESULTS = [ "maxRichResults", 25 ];
|
||||
const PREF_RESTRICT_HISTORY = [ "restrict.history", "^" ];
|
||||
const PREF_RESTRICT_BOOKMARKS = [ "restrict.bookmark", "*" ];
|
||||
const PREF_RESTRICT_TYPED = [ "restrict.typed", "~" ];
|
||||
const PREF_RESTRICT_TAG = [ "restrict.tag", "+" ];
|
||||
const PREF_RESTRICT_SWITCHTAB = [ "restrict.openpage", "%" ];
|
||||
const PREF_MATCH_TITLE = [ "match.title", "#" ];
|
||||
const PREF_MATCH_URL = [ "match.url", "@" ];
|
||||
const PREF_ENABLED = [ "autocomplete.enabled", true ];
|
||||
const PREF_AUTOFILL = [ "autoFill", true ];
|
||||
const PREF_AUTOFILL_TYPED = [ "autoFill.typed", true ];
|
||||
const PREF_AUTOFILL_SEARCHENGINES = [ "autoFill.searchEngines", true ];
|
||||
const PREF_DELAY = [ "delay", 50 ];
|
||||
const PREF_BEHAVIOR = [ "matchBehavior", MATCH_BOUNDARY_ANYWHERE ];
|
||||
const PREF_DEFAULT_BEHAVIOR = [ "default.behavior", DEFAULT_BEHAVIOR ];
|
||||
const PREF_EMPTY_BEHAVIOR = [ "default.behavior.emptyRestriction",
|
||||
Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
|
||||
Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED ];
|
||||
const PREF_FILTER_JS = [ "filter.javascript", true ];
|
||||
const PREF_MAXRESULTS = [ "maxRichResults", 25 ];
|
||||
const PREF_RESTRICT_HISTORY = [ "restrict.history", "^" ];
|
||||
const PREF_RESTRICT_BOOKMARKS = [ "restrict.bookmark", "*" ];
|
||||
const PREF_RESTRICT_TYPED = [ "restrict.typed", "~" ];
|
||||
const PREF_RESTRICT_TAG = [ "restrict.tag", "+" ];
|
||||
const PREF_RESTRICT_SWITCHTAB = [ "restrict.openpage", "%" ];
|
||||
const PREF_MATCH_TITLE = [ "match.title", "#" ];
|
||||
const PREF_MATCH_URL = [ "match.url", "@" ];
|
||||
|
||||
// Match type constants.
|
||||
// These indicate what type of search function we should be using.
|
||||
@ -62,11 +62,14 @@ const QUERYTYPE_AUTOFILL_URL = 3;
|
||||
// "comment" back into the title and the tag.
|
||||
const TITLE_TAGS_SEPARATOR = " \u2013 ";
|
||||
|
||||
// This separator identifies the search engine name in the title.
|
||||
const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
|
||||
|
||||
// Telemetry probes.
|
||||
const TELEMETRY_1ST_RESULT = "PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS";
|
||||
|
||||
// The default frecency value used when inserting priority results.
|
||||
const FRECENCY_PRIORITY_DEFAULT = 1000;
|
||||
// The default frecency value used when inserting search engine results.
|
||||
const FRECENCY_SEARCHENGINES_DEFAULT = 1000;
|
||||
|
||||
// Sqlite result row index constants.
|
||||
const QUERYINDEX_QUERYTYPE = 0;
|
||||
@ -198,10 +201,30 @@ const SQL_HOST_QUERY = sql(
|
||||
|
||||
const SQL_TYPED_HOST_QUERY = SQL_HOST_QUERY.replace("/*CONDITIONS*/",
|
||||
"AND typed = 1");
|
||||
|
||||
const SQL_BOOKMARKED_HOST_QUERY = sql(
|
||||
"/* do not warn (bug NA): not worth to index on (typed, frecency) */",
|
||||
"SELECT :query_type, host || '/', IFNULL(prefix, '') || host || '/',",
|
||||
"NULL, (",
|
||||
"SELECT foreign_count > 0 FROM moz_places ",
|
||||
"WHERE rev_host = get_unreversed_host(host || '.') || '.'",
|
||||
"OR rev_host = get_unreversed_host(host || '.') || '.www.'",
|
||||
") AS bookmarked, NULL, NULL, NULL, NULL, NULL, NULL, frecency",
|
||||
"FROM moz_hosts",
|
||||
"WHERE host BETWEEN :searchString AND :searchString || X'FFFF'",
|
||||
"AND bookmarked",
|
||||
"AND frecency <> 0",
|
||||
"/*CONDITIONS*/",
|
||||
"ORDER BY frecency DESC",
|
||||
"LIMIT 1");
|
||||
|
||||
const SQL_BOOKMARKED_TYPED_HOST_QUERY =
|
||||
SQL_BOOKMARKED_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, NULL,",
|
||||
"NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, h.frecency",
|
||||
"NULL, foreign_count > 0 AS bookmarked, NULL, NULL, NULL, NULL, NULL, NULL, h.frecency",
|
||||
"FROM moz_places h",
|
||||
"WHERE h.frecency <> 0",
|
||||
"/*CONDITIONS*/",
|
||||
@ -215,6 +238,13 @@ const SQL_URL_QUERY = sql(
|
||||
const SQL_TYPED_URL_QUERY = SQL_URL_QUERY.replace("/*CONDITIONS*/",
|
||||
"AND typed = 1");
|
||||
|
||||
// TODO (bug 1045924): use foreign_count once available.
|
||||
const SQL_BOOKMARKED_URL_QUERY =
|
||||
SQL_URL_QUERY.replace("/*CONDITIONS*/", "AND bookmarked");
|
||||
|
||||
const SQL_BOOKMARKED_TYPED_URL_QUERY =
|
||||
SQL_URL_QUERY.replace("/*CONDITIONS*/", "AND bookmarked AND typed = 1");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Getters
|
||||
|
||||
@ -237,8 +267,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PriorityUrlProvider",
|
||||
"resource://gre/modules/PriorityUrlProvider.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesSearchAutocompleteProvider",
|
||||
"resource://gre/modules/PlacesSearchAutocompleteProvider.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "textURIService",
|
||||
"@mozilla.org/intl/texttosuburi;1",
|
||||
@ -332,7 +362,7 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
|
||||
store.enabled = prefs.get(...PREF_ENABLED);
|
||||
store.autofill = prefs.get(...PREF_AUTOFILL);
|
||||
store.autofillTyped = prefs.get(...PREF_AUTOFILL_TYPED);
|
||||
store.autofillPriority = prefs.get(...PREF_AUTOFILL_PRIORITY);
|
||||
store.autofillSearchEngines = prefs.get(...PREF_AUTOFILL_SEARCHENGINES);
|
||||
store.delay = prefs.get(...PREF_DELAY);
|
||||
store.matchBehavior = prefs.get(...PREF_BEHAVIOR);
|
||||
store.filterJavaScript = prefs.get(...PREF_FILTER_JS);
|
||||
@ -602,8 +632,12 @@ Search.prototype = {
|
||||
this._pendingQuery = true;
|
||||
TelemetryStopwatch.start(TELEMETRY_1ST_RESULT);
|
||||
|
||||
// Since we call the synchronous parseSubmissionURL function later, we must
|
||||
// wait for the initialization of PlacesSearchAutocompleteProvider first.
|
||||
yield PlacesSearchAutocompleteProvider.ensureInitialized();
|
||||
|
||||
// For any given search, we run many queries:
|
||||
// 1) priority domains
|
||||
// 1) search engine domains
|
||||
// 2) inline completion
|
||||
// 3) keywords (this._keywordQuery)
|
||||
// 4) adaptive learning (this._adaptiveQuery)
|
||||
@ -622,7 +656,7 @@ Search.prototype = {
|
||||
PlacesUtils.bookmarks.getURIForKeyword(this._searchTokens[0])) {
|
||||
queries.unshift(this._keywordQuery);
|
||||
} else if (this._searchTokens.length == 1) {
|
||||
yield this._matchPriorityUrl();
|
||||
yield this._matchSearchEngineUrl();
|
||||
}
|
||||
|
||||
if (this._shouldAutofill) {
|
||||
@ -673,19 +707,21 @@ Search.prototype = {
|
||||
}
|
||||
}),
|
||||
|
||||
_matchPriorityUrl: function* () {
|
||||
if (!Prefs.autofillPriority)
|
||||
_matchSearchEngineUrl: function* () {
|
||||
if (!Prefs.autofillSearchEngines)
|
||||
return;
|
||||
let priorityMatch = yield PriorityUrlProvider.getMatch(this._searchString);
|
||||
if (priorityMatch) {
|
||||
|
||||
let match = yield PlacesSearchAutocompleteProvider.findMatchByToken(
|
||||
this._searchString);
|
||||
if (match) {
|
||||
this._result.setDefaultIndex(0);
|
||||
this._addFrecencyMatch({
|
||||
value: priorityMatch.token,
|
||||
comment: priorityMatch.title,
|
||||
icon: priorityMatch.iconUrl,
|
||||
style: "priority-" + priorityMatch.reason,
|
||||
finalCompleteValue: priorityMatch.url,
|
||||
frecency: FRECENCY_PRIORITY_DEFAULT
|
||||
value: match.token,
|
||||
comment: match.engineName,
|
||||
icon: match.iconUrl,
|
||||
style: "priority-search",
|
||||
finalCompleteValue: match.url,
|
||||
frecency: FRECENCY_SEARCHENGINES_DEFAULT
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -724,6 +760,30 @@ Search.prototype = {
|
||||
this._frecencyMatches.sort((a, b) => a.frecency - b.frecency);
|
||||
},
|
||||
|
||||
_maybeRestyleSearchMatch: function (match) {
|
||||
// Return if the URL does not represent a search result.
|
||||
let parseResult =
|
||||
PlacesSearchAutocompleteProvider.parseSubmissionURL(match.value);
|
||||
if (!parseResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not apply the special style if the user is doing a search from the
|
||||
// location bar but the entered terms match an irrelevant portion of the
|
||||
// URL. For example, "https://www.google.com/search?q=terms&client=firefox"
|
||||
// when searching for "Firefox".
|
||||
let terms = parseResult.terms.toLowerCase();
|
||||
if (this._searchTokens.length > 0 &&
|
||||
this._searchTokens.every(token => terms.indexOf(token) == -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the special separator that the binding will use to style the item.
|
||||
match.style = "search " + match.style;
|
||||
match.comment = parseResult.terms + TITLE_SEARCH_ENGINE_SEPARATOR +
|
||||
parseResult.engineName;
|
||||
},
|
||||
|
||||
_addMatch: function (match) {
|
||||
let notifyResults = false;
|
||||
|
||||
@ -749,10 +809,19 @@ Search.prototype = {
|
||||
this._usedPlaceIds.add(match.placeId);
|
||||
this._usedURLs.add(urlMapKey);
|
||||
|
||||
if (!match.style) {
|
||||
match.style = "favicon";
|
||||
}
|
||||
|
||||
// Restyle past searches, unless they are bookmarks or special results.
|
||||
if (match.style == "favicon") {
|
||||
this._maybeRestyleSearchMatch(match);
|
||||
}
|
||||
|
||||
this._result.appendMatch(match.value,
|
||||
match.comment,
|
||||
match.icon || PlacesUtils.favicons.defaultFavicon.spec,
|
||||
match.style || "favicon",
|
||||
match.style,
|
||||
match.finalCompleteValue);
|
||||
notifyResults = true;
|
||||
}
|
||||
@ -784,7 +853,8 @@ Search.prototype = {
|
||||
}
|
||||
|
||||
match.value = this._strippedPrefix + trimmedHost;
|
||||
match.comment = trimmedHost;
|
||||
// Remove the trailing slash.
|
||||
match.comment = stripHttpAndTrim(trimmedHost);
|
||||
match.finalCompleteValue = untrimmedHost;
|
||||
match.frecency = frecency;
|
||||
return match;
|
||||
@ -1020,8 +1090,18 @@ Search.prototype = {
|
||||
// Then, we should not try to autofill if the behavior is not the default.
|
||||
// TODO (bug 751709): Ideally we should have a more fine-grained behavior
|
||||
// here, but for now it's enough to just check for default behavior.
|
||||
if (Prefs.defaultBehavior != DEFAULT_BEHAVIOR)
|
||||
return false;
|
||||
if (Prefs.defaultBehavior != DEFAULT_BEHAVIOR) {
|
||||
// autoFill can only cope with history or bookmarks entries
|
||||
// (typed or not).
|
||||
if (!this.hasBehavior("typed") &&
|
||||
!this.hasBehavior("history") &&
|
||||
!this.hasBehavior("bookmark"))
|
||||
return false;
|
||||
|
||||
// autoFill doesn't search titles or tags.
|
||||
if (this.hasBehavior("title") || this.hasBehavior("tags"))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't try to autofill if the search term includes any whitespace.
|
||||
// This may confuse completeDefaultIndex cause the AUTOCOMPLETE_MATCH
|
||||
@ -1049,13 +1129,21 @@ Search.prototype = {
|
||||
* @return an array consisting of the correctly optimized query to search the
|
||||
* database with and an object containing the params to bound.
|
||||
*/
|
||||
get _hostQuery() [
|
||||
Prefs.autofillTyped ? SQL_TYPED_HOST_QUERY : SQL_HOST_QUERY,
|
||||
{
|
||||
query_type: QUERYTYPE_AUTOFILL_HOST,
|
||||
searchString: this._searchString.toLowerCase()
|
||||
}
|
||||
],
|
||||
get _hostQuery() {
|
||||
let typed = Prefs.autofillTyped || this.hasBehavior("typed");
|
||||
let bookmarked = this.hasBehavior("bookmark");
|
||||
|
||||
return [
|
||||
bookmarked ? typed ? SQL_BOOKMARKED_TYPED_HOST_QUERY
|
||||
: SQL_BOOKMARKED_HOST_QUERY
|
||||
: typed ? SQL_TYPED_HOST_QUERY
|
||||
: SQL_HOST_QUERY,
|
||||
{
|
||||
query_type: QUERYTYPE_AUTOFILL_HOST,
|
||||
searchString: this._searchString.toLowerCase()
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Obtains the query to search for autoFill url results.
|
||||
@ -1063,15 +1151,23 @@ Search.prototype = {
|
||||
* @return an array consisting of the correctly optimized query to search the
|
||||
* database with and an object containing the params to bound.
|
||||
*/
|
||||
get _urlQuery() [
|
||||
Prefs.autofillTyped ? SQL_TYPED_URL_QUERY : SQL_URL_QUERY,
|
||||
{
|
||||
query_type: QUERYTYPE_AUTOFILL_URL,
|
||||
searchString: this._autofillUrlSearchString,
|
||||
matchBehavior: MATCH_BEGINNING_CASE_SENSITIVE,
|
||||
searchBehavior: Ci.mozIPlacesAutoComplete.BEHAVIOR_URL
|
||||
}
|
||||
],
|
||||
get _urlQuery() {
|
||||
let typed = Prefs.autofillTyped || this.hasBehavior("typed");
|
||||
let bookmarked = this.hasBehavior("bookmark");
|
||||
|
||||
return [
|
||||
bookmarked ? typed ? SQL_BOOKMARKED_TYPED_URL_QUERY
|
||||
: SQL_BOOKMARKED_URL_QUERY
|
||||
: typed ? SQL_TYPED_URL_QUERY
|
||||
: SQL_URL_QUERY,
|
||||
{
|
||||
query_type: QUERYTYPE_AUTOFILL_URL,
|
||||
searchString: this._autofillUrlSearchString,
|
||||
matchBehavior: MATCH_BEGINNING_CASE_SENSITIVE,
|
||||
searchBehavior: Ci.mozIPlacesAutoComplete.BEHAVIOR_URL
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* Notifies the listener about results.
|
||||
|
@ -67,8 +67,8 @@ if CONFIG['MOZ_PLACES']:
|
||||
'ColorConversion.js',
|
||||
'PlacesBackups.jsm',
|
||||
'PlacesDBUtils.jsm',
|
||||
'PlacesSearchAutocompleteProvider.jsm',
|
||||
'PlacesTransactions.jsm',
|
||||
'PriorityUrlProvider.jsm'
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
|
@ -206,10 +206,9 @@ const EXPIRATION_QUERIES = {
|
||||
+ "SELECT h.id, h.url, h.guid, h.last_visit_date, :limit_uris "
|
||||
+ "FROM moz_places h "
|
||||
+ "LEFT JOIN moz_historyvisits v ON h.id = v.place_id "
|
||||
+ "LEFT JOIN moz_bookmarks b ON h.id = b.fk "
|
||||
+ "WHERE h.last_visit_date IS NULL "
|
||||
+ "AND h.foreign_count = 0 "
|
||||
+ "AND v.id IS NULL "
|
||||
+ "AND b.id IS NULL "
|
||||
+ "AND frecency <> -1 "
|
||||
+ "LIMIT :limit_uris",
|
||||
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
|
||||
@ -231,10 +230,9 @@ const EXPIRATION_QUERIES = {
|
||||
+ "SELECT h.id "
|
||||
+ "FROM moz_places h "
|
||||
+ "LEFT JOIN moz_historyvisits v ON h.id = v.place_id "
|
||||
+ "LEFT JOIN moz_bookmarks b ON h.id = b.fk "
|
||||
+ "WHERE h.last_visit_date IS NULL "
|
||||
+ "AND h.foreign_count = 0 "
|
||||
+ "AND v.id IS NULL "
|
||||
+ "AND b.id IS NULL "
|
||||
+ "LIMIT :limit_uris "
|
||||
+ ")",
|
||||
actions: ACTION.CLEAR_HISTORY
|
||||
|
@ -21,11 +21,9 @@
|
||||
", frecency INTEGER DEFAULT -1 NOT NULL" \
|
||||
", last_visit_date INTEGER " \
|
||||
", guid TEXT" \
|
||||
", foreign_count INTEGER DEFAULT 0 NOT NULL" \
|
||||
")" \
|
||||
)
|
||||
#define MOZ_PLACES_COLUMNS \
|
||||
"id, url, title, rev_host, visit_count, hidden, typed, favicon_id, " \
|
||||
"frecency, last_visit_date"
|
||||
|
||||
#define CREATE_MOZ_HISTORYVISITS NS_LITERAL_CSTRING( \
|
||||
"CREATE TABLE moz_historyvisits (" \
|
||||
@ -37,8 +35,6 @@
|
||||
", session INTEGER" \
|
||||
")" \
|
||||
)
|
||||
#define MOZ_HISTORYVISITS_COLUMNS \
|
||||
"id, from_visit, place_id, visit_date, visit_type, session"
|
||||
|
||||
|
||||
#define CREATE_MOZ_INPUTHISTORY NS_LITERAL_CSTRING( \
|
||||
|
@ -175,4 +175,36 @@
|
||||
"END" \
|
||||
)
|
||||
|
||||
#define CREATE_FOREIGNCOUNT_AFTERDELETE_TRIGGER NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMP TRIGGER moz_bookmarks_foreign_count_afterdelete_trigger " \
|
||||
"AFTER DELETE ON moz_bookmarks FOR EACH ROW " \
|
||||
"BEGIN " \
|
||||
"UPDATE moz_places " \
|
||||
"SET foreign_count = foreign_count - 1 " \
|
||||
"WHERE id = OLD.fk;" \
|
||||
"END" \
|
||||
)
|
||||
|
||||
#define CREATE_FOREIGNCOUNT_AFTERINSERT_TRIGGER NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMP TRIGGER moz_bookmarks_foreign_count_afterinsert_trigger " \
|
||||
"AFTER INSERT ON moz_bookmarks FOR EACH ROW " \
|
||||
"BEGIN " \
|
||||
"UPDATE moz_places " \
|
||||
"SET foreign_count = foreign_count + 1 " \
|
||||
"WHERE id = NEW.fk;" \
|
||||
"END" \
|
||||
)
|
||||
|
||||
#define CREATE_FOREIGNCOUNT_AFTERUPDATE_TRIGGER NS_LITERAL_CSTRING( \
|
||||
"CREATE TEMP TRIGGER moz_bookmarks_foreign_count_afterupdate_trigger " \
|
||||
"AFTER UPDATE OF fk ON moz_bookmarks FOR EACH ROW " \
|
||||
"BEGIN " \
|
||||
"UPDATE moz_places " \
|
||||
"SET foreign_count = foreign_count + 1 " \
|
||||
"WHERE id = NEW.fk;" \
|
||||
"UPDATE moz_places " \
|
||||
"SET foreign_count = foreign_count - 1 " \
|
||||
"WHERE id = OLD.fk;" \
|
||||
"END" \
|
||||
)
|
||||
#endif // __nsPlacesTriggers_h__
|
||||
|
@ -0,0 +1,109 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
|
||||
|
||||
/* Bug 1017502 - Add a foreign_count column to moz_places
|
||||
This tests, tests the triggers that adjust the foreign_count when a bookmark is
|
||||
added or removed and also the maintenance task to fix wrong counts.
|
||||
*/
|
||||
|
||||
const T_URI = NetUtil.newURI("https://www.mozilla.org/firefox/nightly/firstrun/");
|
||||
|
||||
function* getForeignCountForURL(conn, url){
|
||||
let url = url instanceof Ci.nsIURI ? url.spec : url;
|
||||
let rows = yield conn.executeCached(
|
||||
"SELECT foreign_count FROM moz_places WHERE url = :t_url ", { t_url: url });
|
||||
return rows[0].getResultByName("foreign_count");
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* add_remove_change_bookmark_test() {
|
||||
let conn = yield PlacesUtils.promiseDBConnection();
|
||||
|
||||
// Simulate a visit to the url
|
||||
yield promiseAddVisits(T_URI);
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 0);
|
||||
|
||||
// Add 1st bookmark which should increment foreign_count by 1
|
||||
let id1 = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
T_URI, PlacesUtils.bookmarks.DEFAULT_INDEX, "First Run");
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 1);
|
||||
|
||||
// Add 2nd bookmark
|
||||
let id2 = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarksMenuFolderId,
|
||||
T_URI, PlacesUtils.bookmarks.DEFAULT_INDEX, "First Run");
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 2);
|
||||
|
||||
// Remove 2nd bookmark which should decrement foreign_count by 1
|
||||
PlacesUtils.bookmarks.removeItem(id2);
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 1);
|
||||
|
||||
// Change first bookmark's URI
|
||||
const URI2 = NetUtil.newURI("http://www.mozilla.org");
|
||||
PlacesUtils.bookmarks.changeBookmarkURI(id1, URI2);
|
||||
// Check foreign count for original URI
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 0);
|
||||
// Check foreign count for new URI
|
||||
Assert.equal((yield getForeignCountForURL(conn, URI2)), 1);
|
||||
|
||||
// Cleanup - Remove changed bookmark
|
||||
let id = PlacesUtils.bookmarks.getBookmarkIdsForURI(URI2);
|
||||
PlacesUtils.bookmarks.removeItem(id);
|
||||
Assert.equal((yield getForeignCountForURL(conn, URI2)), 0);
|
||||
|
||||
});
|
||||
|
||||
add_task(function* maintenance_foreign_count_test() {
|
||||
let conn = yield PlacesUtils.promiseDBConnection();
|
||||
|
||||
// Simulate a visit to the url
|
||||
yield promiseAddVisits(T_URI);
|
||||
|
||||
// Adjust the foreign_count for the added entry to an incorrect value
|
||||
let deferred = Promise.defer();
|
||||
let stmt = DBConn().createAsyncStatement(
|
||||
"UPDATE moz_places SET foreign_count = 10 WHERE url = :t_url ");
|
||||
stmt.params.t_url = T_URI.spec;
|
||||
stmt.executeAsync({
|
||||
handleCompletion: function(){
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
stmt.finalize();
|
||||
yield deferred.promise;
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 10);
|
||||
|
||||
// Run maintenance
|
||||
Components.utils.import("resource://gre/modules/PlacesDBUtils.jsm");
|
||||
let promiseMaintenanceFinished =
|
||||
promiseTopicObserved("places-maintenance-finished");
|
||||
PlacesDBUtils.maintenanceOnIdle();
|
||||
yield promiseMaintenanceFinished;
|
||||
|
||||
// Check if the foreign_count has been adjusted to the correct value
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 0);
|
||||
});
|
||||
|
||||
add_task(function* add_remove_tags_test(){
|
||||
let conn = yield PlacesUtils.promiseDBConnection();
|
||||
|
||||
yield promiseAddVisits(T_URI);
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 0);
|
||||
|
||||
// Check foreign count incremented by 1 for a single tag
|
||||
PlacesUtils.tagging.tagURI(T_URI, ["test tag"]);
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 1);
|
||||
|
||||
// Check foreign count is incremented by 2 for two tags
|
||||
PlacesUtils.tagging.tagURI(T_URI, ["one", "two"]);
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 3);
|
||||
|
||||
// Check foreign count is set to 0 when all tags are removed
|
||||
PlacesUtils.tagging.untagURI(T_URI, ["test tag", "one", "two"]);
|
||||
Assert.equal((yield getForeignCountForURL(conn, T_URI)), 0);
|
||||
});
|
@ -34,3 +34,4 @@ tail =
|
||||
[test_992901-backup-unsorted-hierarchy.js]
|
||||
[test_997030-bookmarks-html-encode.js]
|
||||
[test_1016953-renaming-uncompressed.js]
|
||||
[test_1017502-bookmarks_foreign_count.js]
|
@ -3,7 +3,7 @@
|
||||
* 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 CURRENT_SCHEMA_VERSION = 23;
|
||||
const CURRENT_SCHEMA_VERSION = 24;
|
||||
|
||||
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
|
||||
const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
|
||||
|
@ -17,6 +17,8 @@ let (commonFile = do_get_file("../head_common.js", false)) {
|
||||
|
||||
// Put any other stuff relative to this test folder below.
|
||||
|
||||
const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
@ -147,9 +149,11 @@ function* check_autocomplete(test) {
|
||||
if (matches[j] == undefined)
|
||||
continue;
|
||||
|
||||
let { uri, title, tags } = matches[j];
|
||||
let { uri, title, tags, searchEngine } = matches[j];
|
||||
if (tags)
|
||||
title += " \u2013 " + tags.sort().join(", ");
|
||||
if (searchEngine)
|
||||
title += TITLE_SEARCH_ENGINE_SEPARATOR + searchEngine;
|
||||
|
||||
do_log_info("Checking against expected '" + uri.spec + "', '" + title + "'...");
|
||||
// Got a match on both uri and title?
|
||||
|
@ -0,0 +1,269 @@
|
||||
/* 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 autoFill for different default behaviors.
|
||||
*/
|
||||
|
||||
add_task(function* test_default_behavior_host() {
|
||||
let uri1 = NetUtil.newURI("http://typed/");
|
||||
let uri2 = NetUtil.newURI("http://visited/");
|
||||
let uri3 = NetUtil.newURI("http://bookmarked/");
|
||||
let uri4 = NetUtil.newURI("http://tpbk/");
|
||||
|
||||
yield promiseAddVisits([
|
||||
{ uri: uri1, title: "typed", transition: TRANSITION_TYPED },
|
||||
{ uri: uri2, title: "visited" },
|
||||
{ uri: uri4, title: "tpbk", transition: TRANSITION_TYPED },
|
||||
]);
|
||||
addBookmark( { uri: uri3, title: "bookmarked" } );
|
||||
addBookmark( { uri: uri4, title: "tpbk" } );
|
||||
|
||||
// RESTRICT TO HISTORY.
|
||||
Services.prefs.setIntPref("browser.urlbar.default.behavior", 1);
|
||||
|
||||
do_log_info("Restrict history, common visit, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "vi",
|
||||
matches: [ { uri: uri2, title: "visited" } ],
|
||||
autofilled: "vi",
|
||||
completed: "vi"
|
||||
});
|
||||
|
||||
do_log_info("Restrict history, typed visit, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "ty",
|
||||
matches: [ { uri: uri1, title: "typed" } ],
|
||||
autofilled: "typed/",
|
||||
completed: "typed/"
|
||||
});
|
||||
|
||||
// Don't autoFill this one cause it's not typed.
|
||||
do_log_info("Restrict history, bookmark, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bo",
|
||||
matches: [ ],
|
||||
autofilled: "bo",
|
||||
completed: "bo"
|
||||
});
|
||||
|
||||
// Note we don't show this one cause it's not typed.
|
||||
do_log_info("Restrict history, typed bookmark, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "tp",
|
||||
matches: [ { uri: uri4, title: "tpbk" } ],
|
||||
autofilled: "tpbk/",
|
||||
completed: "tpbk/"
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
|
||||
|
||||
// We are not restricting on typed, so we autoFill the bookmark even if we
|
||||
// are restricted to history. We accept that cause not doing that
|
||||
// would be a perf hit and the privacy implications are very weak.
|
||||
do_log_info("Restrict history, bookmark, autoFill.typed = false, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bo",
|
||||
matches: [ { uri: uri3, title: "bookmarked" } ],
|
||||
autofilled: "bookmarked/",
|
||||
completed: "bookmarked/"
|
||||
});
|
||||
|
||||
do_log_info("Restrict history, common visit, autoFill.typed = false, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "vi",
|
||||
matches: [ { uri: uri2, title: "visited" } ],
|
||||
autofilled: "visited/",
|
||||
completed: "visited/"
|
||||
});
|
||||
|
||||
// RESTRICT TO TYPED.
|
||||
// This should basically ignore autoFill.typed and acts as if it would be set.
|
||||
Services.prefs.setIntPref("browser.urlbar.default.behavior", 32);
|
||||
|
||||
// Typed behavior basically acts like history, but filters on typed.
|
||||
do_log_info("Restrict typed, common visit, autoFill.typed = false, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "vi",
|
||||
matches: [ ],
|
||||
autofilled: "vi",
|
||||
completed: "vi"
|
||||
});
|
||||
|
||||
do_log_info("Restrict typed, typed visit, autofill.typed = false, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "ty",
|
||||
matches: [ { uri: uri1, title: "typed" } ],
|
||||
autofilled: "typed/",
|
||||
completed: "typed/"
|
||||
});
|
||||
|
||||
do_log_info("Restrict typed, bookmark, autofill.typed = false, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bo",
|
||||
matches: [ ],
|
||||
autofilled: "bo",
|
||||
completed: "bo"
|
||||
});
|
||||
|
||||
do_log_info("Restrict typed, typed bookmark, autofill.typed = false, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "tp",
|
||||
matches: [ { uri: uri4, title: "tpbk" } ],
|
||||
autofilled: "tpbk/",
|
||||
completed: "tpbk/"
|
||||
});
|
||||
|
||||
// RESTRICT BOOKMARKS.
|
||||
Services.prefs.setIntPref("browser.urlbar.default.behavior", 2);
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true);
|
||||
|
||||
do_log_info("Restrict bookmarks, common visit, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "vi",
|
||||
matches: [ ],
|
||||
autofilled: "vi",
|
||||
completed: "vi"
|
||||
});
|
||||
|
||||
do_log_info("Restrict bookmarks, typed visit, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "ty",
|
||||
matches: [ ],
|
||||
autofilled: "ty",
|
||||
completed: "ty"
|
||||
});
|
||||
|
||||
// Don't autoFill this one cause it's not typed.
|
||||
do_log_info("Restrict bookmarks, bookmark, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bo",
|
||||
matches: [ { uri: uri3, title: "bookmarked" } ],
|
||||
autofilled: "bo",
|
||||
completed: "bo"
|
||||
});
|
||||
|
||||
// Note we don't show this one cause it's not typed.
|
||||
do_log_info("Restrict bookmarks, typed bookmark, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "tp",
|
||||
matches: [ { uri: uri4, title: "tpbk" } ],
|
||||
autofilled: "tpbk/",
|
||||
completed: "tpbk/"
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
|
||||
|
||||
do_log_info("Restrict bookmarks, bookmark, autofill.typed = false, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bo",
|
||||
matches: [ { uri: uri3, title: "bookmarked" } ],
|
||||
autofilled: "bookmarked/",
|
||||
completed: "bookmarked/"
|
||||
});
|
||||
|
||||
yield cleanup();
|
||||
});
|
||||
|
||||
add_task(function* test_default_behavior_url() {
|
||||
let uri1 = NetUtil.newURI("http://typed/ty/");
|
||||
let uri2 = NetUtil.newURI("http://visited/vi/");
|
||||
let uri3 = NetUtil.newURI("http://bookmarked/bo/");
|
||||
let uri4 = NetUtil.newURI("http://tpbk/tp/");
|
||||
|
||||
yield promiseAddVisits([
|
||||
{ uri: uri1, title: "typed", transition: TRANSITION_TYPED },
|
||||
{ uri: uri2, title: "visited" },
|
||||
{ uri: uri4, title: "tpbk", transition: TRANSITION_TYPED },
|
||||
]);
|
||||
addBookmark( { uri: uri3, title: "bookmarked" } );
|
||||
addBookmark( { uri: uri4, title: "tpbk" } );
|
||||
|
||||
// RESTRICT TO HISTORY.
|
||||
Services.prefs.setIntPref("browser.urlbar.default.behavior", 1);
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true);
|
||||
|
||||
do_log_info("URL: Restrict history, common visit, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "visited/v",
|
||||
matches: [ { uri: uri2, title: "visited" } ],
|
||||
autofilled: "visited/v",
|
||||
completed: "visited/v"
|
||||
});
|
||||
|
||||
do_log_info("URL: Restrict history, typed visit, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "typed/t",
|
||||
matches: [ { uri: uri1, title: "typed/ty/" } ],
|
||||
autofilled: "typed/ty/",
|
||||
completed: "http://typed/ty/"
|
||||
});
|
||||
|
||||
// Don't autoFill this one cause it's not typed.
|
||||
do_log_info("URL: Restrict history, bookmark, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bookmarked/b",
|
||||
matches: [ ],
|
||||
autofilled: "bookmarked/b",
|
||||
completed: "bookmarked/b"
|
||||
});
|
||||
|
||||
// Note we don't show this one cause it's not typed.
|
||||
do_log_info("URL: Restrict history, typed bookmark, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "tpbk/t",
|
||||
matches: [ { uri: uri4, title: "tpbk/tp/" } ],
|
||||
autofilled: "tpbk/tp/",
|
||||
completed: "http://tpbk/tp/"
|
||||
});
|
||||
|
||||
// RESTRICT BOOKMARKS.
|
||||
Services.prefs.setIntPref("browser.urlbar.default.behavior", 2);
|
||||
|
||||
do_log_info("URL: Restrict bookmarks, common visit, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "visited/v",
|
||||
matches: [ ],
|
||||
autofilled: "visited/v",
|
||||
completed: "visited/v"
|
||||
});
|
||||
|
||||
do_log_info("URL: Restrict bookmarks, typed visit, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "typed/t",
|
||||
matches: [ ],
|
||||
autofilled: "typed/t",
|
||||
completed: "typed/t"
|
||||
});
|
||||
|
||||
// Don't autoFill this one cause it's not typed.
|
||||
do_log_info("URL: Restrict bookmarks, bookmark, should not autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bookmarked/b",
|
||||
matches: [ { uri: uri3, title: "bookmarked" } ],
|
||||
autofilled: "bookmarked/b",
|
||||
completed: "bookmarked/b"
|
||||
});
|
||||
|
||||
// Note we don't show this one cause it's not typed.
|
||||
do_log_info("URL: Restrict bookmarks, typed bookmark, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "tpbk/t",
|
||||
matches: [ { uri: uri4, title: "tpbk/tp/" } ],
|
||||
autofilled: "tpbk/tp/",
|
||||
completed: "http://tpbk/tp/"
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
|
||||
|
||||
do_log_info("URL: Restrict bookmarks, bookmark, autofill.typed = false, should autoFill");
|
||||
yield check_autocomplete({
|
||||
search: "bookmarked/b",
|
||||
matches: [ { uri: uri3, title: "bookmarked/bo/" } ],
|
||||
autofilled: "bookmarked/bo/",
|
||||
completed: "http://bookmarked/bo/"
|
||||
});
|
||||
|
||||
yield cleanup();
|
||||
});
|
@ -12,7 +12,8 @@ add_task(function* test_dupe_urls() {
|
||||
search: "moz",
|
||||
autofilled: "mozilla.org/",
|
||||
completed: "mozilla.org/",
|
||||
matches: [ { uri: NetUtil.newURI("http://mozilla.org/"), title: "mozilla.org/" } ]
|
||||
matches: [ { uri: NetUtil.newURI("http://mozilla.org/"),
|
||||
title: "mozilla.org" } ]
|
||||
});
|
||||
yield cleanup();
|
||||
});
|
||||
|
@ -0,0 +1,25 @@
|
||||
/* 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_searchEngine() {
|
||||
Services.search.addEngineWithDetails("SearchEngine", "", "", "",
|
||||
"GET", "http://s.example.com/search");
|
||||
let engine = Services.search.getEngineByName("SearchEngine");
|
||||
engine.addParam("q", "{searchTerms}", null);
|
||||
do_register_cleanup(() => Services.search.removeEngine(engine));
|
||||
|
||||
let uri1 = NetUtil.newURI("http://s.example.com/search?q=Terms&client=1");
|
||||
let uri2 = NetUtil.newURI("http://s.example.com/search?q=Terms&client=2");
|
||||
yield promiseAddVisits({ uri: uri1, title: "Terms - SearchEngine Search" });
|
||||
addBookmark({ uri: uri2, title: "Terms - SearchEngine Search" });
|
||||
|
||||
do_log_info("Past search terms should be styled, unless bookmarked");
|
||||
yield check_autocomplete({
|
||||
search: "term",
|
||||
matches: [ { uri: uri1, title: "Terms", searchEngine: "SearchEngine" },
|
||||
{ uri: uri2, title: "Terms - SearchEngine Search" } ]
|
||||
});
|
||||
|
||||
yield cleanup();
|
||||
});
|
@ -358,6 +358,10 @@ add_task(function* test_special_searches() {
|
||||
matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ] } ]
|
||||
});
|
||||
|
||||
// Disable autoFill for the next tests, see test_autoFill_default_behavior.js
|
||||
// for specific tests.
|
||||
Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
|
||||
|
||||
// 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);
|
||||
|
@ -9,6 +9,7 @@ tail =
|
||||
[test_422277.js]
|
||||
[test_autocomplete_functional.js]
|
||||
[test_autocomplete_on_value_removed_479089.js]
|
||||
[test_autoFill_default_behavior.js]
|
||||
[test_casing.js]
|
||||
[test_do_not_trim.js]
|
||||
[test_download_embed_bookmarks.js]
|
||||
@ -22,6 +23,7 @@ tail =
|
||||
[test_match_beginning.js]
|
||||
[test_multi_word_search.js]
|
||||
[test_queryurl.js]
|
||||
[test_searchEngine.js]
|
||||
[test_special_search.js]
|
||||
[test_swap_protocol.js]
|
||||
[test_tabmatches.js]
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
Cu.import("resource://gre/modules/PriorityUrlProvider.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesSearchAutocompleteProvider.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
@ -11,15 +11,14 @@ function run_test() {
|
||||
add_task(function* search_engine_match() {
|
||||
let engine = yield promiseDefaultSearchEngine();
|
||||
let token = engine.getResultDomain();
|
||||
let match = yield PriorityUrlProvider.getMatch(token.substr(0, 1));
|
||||
let match = yield PlacesSearchAutocompleteProvider.findMatchByToken(token.substr(0, 1));
|
||||
do_check_eq(match.url, engine.searchForm);
|
||||
do_check_eq(match.title, engine.name);
|
||||
do_check_eq(match.engineName, engine.name);
|
||||
do_check_eq(match.iconUrl, engine.iconURI ? engine.iconURI.spec : null);
|
||||
do_check_eq(match.reason, "search");
|
||||
});
|
||||
|
||||
add_task(function* no_match() {
|
||||
do_check_eq(null, yield PriorityUrlProvider.getMatch("test"));
|
||||
do_check_eq(null, yield PlacesSearchAutocompleteProvider.findMatchByToken("test"));
|
||||
});
|
||||
|
||||
add_task(function* hide_search_engine_nomatch() {
|
||||
@ -29,20 +28,19 @@ add_task(function* hide_search_engine_nomatch() {
|
||||
Services.search.removeEngine(engine);
|
||||
yield promiseTopic;
|
||||
do_check_true(engine.hidden);
|
||||
do_check_eq(null, yield PriorityUrlProvider.getMatch(token.substr(0, 1)));
|
||||
do_check_eq(null, yield PlacesSearchAutocompleteProvider.findMatchByToken(token.substr(0, 1)));
|
||||
});
|
||||
|
||||
add_task(function* add_search_engine_match() {
|
||||
let promiseTopic = promiseSearchTopic("engine-added");
|
||||
do_check_eq(null, yield PriorityUrlProvider.getMatch("bacon"));
|
||||
do_check_eq(null, yield PlacesSearchAutocompleteProvider.findMatchByToken("bacon"));
|
||||
Services.search.addEngineWithDetails("bacon", "", "bacon", "Search Bacon",
|
||||
"GET", "http://www.bacon.moz/?search={searchTerms}");
|
||||
yield promiseSearchTopic;
|
||||
let match = yield PriorityUrlProvider.getMatch("bacon");
|
||||
let match = yield PlacesSearchAutocompleteProvider.findMatchByToken("bacon");
|
||||
do_check_eq(match.url, "http://www.bacon.moz");
|
||||
do_check_eq(match.title, "bacon");
|
||||
do_check_eq(match.engineName, "bacon");
|
||||
do_check_eq(match.iconUrl, null);
|
||||
do_check_eq(match.reason, "search");
|
||||
});
|
||||
|
||||
add_task(function* remove_search_engine_nomatch() {
|
||||
@ -50,7 +48,21 @@ add_task(function* remove_search_engine_nomatch() {
|
||||
let promiseTopic = promiseSearchTopic("engine-removed");
|
||||
Services.search.removeEngine(engine);
|
||||
yield promiseTopic;
|
||||
do_check_eq(null, yield PriorityUrlProvider.getMatch("bacon"));
|
||||
do_check_eq(null, yield PlacesSearchAutocompleteProvider.findMatchByToken("bacon"));
|
||||
});
|
||||
|
||||
add_task(function* test_parseSubmissionURL_basic() {
|
||||
// Most of the logic of parseSubmissionURL is tested in the search service
|
||||
// itself, thus we only do a sanity check of the wrapper here.
|
||||
let engine = yield promiseDefaultSearchEngine();
|
||||
let submissionURL = engine.getSubmission("terms").uri.spec;
|
||||
|
||||
let result = PlacesSearchAutocompleteProvider.parseSubmissionURL(submissionURL);
|
||||
do_check_eq(result.engineName, engine.name);
|
||||
do_check_eq(result.terms, "terms");
|
||||
|
||||
let result = PlacesSearchAutocompleteProvider.parseSubmissionURL("http://example.org/");
|
||||
do_check_eq(result, null);
|
||||
});
|
||||
|
||||
function promiseDefaultSearchEngine() {
|
@ -115,6 +115,7 @@ skip-if = true
|
||||
[test_pageGuid_bookmarkGuid.js]
|
||||
[test_frecency_observers.js]
|
||||
[test_placeURIs.js]
|
||||
[test_PlacesSearchAutocompleteProvider.js]
|
||||
[test_PlacesUtils_asyncGetBookmarkIds.js]
|
||||
[test_PlacesUtils_lazyobservers.js]
|
||||
[test_placesTxn.js]
|
||||
@ -125,7 +126,6 @@ skip-if = os == "android"
|
||||
# Bug 676989: test hangs consistently on Android
|
||||
skip-if = os == "android"
|
||||
[test_preventive_maintenance_runTasks.js]
|
||||
[test_priorityUrlProvider.js]
|
||||
[test_promiseBookmarksTree.js]
|
||||
[test_removeVisitsByTimeframe.js]
|
||||
# Bug 676989: test hangs consistently on Android
|
||||
|
@ -1436,6 +1436,20 @@ extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
type = types.join(" ");
|
||||
}
|
||||
|
||||
// Check if we have a search engine name
|
||||
let searchEngine = "";
|
||||
let searchIndex = types.indexOf("search");
|
||||
if (searchIndex >= 0) {
|
||||
const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
|
||||
|
||||
[title, searchEngine] = title.split(TITLE_SEARCH_ENGINE_SEPARATOR);
|
||||
|
||||
// Remove the "search" substring so that the correct style, if any,
|
||||
// is applied below.
|
||||
types.splice(searchIndex, 1);
|
||||
type = types.join(" ");
|
||||
}
|
||||
|
||||
// If we have a tag match, show the tags and icon
|
||||
if (type == "tag") {
|
||||
// Configure the extra box for tags display
|
||||
@ -1496,7 +1510,12 @@ extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
|
||||
// Emphasize the matching search terms for the description
|
||||
this._setUpDescription(this._title, title);
|
||||
this._setUpDescription(this._url, url);
|
||||
if (!searchEngine) {
|
||||
this._setUpDescription(this._url, url);
|
||||
} else {
|
||||
// The search engine name, when present, is not emphasized.
|
||||
this._setUpDescription(this._url, searchEngine, true);
|
||||
}
|
||||
|
||||
// Set up overflow on a timeout because the contents of the box
|
||||
// might not have a width yet even though we just changed them
|
||||
|
@ -363,7 +363,7 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||
}
|
||||
|
||||
let global = Cu.getGlobalForObject(listener);
|
||||
let globalDO = dbg.makeGlobalObjectReference(global);
|
||||
let globalDO = dbg.addDebuggee(global);
|
||||
let listenerDO = globalDO.makeDebuggeeValue(listener);
|
||||
|
||||
// If the listener is an object with a 'handleEvent' method, use that.
|
||||
@ -445,6 +445,7 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||
searchString: searchString
|
||||
});
|
||||
}
|
||||
dbg.removeAllDebuggees();
|
||||
return events;
|
||||
},
|
||||
|
||||
|
@ -32,7 +32,7 @@ const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
|
||||
const REGEX_MATCH_FUNCTION_ARGS = /^\(?function\s*[^\s(]*\s*\((.+?)\)/;
|
||||
|
||||
// Number of terminal entries for the self-xss prevention to go away
|
||||
const CONSOLE_ENTRY_THRESHOLD = 10
|
||||
const CONSOLE_ENTRY_THRESHOLD = 5
|
||||
let WebConsoleUtils = {
|
||||
/**
|
||||
* Convenience function to unwrap a wrapped object.
|
||||
|