merge fx-team to mozilla-central

This commit is contained in:
Carsten "Tomcat" Book 2014-04-25 13:18:39 +02:00
commit b2f7f763c4
75 changed files with 1847 additions and 226 deletions

View File

@ -1228,7 +1228,7 @@ pref("devtools.toolbox.footer.height", 250);
pref("devtools.toolbox.sidebar.width", 500);
pref("devtools.toolbox.host", "bottom");
pref("devtools.toolbox.selectedTool", "webconsole");
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper"]');
pref("devtools.toolbox.sideEnabled", true);
pref("devtools.toolbox.zoomValue", "1");
@ -1239,6 +1239,7 @@ pref("devtools.command-button-paintflashing.enabled", false);
pref("devtools.command-button-tilt.enabled", false);
pref("devtools.command-button-scratchpad.enabled", false);
pref("devtools.command-button-responsive.enabled", true);
pref("devtools.command-button-eyedropper.enabled", false);
// Inspector preferences
// Enable the Inspector

View File

@ -191,6 +191,60 @@ let AboutHomeListener = {
};
AboutHomeListener.init(this);
let ContentSearchMediator = {
whitelist: new Set([
"about:newtab",
]),
init: function (chromeGlobal) {
chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
addMessageListener("ContentSearch", this);
},
handleEvent: function (event) {
if (this._contentWhitelisted) {
this._sendMsg(event.detail.type, event.detail.data);
}
},
receiveMessage: function (msg) {
if (msg.data.type == "AddToWhitelist") {
for (let uri of msg.data.data) {
this.whitelist.add(uri);
}
this._sendMsg("AddToWhitelistAck");
return;
}
if (this._contentWhitelisted) {
this._fireEvent(msg.data.type, msg.data.data);
}
},
get _contentWhitelisted() {
return this.whitelist.has(content.document.documentURI.toLowerCase());
},
_sendMsg: function (type, data=null) {
sendAsyncMessage("ContentSearch", {
type: type,
data: data,
});
},
_fireEvent: function (type, data=null) {
content.dispatchEvent(new content.CustomEvent("ContentSearchService", {
detail: {
type: type,
data: data,
},
}));
},
};
ContentSearchMediator.init(this);
var global = this;
// Lazily load the finder code

View File

@ -197,12 +197,18 @@ let gGrid = {
}
let availSpace = document.documentElement.clientHeight - this._cellMargin -
document.querySelector("#newtab-margin-undo-container").offsetHeight;
document.querySelector("#newtab-margin-undo-container").offsetHeight -
document.querySelector("#newtab-search-form").offsetHeight;
let visibleRows = Math.floor(availSpace / this._cellHeight);
this._node.style.height = this._computeHeight() + "px";
this._node.style.maxHeight = this._computeHeight(visibleRows) + "px";
this._node.style.maxWidth = gGridPrefs.gridColumns * this._cellWidth +
GRID_WIDTH_EXTRA + "px";
// Resize the search bar.
let width = parseFloat(window.getComputedStyle(this._node).width);
let visibleCols = Math.floor(width / this._cellWidth);
gSearch.setWidth(visibleCols * this._cellWidth - this._cellMargin);
},
_shouldRenderGrid : function Grid_shouldRenderGrid() {

View File

@ -2,6 +2,11 @@
* 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/. */
input {
font: message-box !important;
font-size: 16px !important;
}
input[type=button] {
cursor: pointer;
}
@ -12,6 +17,7 @@ input[type=button] {
position: relative;
-moz-box-flex: 1;
-moz-user-focus: normal;
-moz-box-orient: vertical;
}
#newtab-scrollbox:not([page-disabled]) {
@ -54,6 +60,7 @@ input[type=button] {
#newtab-margin-undo-container {
display: -moz-box;
-moz-box-pack: center;
margin-bottom: 26px; /* 32 - 6 search form top "padding" */
}
#newtab-horizontal-margin {
@ -64,10 +71,17 @@ input[type=button] {
#newtab-margin-top,
#newtab-margin-bottom {
display: -moz-box;
-moz-box-flex: 1;
position: relative;
}
#newtab-margin-top {
-moz-box-flex: 1;
}
#newtab-margin-bottom {
-moz-box-flex: 2;
}
.newtab-side-margin {
min-width: 16px;
-moz-box-flex: 1;
@ -213,7 +227,7 @@ input[type=button] {
opacity: 0.01;
}
/* PANEL */
/* SPONSORED PANEL */
#sponsored-panel {
width: 330px;
}
@ -225,3 +239,156 @@ input[type=button] {
#sponsored-panel .text-link {
margin: 12px 0 0;
}
/* SEARCH */
#newtab-search-container {
display: -moz-box;
position: relative;
-moz-box-align: center;
-moz-box-pack: center;
}
#newtab-search-container[page-disabled] {
opacity: 0;
pointer-events: none;
}
#newtab-search-form {
display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-align: center;
height: 44px; /* 32 + 6 logo top "padding" + 6 logo bottom "padding" */
margin-bottom: 10px; /* 32 - 16 tiles top margin - 6 logo bottom "padding" */
}
#newtab-search-logo {
display: -moz-box;
width: 77px; /* 65 image width + 6 left "padding" + 6 right "padding" */
height: 38px; /* 26 image height + 6 top "padding" + 6 bottom "padding" */
border: 1px solid transparent;
-moz-margin-end: 8px;
background-repeat: no-repeat;
background-position: center;
background-size: 65px 26px;
}
#newtab-search-logo[hidden] {
display: none;
}
#newtab-search-logo[active],
#newtab-search-logo:hover {
background-color: #e9e9e9;
border: 1px solid rgb(226, 227, 229);
border-radius: 2.5px;
}
#newtab-search-text {
height: 32px;
-moz-box-flex: 1;
padding: 0 8px;
background: hsla(0,0%,100%,.9) padding-box;
border: 1px solid;
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset,
0 0 2px hsla(210,65%,9%,.1) inset,
0 1px 0 hsla(0,0%,100%,.2);
border-radius: 2.5px 0 0 2.5px;
}
#newtab-search-text:-moz-dir(rtl) {
border-radius: 0 2.5px 2.5px 0;
}
#newtab-search-text:focus,
#newtab-search-text[autofocus] {
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
}
#newtab-search-submit {
height: 32px;
-moz-margin-start: -1px;
background: linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
padding: 0 9px;
border: 1px solid;
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
-moz-border-start: 1px solid transparent;
border-radius: 0 2.5px 2.5px 0;
box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
0 1px 0 hsla(0,0%,100%,.2);
cursor: pointer;
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
}
#newtab-search-submit:-moz-dir(rtl) {
border-radius: 2.5px 0 0 2.5px;
}
#newtab-search-text:focus + #newtab-search-submit,
#newtab-search-text + #newtab-search-submit:hover,
#newtab-search-text[autofocus] + #newtab-search-submit {
border-color: #59b5fc #45a3e7 #3294d5;
color: white;
}
#newtab-search-text:focus + #newtab-search-submit,
#newtab-search-text[autofocus] + #newtab-search-submit {
background-image: linear-gradient(#4cb1ff, #1793e5);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
0 0 0 1px hsla(0,0%,100%,.1) inset,
0 1px 0 hsla(210,54%,20%,.03);
}
#newtab-search-text + #newtab-search-submit:hover {
background-image: linear-gradient(#66bdff, #0d9eff);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
0 0 0 1px hsla(0,0%,100%,.1) inset,
0 1px 0 hsla(210,54%,20%,.03),
0 0 4px hsla(206,100%,20%,.2);
}
#newtab-search-text + #newtab-search-submit:hover:active {
box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
0 0 1px hsla(211,79%,6%,.2) inset;
transition-duration: 0ms;
}
#newtab-search-panel .panel-arrowcontent {
-moz-padding-start: 0;
-moz-padding-end: 0;
padding-top: 0;
padding-bottom: 0;
background: rgb(248, 250, 251);
}
.newtab-search-panel-engine {
-moz-box-align: center;
padding-top: 4px;
padding-bottom: 4px;
-moz-padding-start: 24px;
-moz-padding-end: 24px;
}
.newtab-search-panel-engine:not(:last-child) {
border-bottom: 1px solid #ccc;
}
.newtab-search-panel-engine > image {
-moz-margin-end: 8px;
width: 16px;
height: 16px;
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.newtab-search-panel-engine > label {
-moz-padding-start: 0;
-moz-margin-start: 0;
color: rgb(130, 132, 133);
}
.newtab-search-panel-engine[selected] {
background: url("chrome://global/skin/menu/shared-menu-check.png") center left 4px no-repeat transparent;
}

View File

@ -41,6 +41,7 @@ function inPrivateBrowsingMode() {
}
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
#include transformations.js
#include page.js
@ -54,6 +55,7 @@ const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
#include dropPreview.js
#include updater.js
#include undo.js
#include search.js
// Everything is loaded. Initialize the New Tab Page.
gPage.init();

View File

@ -11,6 +11,8 @@
<!DOCTYPE window [
<!ENTITY % newTabDTD SYSTEM "chrome://browser/locale/newTab.dtd">
%newTabDTD;
<!ENTITY % searchBarDTD SYSTEM "chrome://browser/locale/searchbar.dtd">
%searchBarDTD;
]>
<xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
@ -24,6 +26,13 @@
value="&newtab.panel.link.text;" />
</xul:panel>
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
noautohide="true">
<xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
<xul:label>&cmd_engineManager.label;</xul:label>
</xul:hbox>
</xul:panel>
<div id="newtab-scrollbox">
<div id="newtab-vertical-margin">
@ -46,6 +55,16 @@
</div>
</div>
<div id="newtab-search-container">
<form id="newtab-search-form" name="searchForm">
<div id="newtab-search-logo"/>
<input type="text" name="q" value="" id="newtab-search-text"
maxlength="256" dir="auto"/>
<input id="newtab-search-submit" type="submit"
value="&searchEndCap.label;"/>
</form>
</div>
<div id="newtab-horizontal-margin">
<div class="newtab-side-margin"/>

View File

@ -111,6 +111,8 @@ let gPage = {
this._initialized = true;
gSearch.init();
this._mutationObserver = new MutationObserver(() => {
if (this.allowBackgroundCaptures) {
Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true);
@ -138,6 +140,10 @@ let gPage = {
let shownCount = Math.min(10, count);
Services.telemetry.getHistogramById(shownId).add(shownCount);
}
// content.js isn't loaded for the page while it's in the preloader,
// which is why this is necessary.
gSearch.setUpInitialState();
}
});
this._mutationObserver.observe(document.documentElement, {
@ -164,7 +170,7 @@ let gPage = {
*/
_updateAttributes: function Page_updateAttributes(aValue) {
// Set the nodes' states.
let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid, #newtab-search-container";
for (let node of document.querySelectorAll(nodeSelector)) {
if (aValue)
node.removeAttribute("page-disabled");

View File

@ -0,0 +1,170 @@
#ifdef 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/. */
#endif
let gSearch = {
currentEngineName: null,
init: function () {
for (let idSuffix of this._nodeIDSuffixes) {
this._nodes[idSuffix] =
document.getElementById("newtab-search-" + idSuffix);
}
window.addEventListener("ContentSearchService", this);
this.setUpInitialState();
},
setUpInitialState: function () {
this._send("GetState");
},
showPanel: function () {
let panel = this._nodes.panel;
let logo = this._nodes.logo;
panel.openPopup(logo);
logo.setAttribute("active", "true");
panel.addEventListener("popuphidden", function onHidden() {
panel.removeEventListener("popuphidden", onHidden);
logo.removeAttribute("active");
});
},
search: function (event) {
event.preventDefault();
let searchStr = this._nodes.text.value;
if (this.currentEngineName && searchStr.length) {
this._send("Search", {
engineName: this.currentEngineName,
searchString: searchStr,
whence: "newtab",
});
}
},
manageEngines: function () {
this._nodes.panel.hidePopup();
this._send("ManageEngines");
},
setWidth: function (width) {
this._nodes.form.style.width = width + "px";
this._nodes.form.style.maxWidth = width + "px";
},
handleEvent: function (event) {
this["on" + event.detail.type](event.detail.data);
},
onState: function (data) {
this._makePanel(data.engines);
this._setCurrentEngine(data.currentEngine);
this._initWhenInitalStateReceived();
},
onCurrentEngine: function (engineName) {
this._setCurrentEngine(engineName);
},
_nodeIDSuffixes: [
"form",
"logo",
"manage",
"panel",
"text",
],
_nodes: {},
_initWhenInitalStateReceived: function () {
this._nodes.form.addEventListener("submit", e => this.search(e));
this._nodes.logo.addEventListener("click", e => this.showPanel());
this._nodes.manage.addEventListener("click", e => this.manageEngines());
this._initWhenInitalStateReceived = function () {};
},
_send: function (type, data=null) {
window.dispatchEvent(new CustomEvent("ContentSearchClient", {
detail: {
type: type,
data: data,
},
}));
},
_makePanel: function (engines) {
let panel = this._nodes.panel;
// Empty the panel except for the Manage Engines row.
let i = 0;
while (i < panel.childNodes.length) {
let node = panel.childNodes[i];
if (node != this._nodes.manage) {
panel.removeChild(node);
}
else {
i++;
}
}
// Add all the engines.
for (let engine of engines) {
panel.insertBefore(this._makePanelEngine(panel, engine),
this._nodes.manage);
}
},
_makePanelEngine: function (panel, engine) {
let box = document.createElementNS(XUL_NAMESPACE, "hbox");
box.className = "newtab-search-panel-engine";
box.setAttribute("engine", engine.name);
box.addEventListener("click", () => {
this._send("SetCurrentEngine", engine.name);
panel.hidePopup();
this._nodes.text.focus();
});
let image = document.createElementNS(XUL_NAMESPACE, "image");
if (engine.iconURI) {
image.setAttribute("src", engine.iconURI);
}
box.appendChild(image);
let label = document.createElementNS(XUL_NAMESPACE, "label");
label.setAttribute("value", engine.name);
box.appendChild(label);
return box;
},
_setCurrentEngine: function (engine) {
this.currentEngineName = engine.name;
// Set the logo.
let logoURI = window.devicePixelRatio == 2 ? engine.logo2xURI :
engine.logoURI;
if (logoURI) {
this._nodes.logo.hidden = false;
this._nodes.logo.style.backgroundImage = "url(" + logoURI + ")";
this._nodes.text.placeholder = "";
}
else {
this._nodes.logo.hidden = true;
this._nodes.text.placeholder = engine.name;
}
// Set the selected state of all the engines in the panel.
for (let box of this._nodes.panel.childNodes) {
if (box.getAttribute("engine") == engine.name) {
box.setAttribute("selected", "true");
}
else {
box.removeAttribute("selected");
}
}
},
};

View File

@ -399,7 +399,6 @@ skip-if = e10s # Bug ????? - test calls gBrowser.contentWindow.stop
[browser_urlbar_search_healthreport.js]
skip-if = e10s # Bug ?????? - FHR tests failing (either with "no data for today" or "2 records for today")
[browser_utilityOverlay.js]
skip-if = e10s # Bug 921947 - openNewTabWith failed with window.content.document being null
[browser_visibleFindSelection.js]
skip-if = e10s # Bug ?????? - test directly manipulates content
[browser_visibleLabel.js]

View File

@ -1,6 +1,9 @@
[DEFAULT]
support-files = head.js
skip-if = e10s # Bug ?????? - about:newtab tests don't work in e10s
support-files =
head.js
searchEngineLogo.xml
searchEngineNoLogo.xml
[browser_newtab_background_captures.js]
[browser_newtab_block.js]
@ -25,6 +28,7 @@ skip-if = os == "mac" # Intermittent failures, bug 898317
[browser_newtab_focus.js]
[browser_newtab_perwindow_private_browsing.js]
[browser_newtab_reset.js]
[browser_newtab_search.js]
[browser_newtab_sponsored_icon_click.js]
[browser_newtab_tabsync.js]
[browser_newtab_undo.js]

View File

@ -9,9 +9,9 @@ function runTests() {
Services.prefs.setIntPref("accessibility.tabfocus", 7);
// Focus count in new tab page.
// 28 = 9 * 3 + 1 = 9 sites and 1 toggle button, each site has a link, a pin
// and a remove button.
let FOCUS_COUNT = 28;
// 30 = 9 * 3 + 3 = 9 sites, each with link, pin and remove buttons; search
// bar; search button; and toggle button.
let FOCUS_COUNT = 30;
// Create a new tab page.
yield setLinks("0,1,2,3,4,5,6,7,8");

View File

@ -0,0 +1,295 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// See browser/components/search/test/browser_*_behavior.js for tests of actual
// searches.
const ENGINE_LOGO = "searchEngineLogo.xml";
const ENGINE_NO_LOGO = "searchEngineNoLogo.xml";
const SERVICE_EVENT_NAME = "ContentSearchService";
const LOGO_LOW_DPI_SIZE = [65, 26];
const LOGO_HIGH_DPI_SIZE = [130, 52];
// The test has an expected search event queue and a search event listener.
// Search events that are expected to happen are added to the queue, and the
// listener consumes the queue and ensures that each event it receives is at
// the head of the queue.
//
// Each item in the queue is an object { type, deferred }. type is the
// expected search event type. deferred is a Promise.defer() value that is
// resolved when the event is consumed.
var gExpectedSearchEventQueue = [];
var gNewEngines = [];
function runTests() {
let oldCurrentEngine = Services.search.currentEngine;
yield addNewTabPageTab();
yield whenSearchInitDone();
// The tab is removed at the end of the test, so there's no need to remove
// this listener at the end of the test.
info("Adding search event listener");
getContentWindow().addEventListener(SERVICE_EVENT_NAME, searchEventListener);
let panel = searchPanel();
is(panel.state, "closed", "Search panel should be closed initially");
// The panel's animation often is not finished when the test clicks on panel
// children, which makes the test click the wrong children, so disable it.
panel.setAttribute("animate", "false");
// Add the two test engines.
let logoEngine = null;
yield promiseNewSearchEngine(true).then(engine => {
logoEngine = engine;
TestRunner.next();
});
ok(!!logoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
"Sanity check: engine should have 1x logo");
ok(!!logoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
"Sanity check: engine should have 2x logo");
let noLogoEngine = null;
yield promiseNewSearchEngine(false).then(engine => {
noLogoEngine = engine;
TestRunner.next();
});
ok(!noLogoEngine.getIconURLBySize(...LOGO_LOW_DPI_SIZE),
"Sanity check: engine should not have 1x logo");
ok(!noLogoEngine.getIconURLBySize(...LOGO_HIGH_DPI_SIZE),
"Sanity check: engine should not have 2x logo");
// Use the search service to change the current engine to the logo engine.
Services.search.currentEngine = logoEngine;
yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
checkCurrentEngine(ENGINE_LOGO);
// Click the logo to open the search panel.
yield Promise.all([
promisePanelShown(panel),
promiseClick(logoImg()),
]).then(TestRunner.next);
// In the search panel, click the no-logo engine. It should become the
// current engine.
let noLogoBox = null;
for (let box of panel.childNodes) {
if (box.getAttribute("engine") == noLogoEngine.name) {
noLogoBox = box;
break;
}
}
ok(noLogoBox, "Search panel should contain the no-logo engine");
yield Promise.all([
promiseSearchEvents(["CurrentEngine"]),
promiseClick(noLogoBox),
]).then(TestRunner.next);
checkCurrentEngine(ENGINE_NO_LOGO);
// Switch back to the logo engine.
Services.search.currentEngine = logoEngine;
yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
checkCurrentEngine(ENGINE_LOGO);
// Open the panel again.
yield Promise.all([
promisePanelShown(panel),
promiseClick(logoImg()),
]).then(TestRunner.next);
// In the search panel, click the Manage Engines box.
let manageBox = $("manage");
ok(!!manageBox, "The Manage Engines box should be present in the document");
yield Promise.all([
promiseManagerOpen(),
promiseClick(manageBox),
]).then(TestRunner.next);
// Done. Revert the current engine and remove the new engines.
Services.search.currentEngine = oldCurrentEngine;
yield promiseSearchEvents(["CurrentEngine"]).then(TestRunner.next);
let events = [];
for (let engine of gNewEngines) {
Services.search.removeEngine(engine);
events.push("State");
}
yield promiseSearchEvents(events).then(TestRunner.next);
}
function searchEventListener(event) {
info("Got search event " + event.detail.type);
let passed = false;
let nonempty = gExpectedSearchEventQueue.length > 0;
ok(nonempty, "Expected search event queue should be nonempty");
if (nonempty) {
let { type, deferred } = gExpectedSearchEventQueue.shift();
is(event.detail.type, type, "Got expected search event " + type);
if (event.detail.type == type) {
passed = true;
// Let gSearch respond to the event before continuing.
executeSoon(() => deferred.resolve());
}
}
if (!passed) {
info("Didn't get expected event, stopping the test");
getContentWindow().removeEventListener(SERVICE_EVENT_NAME,
searchEventListener);
// Set next() to a no-op so the test really does stop.
TestRunner.next = function () {};
TestRunner.finish();
}
}
function $(idSuffix) {
return getContentDocument().getElementById("newtab-search-" + idSuffix);
}
function promiseSearchEvents(events) {
info("Expecting search events: " + events);
events = events.map(e => ({ type: e, deferred: Promise.defer() }));
gExpectedSearchEventQueue.push(...events);
return Promise.all(events.map(e => e.deferred.promise));
}
function promiseNewSearchEngine(withLogo) {
let basename = withLogo ? ENGINE_LOGO : ENGINE_NO_LOGO;
info("Waiting for engine to be added: " + basename);
// Wait for the search events triggered by adding the new engine.
// engine-added engine-loaded
let expectedSearchEvents = ["State", "State"];
if (withLogo) {
// an engine-changed for each of the two logos
expectedSearchEvents.push("State", "State");
}
let eventPromise = promiseSearchEvents(expectedSearchEvents);
// Wait for addEngine().
let addDeferred = Promise.defer();
let url = getRootDirectory(gTestPath) + basename;
Services.search.addEngine(url, Ci.nsISearchEngine.TYPE_MOZSEARCH, "", false, {
onSuccess: function (engine) {
info("Search engine added: " + basename);
gNewEngines.push(engine);
addDeferred.resolve(engine);
},
onError: function (errCode) {
ok(false, "addEngine failed with error code " + errCode);
addDeferred.reject();
},
});
// Make a new promise that wraps the previous promises. The only point of
// this is to pass the new engine to the yielder via deferred.resolve(),
// which is a little nicer than passing an array whose first element is the
// new engine.
let deferred = Promise.defer();
Promise.all([addDeferred.promise, eventPromise]).then(values => {
let newEngine = values[0];
deferred.resolve(newEngine);
}, () => deferred.reject());
return deferred.promise;
}
function checkCurrentEngine(basename) {
let engine = Services.search.currentEngine;
ok(engine.name.contains(basename),
"Sanity check: current engine: engine.name=" + engine.name +
" basename=" + basename);
// gSearch.currentEngineName
is(gSearch().currentEngineName, engine.name,
"currentEngineName: " + engine.name);
// search bar logo
let logoSize = [px * window.devicePixelRatio for (px of LOGO_LOW_DPI_SIZE)];
let logoURI = engine.getIconURLBySize(...logoSize);
let logo = logoImg();
is(logo.hidden, !logoURI,
"Logo should be visible iff engine has a logo: " + engine.name);
if (logoURI) {
is(logo.style.backgroundImage, 'url("' + logoURI + '")', "Logo URI");
}
// "selected" attributes of engines in the panel
let panel = searchPanel();
for (let engineBox of panel.childNodes) {
let engineName = engineBox.getAttribute("engine");
if (engineName == engine.name) {
is(engineBox.getAttribute("selected"), "true",
"Engine box's selected attribute should be true for " +
"selected engine: " + engineName);
}
else {
ok(!engineBox.hasAttribute("selected"),
"Engine box's selected attribute should be absent for " +
"non-selected engine: " + engineName);
}
}
}
function promisePanelShown(panel) {
let deferred = Promise.defer();
info("Waiting for popupshown");
panel.addEventListener("popupshown", function onEvent() {
panel.removeEventListener("popupshown", onEvent);
is(panel.state, "open", "Panel state");
executeSoon(() => deferred.resolve());
});
return deferred.promise;
}
function promiseClick(node) {
let deferred = Promise.defer();
let win = getContentWindow();
SimpleTest.waitForFocus(() => {
EventUtils.synthesizeMouseAtCenter(node, {}, win);
deferred.resolve();
}, win);
return deferred.promise;
}
function promiseManagerOpen() {
info("Waiting for the search manager window to open...");
let deferred = Promise.defer();
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
winWatcher.registerNotification(function onWin(subj, topic, data) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
subj.addEventListener("load", function onLoad() {
subj.removeEventListener("load", onLoad);
if (subj.document.documentURI ==
"chrome://browser/content/search/engineManager.xul") {
winWatcher.unregisterNotification(onWin);
ok(true, "Observed search manager window opened");
is(subj.opener, gWindow,
"Search engine manager opener should be the chrome browser " +
"window containing the newtab page");
executeSoon(() => {
subj.close();
deferred.resolve();
});
}
});
}
});
return deferred.promise;
}
function searchPanel() {
return $("panel");
}
function logoImg() {
return $("logo");
}
function gSearch() {
return getContentWindow().gSearch;
}

View File

@ -5,6 +5,10 @@ function runTests() {
yield setLinks("0");
yield addNewTabPageTab();
// When gSearch modifies the DOM as it sets itself up, it can prevent the
// popup from opening, depending on the timing. Wait until that's done.
yield whenSearchInitDone();
let site = getCell(0).node.querySelector(".newtab-site");
site.setAttribute("type", "sponsored");

View File

@ -25,10 +25,41 @@ let isLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc);
let isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
let gWindow = window;
// The tests assume all three rows of sites are shown, but the window may be too
// short to actually show three rows. Resize it if necessary.
let requiredInnerHeight =
40 + 32 + // undo container + bottom margin
44 + 32 + // search bar + bottom margin
(3 * (150 + 32)) + // 3 rows * (tile height + title and bottom margin)
100; // breathing room
let oldInnerHeight = null;
if (gBrowser.contentWindow.innerHeight < requiredInnerHeight) {
oldInnerHeight = gBrowser.contentWindow.innerHeight;
info("Changing browser inner height from " + oldInnerHeight + " to " +
requiredInnerHeight);
gBrowser.contentWindow.innerHeight = requiredInnerHeight;
let screenHeight = {};
Cc["@mozilla.org/gfx/screenmanager;1"].
getService(Ci.nsIScreenManager).
primaryScreen.
GetAvailRectDisplayPix({}, {}, {}, screenHeight);
screenHeight = screenHeight.value;
if (screenHeight < gBrowser.contentWindow.outerHeight) {
info("Warning: Browser outer height is now " +
gBrowser.contentWindow.outerHeight + ", which is larger than the " +
"available screen height, " + screenHeight +
". That may cause problems.");
}
}
registerCleanupFunction(function () {
while (gWindow.gBrowser.tabs.length > 1)
gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]);
if (oldInnerHeight)
gBrowser.contentWindow.innerHeight = oldInnerHeight;
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
@ -549,3 +580,35 @@ function whenPagesUpdated(aCallback, aOnlyIfHidden=false) {
NewTabUtils.allPages.unregister(page);
});
}
/**
* Waits a small amount of time for search events to stop occurring in the
* newtab page.
*
* newtab pages receive some search events around load time that are difficult
* to predict. There are two categories of such events: (1) "State" events
* triggered by engine notifications like engine-changed, due to the search
* service initializing itself on app startup. This can happen when a test is
* the first test to run. (2) "State" events triggered by the newtab page
* itself when gSearch first sets itself up. newtab preloading makes these a
* pain to predict.
*/
function whenSearchInitDone() {
info("Waiting for initial search events...");
let numTicks = 0;
function reset(event) {
info("Got initial search event " + event.detail.type +
", waiting for more...");
numTicks = 0;
}
let eventName = "ContentSearchService";
getContentWindow().addEventListener(eventName, reset);
let interval = window.setInterval(() => {
if (++numTicks >= 100) {
info("Done waiting for initial search events");
window.clearInterval(interval);
getContentWindow().removeEventListener(eventName, reset);
TestRunner.next();
}
}, 0);
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>browser_newtab_search searchEngineNoLogo.xml</ShortName>
<Url type="text/html" method="GET" template="http://browser-newtab-search.com/nologo" rel="searchform"/>
</SearchPlugin>

View File

@ -599,17 +599,14 @@ function makeURLAbsolute(aBase, aUrl)
return makeURI(aUrl, null, makeURI(aBase)).spec;
}
/**
* openNewTabWith: opens a new tab with the given URL.
*
* @param aURL
* The URL to open (as a string).
* @param aDocument
* The document from which the URL came, or null. This is used to set the
* referrer header and to do a security check of whether the document is
* allowed to reference the URL. If null, there will be no referrer
* header and no security check.
* Note this parameter is now ignored. There is no security check & no
* referrer header derived from aDocument (null case).
* @param aPostData
* Form POST data, or null.
* @param aEvent
@ -620,46 +617,40 @@ function makeURLAbsolute(aBase, aUrl)
* (e.g., Google's I Feel Lucky) for interpretation. This parameter may
* be undefined in which case it is treated as false.
* @param [optional] aReferrer
* If aDocument is null, then this will be used as the referrer.
* There will be no security check.
* This will be used as the referrer. There will be no security check.
*/
function openNewTabWith(aURL, aDocument, aPostData, aEvent,
aAllowThirdPartyFixup, aReferrer) {
if (aDocument)
urlSecurityCheck(aURL, aDocument.nodePrincipal);
// As in openNewWindowWith(), we want to pass the charset of the
// current document over to a new tab.
var originCharset = aDocument && aDocument.characterSet;
if (!originCharset &&
document.documentElement.getAttribute("windowtype") == "navigator:browser")
originCharset = window.content.document.characterSet;
let originCharset = null;
if (document.documentElement.getAttribute("windowtype") == "navigator:browser")
originCharset = gBrowser.selectedBrowser.characterSet;
openLinkIn(aURL, aEvent && aEvent.shiftKey ? "tabshifted" : "tab",
{ charset: originCharset,
postData: aPostData,
allowThirdPartyFixup: aAllowThirdPartyFixup,
referrerURI: aDocument ? aDocument.documentURIObject : aReferrer });
referrerURI: aReferrer });
}
/**
* @param aDocument
* Note this parameter is ignored. See openNewTabWith()
*/
function openNewWindowWith(aURL, aDocument, aPostData, aAllowThirdPartyFixup, aReferrer) {
if (aDocument)
urlSecurityCheck(aURL, aDocument.nodePrincipal);
// if and only if the current window is a browser window and it has a
// document with a character set, then extract the current charset menu
// setting from the current document and use it to initialize the new browser
// window...
var originCharset = aDocument && aDocument.characterSet;
if (!originCharset &&
document.documentElement.getAttribute("windowtype") == "navigator:browser")
originCharset = window.content.document.characterSet;
// Extract the current charset menu setting from the current document and
// use it to initialize the new browser window...
let originCharset = null;
if (document.documentElement.getAttribute("windowtype") == "navigator:browser")
originCharset = gBrowser.selectedBrowser.characterSet;
openLinkIn(aURL, "window",
{ charset: originCharset,
postData: aPostData,
allowThirdPartyFixup: aAllowThirdPartyFixup,
referrerURI: aDocument ? aDocument.documentURIObject : aReferrer });
referrerURI: aReferrer });
}
// aCalledFromModal is optional

View File

@ -98,6 +98,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
"resource:///modules/SignInToWebsite.jsm");
#endif
XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
"resource:///modules/ContentSearch.jsm");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@ -497,6 +500,7 @@ BrowserGlue.prototype = {
AboutHome.init();
SessionStore.init();
BrowserUITelemetry.init();
ContentSearch.init();
if (Services.appinfo.browserTabsRemote) {
ContentClick.init();

View File

@ -65,6 +65,50 @@ function test() {
EventUtils.synthesizeKey("VK_RETURN", {});
}
},
{
name: "new tab search",
searchURL: base,
run: function () {
function doSearch(doc) {
// Re-add the listener, and perform a search
gBrowser.addProgressListener(listener);
doc.getElementById("newtab-search-text").value = "foo";
doc.getElementById("newtab-search-submit").click();
}
// load about:newtab, but remove the listener first so it doesn't
// get in the way
gBrowser.removeProgressListener(listener);
gBrowser.loadURI("about:newtab");
info("Waiting for about:newtab load");
tab.linkedBrowser.addEventListener("load", function load(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank") {
info("skipping spurious load event");
return;
}
tab.linkedBrowser.removeEventListener("load", load, true);
// Observe page setup
let win = gBrowser.contentWindow;
if (win.gSearch.currentEngineName ==
Services.search.currentEngine.name) {
doSearch(win.document);
}
else {
info("Waiting for newtab search init");
win.addEventListener("ContentSearchService", function done(event) {
info("Got newtab search event " + event.detail.type);
if (event.detail.type == "State") {
win.removeEventListener("ContentSearchService", done);
// Let gSearch respond to the event before continuing.
executeSoon(() => doSearch(win.document));
}
});
}
}, true);
}
},
{
name: "home page search",
searchURL: base + "&form=MOZSPG",

View File

@ -95,6 +95,50 @@ function test() {
EventUtils.synthesizeKey("VK_RETURN", {});
}
},
{
name: "new tab search",
searchURL: base,
run: function () {
function doSearch(doc) {
// Re-add the listener, and perform a search
gBrowser.addProgressListener(listener);
doc.getElementById("newtab-search-text").value = "foo";
doc.getElementById("newtab-search-submit").click();
}
// load about:newtab, but remove the listener first so it doesn't
// get in the way
gBrowser.removeProgressListener(listener);
gBrowser.loadURI("about:newtab");
info("Waiting for about:newtab load");
tab.linkedBrowser.addEventListener("load", function load(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank") {
info("skipping spurious load event");
return;
}
tab.linkedBrowser.removeEventListener("load", load, true);
// Observe page setup
let win = gBrowser.contentWindow;
if (win.gSearch.currentEngineName ==
Services.search.currentEngine.name) {
doSearch(win.document);
}
else {
info("Waiting for newtab search init");
win.addEventListener("ContentSearchService", function done(event) {
info("Got newtab search event " + event.detail.type);
if (event.detail.type == "State") {
win.removeEventListener("ContentSearchService", done);
// Let gSearch respond to the event before continuing.
executeSoon(() => doSearch(win.document));
}
});
}
}, true);
}
},
{
name: "home page search",
searchURL: base + "&channel=np&source=hp",

View File

@ -9,8 +9,6 @@
const TAB_URL = EXAMPLE_URL + "doc_breakpoints-break-on-last-line-of-script-on-reload.html";
const CODE_URL = EXAMPLE_URL + "code_breakpoints-break-on-last-line-of-script-on-reload.js";
const { promiseInvoke } = require("devtools/async-utils");
function test() {
let gPanel, gDebugger, gThreadClient, gEvents;
@ -59,15 +57,15 @@ function test() {
// And we should hit the breakpoints as we resume.
yield promise.all([
doResume(),
doResume(gPanel),
waitForCaretAndScopes(gPanel, 3)
]);
yield promise.all([
doResume(),
doResume(gPanel),
waitForCaretAndScopes(gPanel, 4)
]);
yield promise.all([
doResume(),
doResume(gPanel),
waitForCaretAndScopes(gPanel, 5)
]);
@ -90,23 +88,6 @@ function test() {
});
});
function rdpInvoke(obj, method) {
return promiseInvoke(obj, method)
.then(({error, message }) => {
if (error) {
throw new Error(error + ": " + message);
}
});
}
function doResume() {
return rdpInvoke(gThreadClient, gThreadClient.resume);
}
function doInterrupt() {
return rdpInvoke(gThreadClient, gThreadClient.interrupt);
}
function setBreakpoint(location) {
let deferred = promise.defer();
gThreadClient.setBreakpoint(location, ({ error, message }, bpClient) => {

View File

@ -2,16 +2,15 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that pretty printing when the debugger is paused
* does not switch away from the selected source.
* Test that pretty printing when the debugger is paused does not switch away
* from the selected source.
*/
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-on-paused.html";
let gTab, gDebuggee, gPanel, gDebugger;
let gSources;
let gTab, gDebuggee, gPanel, gDebugger, gThreadClient, gSources;
let gSecondSourceLabel = "code_ugly-2.js";
const SECOND_SOURCE_VALUE = EXAMPLE_URL + "code_ugly-2.js";
function test(){
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
@ -19,63 +18,50 @@ function test(){
gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gThreadClient = gDebugger.gThreadClient;
gSources = gDebugger.DebuggerView.Sources;
gPanel.addBreakpoint({ url: gSources.values[0], line: 6 });
Task.spawn(function* () {
try {
yield ensureSourceIs(gPanel, "code_script-switching-02.js", true);
waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6)
.then(testPaused)
.then(() => {
// Switch to the second source.
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
gSources.selectedIndex = 1;
return finished;
})
.then(testSecondSourceIsSelected)
.then(() => {
const finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
clickPrettyPrintButton();
testProgressBarShown();
return finished;
})
.then(testSecondSourceIsStillSelected)
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(aError));
})
yield doInterrupt(gPanel);
yield rdpInvoke(gThreadClient, gThreadClient.setBreakpoint, {
url: gSources.selectedValue,
line: 6
});
yield doResume(gPanel);
gDebuggee.secondCall();
const bpHit = waitForCaretAndScopes(gPanel, 6);
// Get the debuggee call off this tick so that we aren't accidentally
// blocking the yielding of bpHit which causes a deadlock.
executeSoon(() => gDebuggee.secondCall());
yield bpHit;
info("Switch to the second source.");
const sourceShown = waitForSourceShown(gPanel, SECOND_SOURCE_VALUE);
gSources.selectedValue = SECOND_SOURCE_VALUE;
yield sourceShown;
info("Pretty print the source.");
const prettyPrinted = waitForSourceShown(gPanel, SECOND_SOURCE_VALUE);
gDebugger.document.getElementById("pretty-print").click();
yield prettyPrinted;
yield resumeDebuggerThenCloseAndFinish(gPanel);
} catch (e) {
DevToolsUtils.reportException("browser_dbg_pretty-print-on-paused.js", e);
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
}
});
});
}
function testPaused() {
is(gDebugger.gThreadClient.paused, true,
"The thread should be paused");
}
function testSecondSourceIsSelected() {
ok(gSources.containsValue(EXAMPLE_URL + gSecondSourceLabel),
"The second source should be selected.");
}
function clickPrettyPrintButton() {
gDebugger.document.getElementById("pretty-print").click();
}
function testProgressBarShown() {
const deck = gDebugger.document.getElementById("editor-deck");
is(deck.selectedIndex, 2, "The progress bar should be shown");
}
function testSecondSourceIsStillSelected() {
ok(gSources.containsValue(EXAMPLE_URL + gSecondSourceLabel),
"The second source should still be selected.");
}
registerCleanupFunction(function() {
gTab = null;
gDebuggee = null;
gPanel = null;
gDebugger = null;
gThreadClient = null;
gSources = null;
});

View File

@ -22,6 +22,7 @@ let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxP
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
const { promiseInvoke } = require("devtools/async-utils");
let TargetFactory = devtools.TargetFactory;
let Toolbox = devtools.Toolbox;
@ -856,3 +857,23 @@ function attachAddonActorForUrl(aClient, aUrl) {
return deferred.promise;
}
function rdpInvoke(aClient, aMethod, ...args) {
return promiseInvoke(aClient, aMethod, ...args)
.then(({error, message }) => {
if (error) {
throw new Error(error + ": " + message);
}
});
}
function doResume(aPanel) {
const threadClient = aPanel.panelWin.gThreadClient;
return rdpInvoke(threadClient, threadClient.resume);
}
function doInterrupt(aPanel) {
const threadClient = aPanel.panelWin.gThreadClient;
return rdpInvoke(threadClient, threadClient.interrupt);
}

View File

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const gcli = require("gcli/index");
const EventEmitter = require("devtools/toolkit/event-emitter");
const eventEmitter = new EventEmitter();
let { Eyedropper, EyedropperManager } = require("devtools/eyedropper/eyedropper");
/**
* 'eyedropper' command
*/
exports.items = [{
name: "eyedropper",
description: gcli.lookup("eyedropperDesc"),
manual: gcli.lookup("eyedropperManual"),
buttonId: "command-button-eyedropper",
buttonClass: "command-button command-button-invertable",
tooltipText: gcli.lookup("eyedropperTooltip"),
state: {
isChecked: function(target) {
let chromeWindow = target.tab.ownerDocument.defaultView;
let dropper = EyedropperManager.getInstance(chromeWindow);
if (dropper) {
return true;
}
return false;
},
onChange: function(target, changeHandler) {
eventEmitter.on("changed", changeHandler);
},
offChange: function(target, changeHandler) {
eventEmitter.off("changed", changeHandler);
},
},
exec: function(args, context) {
let chromeWindow = context.environment.chromeWindow;
let target = context.environment.target;
let dropper = EyedropperManager.createInstance(chromeWindow);
dropper.open();
eventEmitter.emit("changed", target.tab);
dropper.once("destroy", () => {
eventEmitter.emit("changed", target.tab);
});
}
}];

View File

@ -48,6 +48,40 @@ const CLOSE_DELAY = 750;
const HEX_BOX_WIDTH = CANVAS_WIDTH + CANVAS_OFFSET * 2;
const HSL_BOX_WIDTH = 158;
/**
* Manage instances of eyedroppers for windows. Registering here isn't
* necessary for creating an eyedropper, but can be used for testing.
*/
let EyedropperManager = {
_instances: new WeakMap(),
getInstance: function(chromeWindow) {
return this._instances.get(chromeWindow);
},
createInstance: function(chromeWindow) {
let dropper = this.getInstance(chromeWindow);
if (dropper) {
return dropper;
}
dropper = new Eyedropper(chromeWindow);
this._instances.set(chromeWindow, dropper);
dropper.on("destroy", () => {
this.deleteInstance(chromeWindow);
});
return dropper;
},
deleteInstance: function(chromeWindow) {
this._instances.delete(chromeWindow);
}
}
exports.EyedropperManager = EyedropperManager;
/**
* Eyedropper widget. Once opened, shows zoomed area above current pixel and
* displays the color value of the center pixel. Clicking on the window will

View File

@ -7,6 +7,7 @@
JS_MODULES_PATH = 'modules/devtools/eyedropper'
EXTRA_JS_MODULES += [
'commands.js',
'eyedropper.js'
]

View File

@ -7,3 +7,4 @@ support-files =
[browser_eyedropper_basic.js]
skip-if = os == "win" && debug # bug 963492
[browser_eyedropper_cmd.js]

View File

@ -62,14 +62,3 @@ function inspectPage(dropper, click=true) {
function pressESC() {
EventUtils.synthesizeKey("VK_ESCAPE", { });
}
function dropperLoaded(dropper) {
if (dropper.loaded) {
return promise.resolve();
}
let deferred = promise.defer();
dropper.once("load", deferred.resolve);
return deferred.promise;
}

View File

@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the eyedropper command works
const TESTCASE_URI = TEST_BASE + "color-block.html";
const DIV_COLOR = "#0000FF";
function test() {
return Task.spawn(spawnTest).then(finish, helpers.handleError);
}
function spawnTest() {
let options = yield helpers.openTab(TESTCASE_URI);
yield helpers.openToolbar(options);
yield helpers.audit(options, [
{
setup: "eyedropper",
check: {
input: "eyedropper"
},
exec: { output: "" }
},
]);
yield inspectAndWaitForCopy();
yield helpers.closeToolbar(options);
yield helpers.closeTab(options);
}
function inspectAndWaitForCopy() {
let deferred = promise.defer();
waitForClipboard(DIV_COLOR, () => {
inspectPage(); // setup: inspect the page
}, deferred.resolve, deferred.reject);
return deferred.promise;
}
function inspectPage() {
let target = content.document.getElementById("test");
let win = content.window;
EventUtils.synthesizeMouse(target, 20, 20, { type: "mousemove" }, win);
let dropper = EyedropperManager.getInstance(window);
return dropperLoaded(dropper).then(() => {
EventUtils.synthesizeMouse(target, 30, 30, { type: "mousemove" }, win);
EventUtils.synthesizeMouse(target, 30, 30, {}, win);
});
}

View File

@ -4,10 +4,11 @@
const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/eyedropper/test/";
const TEST_HOST = 'mochi.test:8888';
const promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js").Promise;
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
const { Eyedropper } = require("devtools/eyedropper/eyedropper");
let { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
const { Eyedropper, EyedropperManager } = devtools.require("devtools/eyedropper/eyedropper");
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
waitForExplicitFinish();
@ -35,3 +36,10 @@ function addTab(uri) {
return deferred.promise;
}
function dropperLoaded(dropper) {
if (dropper.loaded) {
return promise.resolve();
}
return dropper.once("load");
}

View File

@ -573,7 +573,8 @@ Toolbox.prototype = {
"command-button-responsive",
"command-button-paintflashing",
"command-button-tilt",
"command-button-scratchpad"
"command-button-scratchpad",
"command-button-eyedropper"
].map(id => {
let button = this.doc.getElementById(id);
// Some buttons may not exist inside of Browser Toolbox

View File

@ -128,6 +128,7 @@ Tools.inspector = {
commands: [
"devtools/resize-commands",
"devtools/inspector/inspector-commands",
"devtools/eyedropper/commands.js"
],
preventClosingOnKey: true,

View File

@ -693,7 +693,7 @@ Tooltip.prototype = {
let iframe = this.doc.createElementNS(XHTML_NS, "iframe");
iframe.setAttribute("transparent", true);
iframe.setAttribute("width", "210");
iframe.setAttribute("height", "220");
iframe.setAttribute("height", "216");
iframe.setAttribute("flex", "1");
iframe.setAttribute("class", "devtools-tooltip-iframe");

View File

@ -19,6 +19,6 @@
</head>
<body role="application">
<div id="spectrum"></div>
<button id="eyedropper-button"></button>
<div id="eyedropper-button"></div>
</body>
</html>

View File

@ -3,17 +3,40 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#eyedropper-button {
background: url("chrome://browser/skin/devtools/eyedropper-black.png") no-repeat center;
width: 20px;
height: 20px;
-moz-margin-start: 6px;
border: 1px solid #ccc;
background-image: url("chrome://browser/skin/devtools/command-eyedropper.png");
width: 16px;
height: 16px;
background-size: 64px 16px;
background-position: 0 center;
background-repeat: no-repeat;
-moz-margin-start: 5px;
border-radius: 2px;
cursor: pointer;
}
.theme-light #eyedropper-button {
filter: url(chrome://browser/skin/devtools/filters.svg#invert);
border: 1px solid #AAA;
}
.theme-dark #eyedropper-button {
filter: url(chrome://browser/skin/devtools/filters.svg#colorpicker-invert);
border: 1px solid #444;
}
#eyedropper-button:hover {
background-position: -16px center;
}
#eyedropper-button:hover:active {
background-position: -32px center;
}
#eyedropper-button[checked=true] {
background-position: -48px center;
}
@media (min-resolution: 2dppx) {
#eyedropper-button {
background-image: url("chrome://browser/skin/devtools/command-eyedropper@2x.png");
}
}
/* Mix-in classes */

View File

@ -430,6 +430,30 @@ FunctionEnd
WriteRegStr SHCTX "$0\.xhtml" "" "FirefoxHTML"
${EndIf}
; Only add .oga if it's not present
${CheckIfRegistryKeyExists} "$0" ".oga" $7
${If} $7 == "false"
WriteRegStr SHCTX "$0\.oga" "" "FirefoxHTML"
${EndIf}
; Only add .ogg if it's not present
${CheckIfRegistryKeyExists} "$0" ".ogg" $7
${If} $7 == "false"
WriteRegStr SHCTX "$0\.ogg" "" "FirefoxHTML"
${EndIf}
; Only add .ogv if it's not present
${CheckIfRegistryKeyExists} "$0" ".ogv" $7
${If} $7 == "false"
WriteRegStr SHCTX "$0\.ogv" "" "FirefoxHTML"
${EndIf}
; Only add .pdf if it's not present
${CheckIfRegistryKeyExists} "$0" ".pdf" $7
${If} $7 == "false"
WriteRegStr SHCTX "$0\.pdf" "" "FirefoxHTML"
${EndIf}
; Only add webm if it's not present
${CheckIfRegistryKeyExists} "$0" ".webm" $7
${If} $7 == "false"

View File

@ -324,6 +324,10 @@ Section "Uninstall"
${un.RegCleanFileHandler} ".shtml" "FirefoxHTML"
${un.RegCleanFileHandler} ".xht" "FirefoxHTML"
${un.RegCleanFileHandler} ".xhtml" "FirefoxHTML"
${un.RegCleanFileHandler} ".oga" "FirefoxHTML"
${un.RegCleanFileHandler} ".ogg" "FirefoxHTML"
${un.RegCleanFileHandler} ".ogv" "FirefoxHTML"
${un.RegCleanFileHandler} ".pdf" "FirefoxHTML"
${un.RegCleanFileHandler} ".webm" "FirefoxHTML"
${EndIf}

View File

@ -165,6 +165,20 @@ inspectNodeDesc=CSS selector
# on what it does.
inspectNodeManual=A CSS selector for use with document.querySelector which identifies a single element
# LOCALIZATION NOTE (eyedropperDesc) A very short description of the 'eyedropper'
# command. See eyedropperManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which
# is why it should be as short as possible.
eyedropperDesc=Grab a color from the page
# LOCALIZATION NOTE (eyedropperManual) A fuller description of the 'eyedropper'
# command, displayed when the user asks for help on what it does.
eyedropperManual=Open a panel that magnifies an area of page to inspect pixels and copy color values
# LOCALIZATION NOTE (eyedropperTooltip) A string displayed as the
# tooltip of button in devtools toolbox which toggles the Eyedropper tool.
eyedropperTooltip=Grab a color from the page
# LOCALIZATION NOTE (tiltDesc) A very short description of the 'tilt'
# command. See tiltManual for a fuller description of what it does. This
# string is designed to be shown in a menu alongside the command name, which

View File

@ -0,0 +1,149 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"ContentSearch",
];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
const INBOUND_MESSAGE = "ContentSearch";
const OUTBOUND_MESSAGE = INBOUND_MESSAGE;
/**
* ContentSearch receives messages named INBOUND_MESSAGE and sends messages
* named OUTBOUND_MESSAGE. The data of each message is expected to look like
* { type, data }. type is the message's type (or subtype if you consider the
* type of the message itself to be INBOUND_MESSAGE), and data is data that is
* specific to the type.
*
* Inbound messages have the following types:
*
* GetState
* Retrieves the current search engine state.
* data: null
* ManageEngines
* Opens the search engine management window.
* data: null
* Search
* Performs a search.
* data: an object { engineName, searchString, whence }
* SetCurrentEngine
* Sets the current engine.
* data: the name of the engine
*
* Outbound messages have the following types:
*
* CurrentEngine
* Sent when the current engine changes.
* data: see _currentEngineObj
* State
* Sent in reply to GetState and when the state changes.
* data: see _currentStateObj
*/
this.ContentSearch = {
init: function () {
Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIMessageListenerManager).
addMessageListener(INBOUND_MESSAGE, this);
Services.obs.addObserver(this, "browser-search-engine-modified", false);
},
receiveMessage: function (msg) {
let methodName = "on" + msg.data.type;
if (methodName in this) {
this[methodName](msg, msg.data.data);
}
},
onGetState: function (msg, data) {
this._reply(msg, "State", this._currentStateObj());
},
onSearch: function (msg, data) {
let expectedDataProps = [
"engineName",
"searchString",
"whence",
];
for (let prop of expectedDataProps) {
if (!(prop in data)) {
Cu.reportError("Message data missing required property: " + prop);
return;
}
}
let browserWin = msg.target.ownerDocument.defaultView;
let engine = Services.search.getEngineByName(data.engineName);
browserWin.BrowserSearch.recordSearchInHealthReport(engine, data.whence);
let submission = engine.getSubmission(data.searchString, "", data.whence);
browserWin.loadURI(submission.uri.spec, null, submission.postData);
},
onSetCurrentEngine: function (msg, data) {
Services.search.currentEngine = Services.search.getEngineByName(data);
},
onManageEngines: function (msg, data) {
let browserWin = msg.target.ownerDocument.defaultView;
browserWin.BrowserSearch.searchBar.openManager(null);
},
observe: function (subj, topic, data) {
switch (topic) {
case "browser-search-engine-modified":
if (data == "engine-current") {
this._broadcast("CurrentEngine", this._currentEngineObj());
}
else if (data != "engine-default") {
// engine-default is always sent with engine-current and isn't otherwise
// relevant to content searches.
this._broadcast("State", this._currentStateObj());
}
break;
}
},
_reply: function (msg, type, data) {
msg.target.messageManager.sendAsyncMessage(...this._msgArgs(type, data));
},
_broadcast: function (type, data) {
Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIMessageListenerManager).
broadcastAsyncMessage(...this._msgArgs(type, data));
},
_msgArgs: function (type, data) {
return [OUTBOUND_MESSAGE, {
type: type,
data: data,
}];
},
_currentStateObj: function () {
return {
engines: Services.search.getVisibleEngines().map(engine => {
return {
name: engine.name,
iconURI: engine.getIconURLBySize(16, 16),
};
}),
currentEngine: this._currentEngineObj(),
};
},
_currentEngineObj: function () {
return {
name: Services.search.currentEngine.name,
logoURI: Services.search.currentEngine.getIconURLBySize(65, 26),
logo2xURI: Services.search.currentEngine.getIconURLBySize(130, 52),
};
},
};

View File

@ -11,6 +11,7 @@ EXTRA_JS_MODULES += [
'BrowserUITelemetry.jsm',
'ContentClick.jsm',
'ContentLinkHandler.jsm',
'ContentSearch.jsm',
'CustomizationTabPreloader.jsm',
'Feeds.jsm',
'NetworkPrioritizer.jsm',

View File

@ -1,10 +1,12 @@
[DEFAULT]
support-files =
head.js
uitour.*
contentSearch.js
image.png
uitour.*
[browser_BrowserUITelemetry_buckets.js]
[browser_ContentSearch.js]
[browser_NetworkPrioritizer.js]
skip-if = e10s # Bug 666804 - Support NetworkPrioritizer in e10s
[browser_SignInToWebsite.js]

View File

@ -0,0 +1,240 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const TEST_MSG = "ContentSearchTest";
const CONTENT_SEARCH_MSG = "ContentSearch";
const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js";
function generatorTest() {
// nextStep() drives the iterator returned by this function. This function's
// iterator in turn drives the iterator of each test below.
let currentTestIter = yield startNextTest();
let arg = undefined;
while (currentTestIter) {
try {
currentTestIter.send(arg);
arg = yield null;
}
catch (err if err instanceof StopIteration) {
currentTestIter = yield startNextTest();
arg = undefined;
}
}
}
function startNextTest() {
if (!gTests.length) {
setTimeout(() => nextStep(null), 0);
return;
}
let nextTestGen = gTests.shift();
let nextTestIter = nextTestGen();
addTab(() => {
info("Starting test " + nextTestGen.name);
nextStep(nextTestIter);
});
}
function addTest(testGen) {
gTests.push(testGen);
}
var gTests = [];
var gMsgMan;
addTest(function GetState() {
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "GetState",
});
let msg = yield waitForTestMsg("State");
checkMsg(msg, {
type: "State",
data: currentStateObj(),
});
});
addTest(function SetCurrentEngine() {
let newCurrentEngine = null;
let oldCurrentEngine = Services.search.currentEngine;
let engines = Services.search.getVisibleEngines();
for (let engine of engines) {
if (engine != oldCurrentEngine) {
newCurrentEngine = engine;
break;
}
}
if (!newCurrentEngine) {
info("Couldn't find a non-selected search engine, " +
"skipping this part of the test");
return;
}
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "SetCurrentEngine",
data: newCurrentEngine.name,
});
Services.obs.addObserver(function obs(subj, topic, data) {
info("Test observed " + data);
if (data == "engine-current") {
ok(true, "Test observed engine-current");
Services.obs.removeObserver(obs, "browser-search-engine-modified", false);
nextStep();
}
}, "browser-search-engine-modified", false);
info("Waiting for test to observe engine-current...");
waitForTestMsg("CurrentEngine");
let maybeMsg1 = yield null;
let maybeMsg2 = yield null;
let msg = maybeMsg1 || maybeMsg2;
ok(!!msg,
"Sanity check: One of the yields is for waitForTestMsg and should have " +
"therefore produced a message object");
checkMsg(msg, {
type: "CurrentEngine",
data: currentEngineObj(newCurrentEngine),
});
Services.search.currentEngine = oldCurrentEngine;
let msg = yield waitForTestMsg("CurrentEngine");
checkMsg(msg, {
type: "CurrentEngine",
data: currentEngineObj(oldCurrentEngine),
});
});
addTest(function ManageEngines() {
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "ManageEngines",
});
let winWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
winWatcher.registerNotification(function onOpen(subj, topic, data) {
if (topic == "domwindowopened" && subj instanceof Ci.nsIDOMWindow) {
subj.addEventListener("load", function onLoad() {
subj.removeEventListener("load", onLoad);
if (subj.document.documentURI ==
"chrome://browser/content/search/engineManager.xul") {
winWatcher.unregisterNotification(onOpen);
ok(true, "Observed search manager window open");
is(subj.opener, window,
"Search engine manager opener should be this chrome window");
subj.close();
nextStep();
}
});
}
});
info("Waiting for search engine manager window to open...");
yield null;
});
addTest(function modifyEngine() {
let engine = Services.search.currentEngine;
let oldAlias = engine.alias;
engine.alias = "ContentSearchTest";
let msg = yield waitForTestMsg("State");
checkMsg(msg, {
type: "State",
data: currentStateObj(),
});
engine.alias = oldAlias;
msg = yield waitForTestMsg("State");
checkMsg(msg, {
type: "State",
data: currentStateObj(),
});
});
addTest(function search() {
let engine = Services.search.currentEngine;
let data = {
engineName: engine.name,
searchString: "ContentSearchTest",
whence: "ContentSearchTest",
};
gMsgMan.sendAsyncMessage(TEST_MSG, {
type: "Search",
data: data,
});
let submissionURL =
engine.getSubmission(data.searchString, "", data.whence).uri.spec;
let listener = {
onStateChange: function (webProg, req, flags, status) {
let url = req.originalURI.spec;
info("onStateChange " + url);
let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
Ci.nsIWebProgressListener.STATE_START;
if ((flags & docStart) && webProg.isTopLevel && url == submissionURL) {
gBrowser.removeProgressListener(listener);
ok(true, "Search URL loaded");
req.cancel(Components.results.NS_ERROR_FAILURE);
nextStep();
}
}
};
gBrowser.addProgressListener(listener);
info("Waiting for search URL to load: " + submissionURL);
yield null;
});
function checkMsg(actualMsg, expectedMsgData) {
SimpleTest.isDeeply(actualMsg.data, expectedMsgData, "Checking message");
}
function waitForMsg(name, type, callback) {
info("Waiting for " + name + " message " + type + "...");
gMsgMan.addMessageListener(name, function onMsg(msg) {
info("Received " + name + " message " + msg.data.type + "\n");
if (msg.data.type == type) {
gMsgMan.removeMessageListener(name, onMsg);
(callback || nextStep)(msg);
}
});
}
function waitForTestMsg(type, callback) {
waitForMsg(TEST_MSG, type, callback);
}
function addTab(onLoad) {
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
tab.linkedBrowser.addEventListener("load", function load() {
tab.removeEventListener("load", load, true);
let url = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
gMsgMan = tab.linkedBrowser.messageManager;
gMsgMan.sendAsyncMessage(CONTENT_SEARCH_MSG, {
type: "AddToWhitelist",
data: ["about:blank"],
});
waitForMsg(CONTENT_SEARCH_MSG, "AddToWhitelistAck", () => {
gMsgMan.loadFrameScript(url, false);
onLoad();
});
}, true);
registerCleanupFunction(() => gBrowser.removeTab(tab));
}
function currentStateObj() {
return {
engines: Services.search.getVisibleEngines().map(engine => {
return {
name: engine.name,
iconURI: engine.getIconURLBySize(16, 16),
};
}),
currentEngine: currentEngineObj(),
};
}
function currentEngineObj(expectedCurrentEngine) {
if (expectedCurrentEngine) {
is(Services.search.currentEngine.name, expectedCurrentEngine.name,
"Sanity check: expected current engine");
}
return {
name: Services.search.currentEngine.name,
logoURI: Services.search.currentEngine.getIconURLBySize(65, 26),
logo2xURI: Services.search.currentEngine.getIconURLBySize(130, 52),
};
}

View File

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const TEST_MSG = "ContentSearchTest";
const SERVICE_EVENT_TYPE = "ContentSearchService";
const CLIENT_EVENT_TYPE = "ContentSearchClient";
// Forward events from the in-content service to the test.
content.addEventListener(SERVICE_EVENT_TYPE, event => {
sendAsyncMessage(TEST_MSG, event.detail);
});
// Forward messages from the test to the in-content service.
addMessageListener(TEST_MSG, msg => {
content.dispatchEvent(
new content.CustomEvent(CLIENT_EVENT_TYPE, {
detail: msg.data,
})
);
});

View File

@ -199,6 +199,8 @@ browser.jar:
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
* skin/classic/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
@ -297,7 +299,6 @@ browser.jar:
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
skin/classic/browser/devtools/eyedropper-black.png (../shared/devtools/images/eyedropper-black.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16.png
skin/classic/browser/sync-32.png

View File

@ -320,6 +320,8 @@ browser.jar:
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
* skin/classic/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
@ -418,7 +420,6 @@ browser.jar:
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
skin/classic/browser/devtools/eyedropper-black.png (../shared/devtools/images/eyedropper-black.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16.png

View File

@ -6,11 +6,4 @@
<feFuncB type="table" tableValues=".1 0"/>
</feComponentTransfer>
</filter>
<filter id="colorpicker-invert" x="0%" y="0%" width="100%" height="100%" >
<feComponentTransfer>
<feFuncR type="table" tableValues=".6 0"/>
<feFuncG type="table" tableValues=".6 0"/>
<feFuncB type="table" tableValues=".6 0"/>
</feComponentTransfer>
</filter>
</svg>

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -580,6 +580,9 @@
background-image: url("chrome://browser/skin/devtools/command-console.png");
}
#command-button-eyedropper > image {
background-image: url("chrome://browser/skin/devtools/command-eyedropper.png");
}
@media (min-resolution: 2dppx) {
#command-button-paintflashing > image {
@ -605,6 +608,10 @@
#command-button-splitconsole > image {
background-image: url("chrome://browser/skin/devtools/command-console@2x.png");
}
#command-button-eyedropper > image {
background-image: url("chrome://browser/skin/devtools/command-eyedropper@2x.png");
}
}
/* Tabs */

View File

@ -239,6 +239,8 @@ browser.jar:
skin/classic/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
@ -333,7 +335,6 @@ browser.jar:
skin/classic/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
skin/classic/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
skin/classic/browser/devtools/eyedropper-black.png (../shared/devtools/images/eyedropper-black.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/browser/sync-16.png
@ -599,6 +600,8 @@ browser.jar:
skin/classic/aero/browser/devtools/command-pick@2x.png (../shared/devtools/images/command-pick@2x.png)
skin/classic/aero/browser/devtools/command-console.png (../shared/devtools/images/command-console.png)
skin/classic/aero/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/aero/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/aero/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/aero/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
* skin/classic/aero/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
skin/classic/aero/browser/devtools/commandline.css (devtools/commandline.css)
@ -695,8 +698,6 @@ browser.jar:
skin/classic/aero/browser/devtools/app-manager/rocket.svg (../shared/devtools/app-manager/images/rocket.svg)
skin/classic/aero/browser/devtools/app-manager/noise.png (../shared/devtools/app-manager/images/noise.png)
skin/classic/aero/browser/devtools/app-manager/default-app-icon.png (../shared/devtools/app-manager/images/default-app-icon.png)
skin/classic/aero/browser/devtools/eyedropper-black.png (../shared/devtools/images/eyedropper-black.png)
#ifdef MOZ_SERVICES_SYNC
skin/classic/aero/browser/sync-16.png
skin/classic/aero/browser/sync-32.png

View File

@ -2485,8 +2485,6 @@ XULDocument::PrepareToWalk()
// Block onload until we've finished building the complete
// document content model.
BlockOnload();
nsContentSink::NotifyDocElementCreated(this);
}
// There'd better not be anything on the context stack at this

View File

@ -5,7 +5,6 @@ support-files =
overlay2_bug335375.xul
window_bug583948.xul
window_bug757137.xul
window_documentnotification.xul
[test_bug199692.xul]
[test_bug311681.xul]
@ -21,4 +20,3 @@ support-files =
[test_bug583948.xul]
[test_bug640158_overlay_persist.xul]
[test_bug757137.xul]
[test_documentnotification.xul]

View File

@ -1,40 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<body xmlns="http://www.w3.org/1999/xhtml">
<div id="content" style="display: none"/>
</body>
<script>
SimpleTest.waitForExplicitFinish();
var seenNotification = false;
function notify(subject, topic, data) {
seenNotification = true;
is(topic, "document-element-inserted", "Should be the right notification");
is(subject, otherWindow.document, "Should have been notified about the right window");
ok(subject.documentElement, "documentElement should be defined");
}
var obs = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService)
obs.addObserver(notify, "document-element-inserted", false);
var otherWindow = window.open("window_documentnotification.xul", "_new", "chrome");
otherWindow.addEventListener("load", function() {
ok(seenNotification, "Should have seen the document-element-inserted")
obs.removeObserver(notify, "document-element-inserted");
window.close();
SimpleTest.waitForFocus(function() {
SimpleTest.finish();
});
});
</script>
</window>

View File

@ -1,6 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<label value="window_documentnotification.xul"/>
</window>

View File

@ -136,6 +136,7 @@ public class CrashReporter extends Activity
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true);
editor.putBoolean(GeckoApp.PREFS_CRASHED, true);
editor.commit();
final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);

View File

@ -157,6 +157,7 @@ public abstract class GeckoApp
public static final String PREFS_OOM_EXCEPTION = "OOMException";
public static final String PREFS_VERSION_CODE = "versionCode";
public static final String PREFS_WAS_STOPPED = "wasStopped";
public static final String PREFS_CRASHED = "crashed";
public static final String PREFS_CLEANUP_TEMP_FILES = "cleanupTempFiles";
public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
@ -1772,10 +1773,17 @@ public abstract class GeckoApp
shouldRestore = true;
} else if (savedInstanceState != null ||
getSessionRestorePreference().equals("always") ||
getRestartFromIntent() ||
prefs.getBoolean(GeckoApp.PREFS_WAS_STOPPED, false)) {
getRestartFromIntent()) {
// We're coming back from a background kill by the OS, the user
// has chosen to always restore, we restarted, or we crashed.
// has chosen to always restore, or we restarted.
shouldRestore = true;
} else if (prefs.getBoolean(GeckoApp.PREFS_CRASHED, false)) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
prefs.edit().putBoolean(PREFS_CRASHED, false).commit();
}
});
shouldRestore = true;
}

View File

@ -21,6 +21,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
public final class TabsAccessor {
private static final String LOGTAG = "GeckoTabsAccessor";
@ -49,6 +50,7 @@ public final class TabsAccessor {
private static final String LOCAL_CLIENT_SELECTION = BrowserContract.Clients.GUID + " IS NULL";
private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
private static final Pattern FILTERED_URL_PATTERN = Pattern.compile("^(about|chrome|wyciwyg|file):.*$");
public static class RemoteTab {
public String title;
@ -149,9 +151,9 @@ public final class TabsAccessor {
int position = 0;
for (Tab tab : tabs) {
// Skip this tab if it has a null URL or is in private browsing mode
// Skip this tab if it has a null URL or is in private browsing mode, or is a filtered URL.
String url = tab.getURL();
if (url == null || tab.isPrivate())
if (url == null || tab.isPrivate() || isFilteredURL(url))
continue;
ContentValues values = new ContentValues();
@ -192,4 +194,13 @@ public final class TabsAccessor {
insertLocalTabs(cr, tabs);
updateLocalClient(cr);
}
/**
* Matches the supplied URL string against the set of URLs to filter.
*
* @return true if the supplied URL should be skipped; false otherwise.
*/
private static boolean isFilteredURL(String url) {
return FILTERED_URL_PATTERN.matcher(url).matches();
}
}

View File

@ -29,10 +29,15 @@ class PanelBackItemView extends LinearLayout {
title = (TextView) findViewById(R.id.title);
final ImageView image = (ImageView) findViewById(R.id.image);
Picasso.with(getContext())
.load(backImageUrl)
.placeholder(R.drawable.folder_up)
.into(image);
if (TextUtils.isEmpty(backImageUrl)) {
image.setImageResource(R.drawable.folder_up);
} else {
Picasso.with(getContext())
.load(backImageUrl)
.placeholder(R.drawable.folder_up)
.into(image);
}
}
public void updateFromFilter(FilterDetail filter) {

View File

@ -437,10 +437,8 @@ abstract class PanelLayout extends FrameLayout {
final String imageUrl = (emptyViewConfig == null) ? null : emptyViewConfig.getImageUrl();
final ImageView imageView = (ImageView) view.findViewById(R.id.home_empty_image);
if (imageUrl == null) {
Picasso.with(getContext())
.load(R.drawable.icon_home_empty_firefox)
.into(imageView);
if (TextUtils.isEmpty(imageUrl)) {
imageView.setImageResource(R.drawable.icon_home_empty_firefox);
} else {
Picasso.with(getContext())
.load(imageUrl)

View File

@ -37,7 +37,8 @@ public class JavascriptTest extends BaseTest {
mAsserter.dumpLog("Loading JavaScript test from " + url);
loadUrl(url);
final JavascriptMessageParser testMessageParser = new JavascriptMessageParser(mAsserter);
final JavascriptMessageParser testMessageParser =
new JavascriptMessageParser(mAsserter, false);
try {
while (!testMessageParser.isTestFinished()) {
if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {

View File

@ -110,8 +110,10 @@ public final class JavascriptBridge {
public JavascriptBridge(final Object target) {
mTarget = target;
mMethods = target.getClass().getMethods();
mLogParser = new JavascriptMessageParser(sAsserter);
mExpecter = sActions.expectGeckoEvent(EVENT_TYPE);
// The JS here is unrelated to a test harness, so we
// have our message parser end on assertion failure.
mLogParser = new JavascriptMessageParser(sAsserter, true);
}
/**

View File

@ -34,9 +34,22 @@ public final class JavascriptMessageParser {
private String lastTestName = "";
// Have we seen a message saying the test is finished?
private boolean testFinishedMessageSeen = false;
private final boolean endOnAssertionFailure;
public JavascriptMessageParser(final Assert asserter) {
/**
* Constructs a message parser for test result messages sent from JavaScript. When seeing an
* assertion failure, the message parser can use the given {@link org.mozilla.gecko.Assert}
* instance to immediately end the test (typically if the underlying JS framework is not able
* to end the test itself) or to swallow the Errors - this functionality is determined by the
* <code>endOnAssertionFailure</code> parameter.
*
* @param asserter The Assert instance to which test results should be passed.
* @param endOnAssertionFailure
* true if the test should end if we see a JS assertion failure, false otherwise.
*/
public JavascriptMessageParser(final Assert asserter, final boolean endOnAssertionFailure) {
this.asserter = asserter;
this.endOnAssertionFailure = endOnAssertionFailure;
}
public boolean isTestFinished() {
@ -61,8 +74,15 @@ public final class JavascriptMessageParser {
try {
asserter.ok(false, name, message);
} catch (AssertionFailedError e) {
// Swallow this exception. We want to see all the
// Javascript failures, not die on the very first one!
// Above, we call the assert, allowing it to log.
// Now we can end the test, if applicable.
if (this.endOnAssertionFailure) {
throw e;
}
// Otherwise, swallow the Error. The JS framework we're
// logging messages from is likely capable of ending tests
// when it needs to, and we want to see all of its failures,
// not just the first one!
}
} else if ("KNOWN-FAIL".equals(type)) {
asserter.todo(false, name, message);

View File

@ -32,6 +32,7 @@ skip-if = android_version == "10"
skip-if = android_version == "10" || processor == "x86"
[testDistribution]
[testDoorHanger]
[testFilterOpenTab]
# disabled on 2.3; bug 986172
skip-if = android_version == "10"
[testFindInPage]

View File

@ -0,0 +1,125 @@
package org.mozilla.gecko.tests;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.mozilla.gecko.PrivateTab;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.TabsAccessor;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.TabsProvider;
import android.content.ContentProvider;
import android.content.Context;
import android.database.Cursor;
/**
* Tests that local tabs are filtered prior to upload.
* - create a set of tabs and perists them through TabsAccessor.
* - verifies that tabs are filtered by querying.
*/
public class testFilterOpenTab extends ContentProviderTest {
private static final String[] TABS_PROJECTION_COLUMNS = new String[] {
BrowserContract.Tabs.TITLE,
BrowserContract.Tabs.URL,
BrowserContract.Clients.GUID,
BrowserContract.Clients.NAME
};
private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
/**
* Factory function that makes new ContentProvider instances.
* <p>
* We want a fresh provider each test, so this should be invoked in
* <code>setUp</code> before each individual test.
*/
protected static Callable<ContentProvider> sTabProviderCallable = new Callable<ContentProvider>() {
@Override
public ContentProvider call() {
return new TabsProvider();
}
};
private Cursor getTabsFromLocalClient() throws Exception {
return mProvider.query(BrowserContract.Tabs.CONTENT_URI,
TABS_PROJECTION_COLUMNS,
LOCAL_TABS_SELECTION,
null,
null);
}
private Tab createTab(int id, String url, boolean external, int parentId, String title) {
return new Tab((Context) getActivity(), id, url, external, parentId, title);
}
private Tab createPrivateTab(int id, String url, boolean external, int parentId, String title) {
return new PrivateTab((Context) getActivity(), id, url, external, parentId, title);
}
@Override
public void setUp() throws Exception {
super.setUp(sTabProviderCallable, BrowserContract.TABS_AUTHORITY, "tabs.db");
mTests.add(new TestInsertLocalTabs());
}
public void testFilterOpenTab() throws Exception {
for (int i = 0; i < mTests.size(); i++) {
Runnable test = mTests.get(i);
setTestName(test.getClass().getSimpleName());
test.run();
}
}
private class TestInsertLocalTabs extends TestCase {
@Override
public void test() throws Exception {
final String TITLE1 = "Google";
final String URL1 = "http://www.google.com/";
final String TITLE2 = "Mozilla Start Page";
final String URL2 = "about:home";
final String TITLE3 = "Chrome Weave URL";
final String URL3 = "chrome://weave/";
final String TITLE4 = "What You Cache Is What You Get";
final String URL4 = "wyciwyg://1/test.com";
final String TITLE5 = "Root Folder";
final String URL5 = "file:///";
// Create a list of local tabs.
List<Tab> tabs = new ArrayList<Tab>(6);
Tab tab1 = createTab(1, URL1, false, 0, TITLE1);
Tab tab2 = createTab(2, URL2, false, 0, TITLE2);
Tab tab3 = createTab(3, URL3, false, 0, TITLE3);
Tab tab4 = createTab(4, URL4, false, 0, TITLE4);
Tab tab5 = createTab(5, URL5, false, 0, TITLE5);
Tab tab6 = createPrivateTab(6, URL1, false, 0, TITLE1);
tabs.add(tab1);
tabs.add(tab2);
tabs.add(tab3);
tabs.add(tab4);
tabs.add(tab5);
tabs.add(tab6);
// Persist the created tabs.
TabsAccessor.persistLocalTabs(mResolver, tabs);
// Get the persisted tab and check if urls are filtered.
Cursor c = getTabsFromLocalClient();
assertCountIsAndClose(c, 1, 1 + " tabs entries found");
}
}
/**
* Assert that the provided cursor has the expected number of rows,
* closing the cursor afterwards.
*/
private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
try {
mAsserter.is(c.getCount(), expectedCount, message);
} finally {
c.close();
}
}
}

View File

@ -5,7 +5,7 @@
html {
background: #f0f0f0;
padding: 0 1em;
font-family: "Nokia Sans", Tahoma, sans-serif !important;
font-family: "Clear Sans", sans-serif !important;
font-size: 100% !important;
}

View File

@ -6,7 +6,7 @@
%include defines.inc
html {
font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
font-family: "Clear Sans",sans-serif;
font-size: 14px;
background-color: @color_about_background@;
-moz-text-size-adjust: none;

View File

@ -126,7 +126,7 @@ section:not([active]) {
.description,
#last-url {
font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
font-family: "Clear Sans",sans-serif;
font-size: 14px;
margin-bottom: 10px;
padding: 5px;

View File

@ -4,7 +4,7 @@
body {
-moz-text-size-adjust: none;
font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
font-family: "Clear Sans",sans-serif;
font-size: 23px;
color: #222222;
background-color: #ced7de;

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
body {
font-family: Roboto,"Droid Sans",helvetica,arial,clean,sans-serif;
font-family: "Clear Sans",sans-serif;
font-size: 14px;
}

View File

@ -321,7 +321,7 @@ body {
}
.toolbar {
font-family: "Droid Sans",helvetica,arial,clean,sans-serif;
font-family: "Clear Sans",sans-serif;
transition-property: visibility, opacity;
transition-duration: 0.7s;
visibility: visible;
@ -465,7 +465,7 @@ body {
.segmented-button > li > a {
display: block;
padding: 5px 0;
font-family: "Roboto",sans-serif;
font-family: "Clear Sans",sans-serif;
font-weight: lighter;
}

View File

@ -8,7 +8,7 @@ body {
padding: 0;
background-color: #ced7de;
-moz-user-select: none;
font-family: "Open Sans", sans-serif;
font-family: "Clear Sans",sans-serif;
-moz-text-size-adjust: none;
}

View File

@ -1222,6 +1222,7 @@ SearchCountMeasurementBase.prototype = Object.freeze({
SOURCES: [
"abouthome",
"contextmenu",
"newtab",
"searchbar",
"urlbar",
],

View File

@ -57,6 +57,7 @@ add_task(function test_record() {
}
yield provider.recordSearch(engine, "abouthome");
yield provider.recordSearch(engine, "contextmenu");
yield provider.recordSearch(engine, "newtab");
yield provider.recordSearch(engine, "searchbar");
yield provider.recordSearch(engine, "urlbar");
}

View File

@ -4781,25 +4781,27 @@ update(AddonThreadActor.prototype, {
onAttach: function(aRequest) {
if (!this.attached) {
Services.obs.addObserver(this, "document-element-inserted", false);
Services.obs.addObserver(this, "chrome-document-global-created", false);
Services.obs.addObserver(this, "content-document-global-created", false);
}
return ThreadActor.prototype.onAttach.call(this, aRequest);
},
disconnect: function() {
if (this.attached) {
Services.obs.removeObserver(this, "document-element-inserted");
Services.obs.removeObserver(this, "content-document-global-created");
Services.obs.removeObserver(this, "chrome-document-global-created");
}
return ThreadActor.prototype.disconnect.call(this);
},
/**
* Called when a new DOM document element is created. Check if the DOM was
* laoded from an add-on and if so make the window a debuggee.
* Called when a new DOM document global is created. Check if the DOM was
* loaded from an add-on and if so make the window a debuggee.
*/
observe: function(aSubject, aTopic, aData) {
let id = {};
if (mapURIToAddonID(aSubject.documentURIObject, id) && id.value === this.addonID) {
if (mapURIToAddonID(aSubject.location, id) && id.value === this.addonID) {
this.dbg.addDebuggee(aSubject.defaultView);
}
},