Merge fx-team to m-c. a=merge
CLOSED TREE
4
.gitignore
vendored
@ -52,8 +52,8 @@ parser/html/java/javaparser/
|
||||
.settings/
|
||||
|
||||
# Python virtualenv artifacts.
|
||||
python/psutil/*.so
|
||||
python/psutil/*.pyd
|
||||
python/psutil/**/*.so
|
||||
python/psutil/**/*.pyd
|
||||
python/psutil/build/
|
||||
|
||||
# Ignore chrome.manifest files from the devtools loader
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
const GRID_BOTTOM_EXTRA = 7; // title's line-height extends 7px past the margin
|
||||
const GRID_WIDTH_EXTRA = 1; // provide 1px buffer to allow for rounding error
|
||||
const SPONSORED_TAG_BUFFER = 2; // 2px buffer to clip off top of sponsored tag
|
||||
|
||||
/**
|
||||
* This singleton represents the grid that contains all sites.
|
||||
@ -175,6 +176,15 @@ let gGrid = {
|
||||
this._siteFragment.appendChild(site);
|
||||
},
|
||||
|
||||
/**
|
||||
* Test a tile at a given position for being pinned or history
|
||||
* @param position Position in sites array
|
||||
*/
|
||||
_isHistoricalTile: function Grid_isHistoricalTile(aPos) {
|
||||
let site = this.sites[aPos];
|
||||
return site && (site.isPinned() || site.link && site.link.type == "history");
|
||||
},
|
||||
|
||||
/**
|
||||
* Make sure the correct number of rows and columns are visible
|
||||
*/
|
||||
@ -195,9 +205,50 @@ let gGrid = {
|
||||
parseFloat(getComputedStyle(refCell).marginBottom);
|
||||
this._cellWidth = refCell.offsetWidth + this._cellMargin;
|
||||
}
|
||||
this._node.style.height = this._computeHeight() + "px";
|
||||
this._node.style.maxHeight = this._node.style.height;
|
||||
|
||||
let searchContainer = document.querySelector("#newtab-search-container");
|
||||
// Save search-container margin height
|
||||
if (this._searchContainerMargin === undefined) {
|
||||
this._searchContainerMargin = parseFloat(getComputedStyle(searchContainer).marginBottom) +
|
||||
parseFloat(getComputedStyle(searchContainer).marginTop);
|
||||
}
|
||||
|
||||
// Find the number of rows we can place into view port
|
||||
let availHeight = document.documentElement.clientHeight - this._cellMargin -
|
||||
searchContainer.offsetHeight - this._searchContainerMargin;
|
||||
let visibleRows = Math.floor(availHeight / this._cellHeight);
|
||||
|
||||
// Find the number of columns that fit into view port
|
||||
let maxGridWidth = gGridPrefs.gridColumns * this._cellWidth + GRID_WIDTH_EXTRA;
|
||||
// available width is current grid width, but no greater than maxGridWidth
|
||||
let availWidth = Math.min(document.querySelector("#newtab-grid").clientWidth,
|
||||
maxGridWidth);
|
||||
// finally get the number of columns we can fit into view port
|
||||
let gridColumns = Math.floor(availWidth / this._cellWidth);
|
||||
// walk sites backwords until a pinned or history tile is found or visibleRows reached
|
||||
let tileIndex = Math.min(gGridPrefs.gridRows * gridColumns, this.sites.length) - 1;
|
||||
while (tileIndex >= visibleRows * gridColumns) {
|
||||
if (this._isHistoricalTile(tileIndex)) {
|
||||
break;
|
||||
}
|
||||
tileIndex --;
|
||||
}
|
||||
|
||||
// Compute the actual number of grid rows we will display (potentially
|
||||
// with a scroll bar). tileIndex now points to a historical tile with
|
||||
// heighest index or to the last index of the visible row, if none found
|
||||
// Dividing tileIndex by number of tiles in a column gives the rows
|
||||
let gridRows = Math.floor(tileIndex / gridColumns) + 1;
|
||||
|
||||
// we need to set grid width, for otherwise the scrollbar may shrink
|
||||
// the grid when shown and cause grid layout to be different from
|
||||
// what being computed above. This, in turn, may cause scrollbar shown
|
||||
// for directory tiles, and introduce jitter when grid width is aligned
|
||||
// exactly on the column boundary
|
||||
this._node.style.width = gridColumns * this._cellWidth + "px";
|
||||
this._node.style.maxWidth = gGridPrefs.gridColumns * this._cellWidth +
|
||||
GRID_WIDTH_EXTRA + "px";
|
||||
this._node.style.height = this._computeHeight() + "px";
|
||||
this._node.style.maxHeight = this._computeHeight(gridRows) - SPONSORED_TAG_BUFFER + "px";
|
||||
}
|
||||
};
|
||||
|
@ -49,3 +49,4 @@ support-files =
|
||||
[browser_newtab_update.js]
|
||||
[browser_newtab_bug1145428.js]
|
||||
[browser_newtab_bug1178586.js]
|
||||
[browser_newtab_bug1194895.js]
|
||||
|
141
browser/base/content/test/newtab/browser_newtab_bug1194895.js
Normal file
@ -0,0 +1,141 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const PRELOAD_PREF = "browser.newtab.preload";
|
||||
const PREF_NEWTAB_COLUMNS = "browser.newtabpage.columns";
|
||||
const PREF_NEWTAB_ROWS = "browser.newtabpage.rows";
|
||||
|
||||
function populateDirectoryTiles() {
|
||||
let directoryTiles = [];
|
||||
let i = 0;
|
||||
while (i++ < 14) {
|
||||
directoryTiles.push({
|
||||
directoryId: i,
|
||||
url: "http://example" + i + ".com/",
|
||||
enhancedImageURI: "data:image/png;base64,helloWORLD",
|
||||
title: "dirtitle" + i,
|
||||
type: "affiliate"
|
||||
});
|
||||
}
|
||||
return directoryTiles;
|
||||
}
|
||||
|
||||
gDirectorySource = "data:application/json," + JSON.stringify({
|
||||
"directory": populateDirectoryTiles()
|
||||
});
|
||||
|
||||
|
||||
function runTests() {
|
||||
let origEnhanced = NewTabUtils.allPages.enhanced;
|
||||
let origCompareLinks = NewTabUtils.links.compareLinks;
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref(PRELOAD_PREF);
|
||||
Services.prefs.clearUserPref(PREF_NEWTAB_ROWS);
|
||||
Services.prefs.clearUserPref(PREF_NEWTAB_COLUMNS);
|
||||
NewTabUtils.allPages.enhanced = origEnhanced;
|
||||
NewTabUtils.links.compareLinks = origCompareLinks;
|
||||
});
|
||||
|
||||
// turn off preload to ensure grid updates on every setLinks
|
||||
Services.prefs.setBoolPref(PRELOAD_PREF, false);
|
||||
// set newtab to have three columns only
|
||||
Services.prefs.setIntPref(PREF_NEWTAB_COLUMNS, 3);
|
||||
Services.prefs.setIntPref(PREF_NEWTAB_ROWS, 5);
|
||||
|
||||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("enhanced"); // Toggle enhanced off
|
||||
|
||||
// Testing history tiles
|
||||
|
||||
// two rows of tiles should always fit on any screen
|
||||
yield setLinks("0,1,2,3,4,5");
|
||||
yield addNewTabPageTab();
|
||||
|
||||
// should do not see scrollbar since tiles fit into visible space
|
||||
checkGrid("0,1,2,3,4,5");
|
||||
ok(!hasScrollbar(), "no scrollbar");
|
||||
|
||||
// add enough tiles to cause extra two rows and observe scrollbar
|
||||
yield setLinks("0,1,2,3,4,5,6,7,8,9");
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8,9");
|
||||
ok(hasScrollbar(), "document has scrollbar");
|
||||
|
||||
// pin the last tile to make it stay at the bottom of the newtab
|
||||
pinCell(9);
|
||||
// block first 6 tiles, which should not remove the scroll bar
|
||||
// since the last tile is pinned in the nineth position
|
||||
for (let i = 0; i < 6; i++) {
|
||||
yield blockCell(0);
|
||||
}
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("6,7,8,,,,,,,9p");
|
||||
ok(hasScrollbar(), "document has scrollbar when tile is pinned to the last row");
|
||||
|
||||
// unpin the site: this will move tile up and make scrollbar disappear
|
||||
yield unpinCell(9);
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("6,7,8,9");
|
||||
ok(!hasScrollbar(), "no scrollbar when bottom row tile is unpinned");
|
||||
|
||||
// reset everything to clean slate
|
||||
NewTabUtils.restore();
|
||||
|
||||
// Testing directory tiles
|
||||
yield customizeNewTabPage("enhanced"); // Toggle enhanced on
|
||||
|
||||
// setup page with no history tiles to test directory only display
|
||||
yield setLinks([]);
|
||||
yield addNewTabPageTab();
|
||||
ok(!hasScrollbar(), "no scrollbar for directory tiles");
|
||||
|
||||
// introduce one history tile - it should occupy the last
|
||||
// available slot at the bottom of newtab and cause scrollbar
|
||||
yield setLinks("41");
|
||||
yield addNewTabPageTab();
|
||||
ok(hasScrollbar(), "adding low frecency history site causes scrollbar");
|
||||
|
||||
// set PREF_NEWTAB_ROWS to 4, that should clip off the history tile
|
||||
// and remove scroll bar
|
||||
Services.prefs.setIntPref(PREF_NEWTAB_ROWS, 4);
|
||||
yield addNewTabPageTab();
|
||||
ok(!hasScrollbar(), "no scrollbar if history tiles falls past max rows");
|
||||
|
||||
// restore max rows and watch scrollbar re-appear
|
||||
Services.prefs.setIntPref(PREF_NEWTAB_ROWS, 5);
|
||||
yield addNewTabPageTab();
|
||||
ok(hasScrollbar(), "scrollbar is back when max rows allow for bottom history tile");
|
||||
|
||||
// block that history tile, and watch scrollbar disappear
|
||||
yield blockCell(14);
|
||||
yield addNewTabPageTab();
|
||||
ok(!hasScrollbar(), "no scrollbar after bottom history tiles is blocked");
|
||||
|
||||
// Test well-populated user history - newtab has highly-frecent history sites
|
||||
// redefine compareLinks to always choose history tiles first
|
||||
NewTabUtils.links.compareLinks = function (aLink1, aLink2) {
|
||||
if (aLink1.type == aLink2.type) {
|
||||
return aLink2.frecency - aLink1.frecency ||
|
||||
aLink2.lastVisitDate - aLink1.lastVisitDate;
|
||||
}
|
||||
else {
|
||||
if (aLink2.type == "history") {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// add a row of history tiles, directory tiles will be clipped off, hence no scrollbar
|
||||
yield setLinks("31,32,33");
|
||||
yield addNewTabPageTab();
|
||||
ok(!hasScrollbar(), "no scrollbar when directory tiles follow history tiles");
|
||||
|
||||
// fill first four rows with history tiles and observer scrollbar
|
||||
yield setLinks("30,31,32,33,34,35,36,37,38,39");
|
||||
yield addNewTabPageTab();
|
||||
ok(hasScrollbar(), "scrollbar appears when history tiles need extra row");
|
||||
|
||||
}
|
@ -785,3 +785,11 @@ function customizeNewTabPage(aTheme) {
|
||||
|
||||
promise.then(TestRunner.next);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports presence of a scrollbar
|
||||
*/
|
||||
function hasScrollbar() {
|
||||
let docElement = getContentDocument().documentElement;
|
||||
return docElement.scrollHeight > docElement.clientHeight;
|
||||
}
|
||||
|
@ -77,4 +77,5 @@ skip-if = !crashreporter
|
||||
[browser_pageInfo_plugins.js]
|
||||
[browser_pluginCrashReportNonDeterminism.js]
|
||||
skip-if = !crashreporter || os == 'linux' # Bug 1152811
|
||||
[browser_private_clicktoplay.js]
|
||||
|
||||
|
235
browser/base/content/test/plugins/browser_private_clicktoplay.js
Normal file
@ -0,0 +1,235 @@
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
const gTestRoot = rootDir;
|
||||
const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
|
||||
|
||||
let gTestBrowser = null;
|
||||
let gNextTest = null;
|
||||
let gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
let gPrivateWindow = null;
|
||||
let gPrivateBrowser = null;
|
||||
|
||||
function pageLoad(aEvent) {
|
||||
// The plugin events are async dispatched and can come after the load event
|
||||
// This just allows the events to fire before we then go on to test the states
|
||||
executeSoon(gNextTest);
|
||||
gNextTest = null;
|
||||
}
|
||||
|
||||
function prepareTest(nextTest, url, window) {
|
||||
gNextTest = nextTest;
|
||||
if (!window)
|
||||
window = gTestBrowser.contentWindow;
|
||||
window.location = url;
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
clearAllPluginPermissions();
|
||||
gTestBrowser.removeEventListener("load", pageLoad, true);
|
||||
gBrowser.removeCurrentTab();
|
||||
if (gPrivateWindow) {
|
||||
gPrivateWindow.close();
|
||||
}
|
||||
window.focus();
|
||||
finish();
|
||||
}
|
||||
|
||||
function createPrivateWindow(nextTest, url) {
|
||||
gPrivateWindow = OpenBrowserWindow({private: true});
|
||||
ok(!!gPrivateWindow, "should have created a private window.");
|
||||
whenDelayedStartupFinished(gPrivateWindow, function() {
|
||||
gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
|
||||
gPrivateBrowser.addEventListener("load", pageLoad, true);
|
||||
gNextTest = function() {
|
||||
prepareTest(nextTest, url, gPrivateBrowser.contentWindow);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(function() {
|
||||
clearAllPluginPermissions();
|
||||
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||
});
|
||||
|
||||
let newTab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = newTab;
|
||||
gTestBrowser = gBrowser.selectedBrowser;
|
||||
gTestBrowser.addEventListener("load", pageLoad, true);
|
||||
|
||||
Services.prefs.setBoolPref("plugins.click_to_play", true);
|
||||
getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||
getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||
gNextTest = test1a;
|
||||
}
|
||||
|
||||
function test1a() {
|
||||
createPrivateWindow(test1b, gHttpTestRoot + "plugin_test.html");
|
||||
}
|
||||
|
||||
function test1b() {
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 1b, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gPrivateBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
|
||||
|
||||
// Check the button status
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
let button1 = gPrivateWindow.PopupNotifications.panel.firstChild._primaryButton;
|
||||
let button2 = gPrivateWindow.PopupNotifications.panel.firstChild._secondaryButton;
|
||||
is(button1.getAttribute("action"), "_singleActivateNow", "Test 1b, Blocked plugin in private window should have a activate now button");
|
||||
ok(button2.hidden, "Test 1b, Blocked plugin in a private window should not have a secondary button")
|
||||
|
||||
gPrivateWindow.close();
|
||||
prepareTest(test2a, gHttpTestRoot + "plugin_test.html");
|
||||
});
|
||||
}
|
||||
|
||||
function test2a() {
|
||||
// enable test plugin on this site
|
||||
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
||||
ok(popupNotification, "Test 2a, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gTestBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 2a, Plugin should not be activated");
|
||||
|
||||
// Simulate clicking the "Allow Now" button.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
PopupNotifications.panel.firstChild._secondaryButton.click();
|
||||
|
||||
let condition = function() objLoadingContent.activated;
|
||||
waitForCondition(condition, test2b, "Test 2a, Waited too long for plugin to activate");
|
||||
});
|
||||
}
|
||||
|
||||
function test2b() {
|
||||
createPrivateWindow(test2c, gHttpTestRoot + "plugin_test.html");
|
||||
}
|
||||
|
||||
function test2c() {
|
||||
let promise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||
promise.then(() => {
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 2c, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gPrivateBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(objLoadingContent.activated, "Test 2c, Plugin should be activated");
|
||||
|
||||
// Check the button status
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
|
||||
ok(buttonContainer.hidden, "Test 2c, Activated plugin in a private window should not have visible buttons");
|
||||
|
||||
clearAllPluginPermissions();
|
||||
gPrivateWindow.close();
|
||||
prepareTest(test3a, gHttpTestRoot + "plugin_test.html");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test3a() {
|
||||
// enable test plugin on this site
|
||||
let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
|
||||
ok(popupNotification, "Test 3a, Should have a click-to-play notification");
|
||||
|
||||
let plugin = gTestBrowser.contentDocument.getElementById("test");
|
||||
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
ok(!objLoadingContent.activated, "Test 3a, Plugin should not be activated");
|
||||
|
||||
// Simulate clicking the "Allow Always" button.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
PopupNotifications.panel.firstChild._secondaryButton.click();
|
||||
|
||||
let condition = function() objLoadingContent.activated;
|
||||
waitForCondition(condition, test3b, "Test 3a, Waited too long for plugin to activate");
|
||||
});
|
||||
}
|
||||
|
||||
function test3b() {
|
||||
createPrivateWindow(test3c, gHttpTestRoot + "plugin_test.html");
|
||||
}
|
||||
|
||||
function test3c() {
|
||||
let promise = TestUtils.topicObserved("PopupNotifications-updateNotShowing");
|
||||
promise.then(() => {
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 3c, Should have a click-to-play notification");
|
||||
|
||||
// Check the button status
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
|
||||
ok(buttonContainer.hidden, "Test 3c, Activated plugin in a private window should not have visible buttons");
|
||||
|
||||
prepareTest(test3d, gHttpTestRoot + "plugin_two_types.html", gPrivateBrowser.contentWindow);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function test3d() {
|
||||
let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
|
||||
ok(popupNotification, "Test 3d, Should have a click-to-play notification");
|
||||
|
||||
// Check the list item status
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
|
||||
"Shown");
|
||||
popupNotification.reshow();
|
||||
promiseShown.then(() => {
|
||||
let doc = gPrivateWindow.document;
|
||||
for (let item of gPrivateWindow.PopupNotifications.panel.firstChild.childNodes) {
|
||||
let allowalways = doc.getAnonymousElementByAttribute(item, "anonid", "allowalways");
|
||||
ok(allowalways, "Test 3d, should have list item for allow always");
|
||||
let allownow = doc.getAnonymousElementByAttribute(item, "anonid", "allownow");
|
||||
ok(allownow, "Test 3d, should have list item for allow now");
|
||||
let block = doc.getAnonymousElementByAttribute(item, "anonid", "block");
|
||||
ok(block, "Test 3d, should have list item for block");
|
||||
|
||||
if (item.action.pluginName === "Test") {
|
||||
is(item.value, "allowalways", "Test 3d, Plugin should bet set to 'allow always'");
|
||||
ok(!allowalways.hidden, "Test 3d, Plugin set to 'always allow' should have a visible 'always allow' action.");
|
||||
ok(allownow.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'allow now' action.");
|
||||
ok(block.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'block' action.");
|
||||
} else if (item.action.pluginName === "Second Test") {
|
||||
is(item.value, "block", "Test 3d, Second plugin should bet set to 'block'");
|
||||
ok(allowalways.hidden, "Test 3d, Plugin set to 'block' should have a visible 'always allow' action.");
|
||||
ok(!allownow.hidden, "Test 3d, Plugin set to 'block' should have a visible 'allow now' action.");
|
||||
ok(!block.hidden, "Test 3d, Plugin set to 'block' should have a visible 'block' action.");
|
||||
} else {
|
||||
ok(false, "Test 3d, Unexpected plugin '"+item.action.pluginName+"'");
|
||||
}
|
||||
}
|
||||
|
||||
finishTest();
|
||||
});
|
||||
}
|
@ -2402,6 +2402,20 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
}
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "center-item-warning-label").value = warningString;
|
||||
|
||||
let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin);
|
||||
|
||||
if (isWindowPrivate) {
|
||||
// TODO: temporary compromise of hiding some privacy leaks, remove once bug 892487 is fixed
|
||||
let allowalways = document.getAnonymousElementByAttribute(this, "anonid", "allowalways");
|
||||
let block = document.getAnonymousElementByAttribute(this, "anonid", "block");
|
||||
let allownow = document.getAnonymousElementByAttribute(this, "anonid", "allownow");
|
||||
|
||||
allowalways.hidden = curState !== "allowalways";
|
||||
block.hidden = curState !== "block";
|
||||
allownow.hidden = curState === "allowalways";
|
||||
}
|
||||
|
||||
if (url || linkHandler) {
|
||||
link.value = linkString;
|
||||
if (url) {
|
||||
@ -2577,6 +2591,8 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
<body><![CDATA[
|
||||
var action = this._items[0].action;
|
||||
var prePath = action.pluginPermissionPrePath;
|
||||
let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin);
|
||||
|
||||
let label, linkLabel, linkUrl, button1, button2;
|
||||
|
||||
@ -2615,6 +2631,11 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
default:
|
||||
Cu.reportError(Error("Unexpected blocklist state"));
|
||||
}
|
||||
|
||||
// TODO: temporary compromise, remove this once bug 892487 is fixed
|
||||
if (isWindowPrivate) {
|
||||
this._buttonContainer.hidden = true;
|
||||
}
|
||||
}
|
||||
else if (action.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED) {
|
||||
let linkElement =
|
||||
@ -2670,6 +2691,12 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
default:
|
||||
Cu.reportError(Error("Unexpected blocklist state"));
|
||||
}
|
||||
|
||||
// TODO: temporary compromise, remove this once bug 892487 is fixed
|
||||
if (isWindowPrivate) {
|
||||
button1.default = true;
|
||||
this._secondaryButton.hidden = true;
|
||||
}
|
||||
}
|
||||
this._setupDescription(label, action.pluginName, prePath);
|
||||
this._setupLink(linkLabel, action.detailsLink);
|
||||
|
@ -491,6 +491,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
disabled: checked,
|
||||
label: checkboxLabel,
|
||||
onChange: this.handleCheckboxChange,
|
||||
useEllipsis: true,
|
||||
value: location}),
|
||||
React.createElement("form", {onSubmit: this.handleFormSubmit},
|
||||
React.createElement("input", {className: "room-context-name",
|
||||
@ -508,7 +509,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
React.createElement("textarea", {className: "room-context-comments",
|
||||
onKeyDown: this.handleTextareaKeyDown,
|
||||
placeholder: mozL10n.get("context_edit_comments_placeholder"),
|
||||
rows: "3", type: "text",
|
||||
rows: "2", type: "text",
|
||||
valueLink: this.linkState("newRoomDescription")})
|
||||
),
|
||||
React.createElement("button", {className: "btn btn-info",
|
||||
|
@ -491,6 +491,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
disabled={checked}
|
||||
label={checkboxLabel}
|
||||
onChange={this.handleCheckboxChange}
|
||||
useEllipsis={true}
|
||||
value={location} />
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<input className="room-context-name"
|
||||
@ -508,7 +509,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
<textarea className="room-context-comments"
|
||||
onKeyDown={this.handleTextareaKeyDown}
|
||||
placeholder={mozL10n.get("context_edit_comments_placeholder")}
|
||||
rows="3" type="text"
|
||||
rows="2" type="text"
|
||||
valueLink={this.linkState("newRoomDescription")} />
|
||||
</form>
|
||||
<button className="btn btn-info"
|
||||
|
@ -524,6 +524,12 @@ html[dir="rtl"] .checkbox {
|
||||
background-image: url("../img/check.svg#check-disabled");
|
||||
}
|
||||
|
||||
.checkbox-label.ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ContextUrlView classes */
|
||||
|
||||
.context-content {
|
||||
|
@ -897,6 +897,10 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
background-color: #dbf7ff;
|
||||
}
|
||||
|
||||
.showing-room-name > .text-chat-entries > .text-chat-scroller > .context-url-view-wrapper {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.room-context {
|
||||
background: rgba(0,0,0,.8);
|
||||
border-top: 2px solid #444;
|
||||
@ -945,6 +949,7 @@ body[platform="win"] .share-service-dropdown.overflow > .dropdown-menu-item {
|
||||
|
||||
.room-context > .checkbox-wrapper {
|
||||
margin-bottom: .5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.room-context-label {
|
||||
@ -1638,7 +1643,6 @@ html[dir="rtl"] .text-chat-entry.received .text-chat-arrow {
|
||||
/* 18px for indent of .text-chat-arrow, 1px for border of .text-chat-entry > p,
|
||||
0.5rem for padding of .text-chat-entry > p */
|
||||
padding: calc(18px - 1px - 0.5rem);
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.text-chat-entry.special > p {
|
||||
|
@ -462,7 +462,6 @@ loop.shared.actions = (function() {
|
||||
// roomContextUrls: Array - Optional.
|
||||
// roomDescription: String - Optional.
|
||||
// roomName: String - Optional.
|
||||
roomOwner: String,
|
||||
roomToken: String,
|
||||
roomUrl: String,
|
||||
socialShareProviders: Array
|
||||
@ -477,7 +476,6 @@ loop.shared.actions = (function() {
|
||||
UpdateRoomInfo: Action.define("updateRoomInfo", {
|
||||
// description: String - Optional.
|
||||
// roomName: String - Optional.
|
||||
roomOwner: String,
|
||||
roomUrl: String
|
||||
// urls: Array - Optional.
|
||||
// See https://wiki.mozilla.org/Loop/Architecture/Context#Format_of_context.value
|
||||
|
@ -273,7 +273,6 @@ loop.store.ActiveRoomStore = (function() {
|
||||
roomContextUrls: roomData.decryptedContext.urls,
|
||||
roomDescription: roomData.decryptedContext.description,
|
||||
roomName: roomData.decryptedContext.roomName,
|
||||
roomOwner: roomData.roomOwner,
|
||||
roomUrl: roomData.roomUrl,
|
||||
socialShareProviders: this._mozLoop.getSocialShareProviders()
|
||||
}));
|
||||
@ -325,7 +324,6 @@ loop.store.ActiveRoomStore = (function() {
|
||||
}
|
||||
|
||||
var roomInfoData = new sharedActions.UpdateRoomInfo({
|
||||
roomOwner: result.roomOwner,
|
||||
roomUrl: result.roomUrl
|
||||
});
|
||||
|
||||
@ -396,7 +394,6 @@ loop.store.ActiveRoomStore = (function() {
|
||||
roomContextUrls: actionData.roomContextUrls,
|
||||
roomDescription: actionData.roomDescription,
|
||||
roomName: actionData.roomName,
|
||||
roomOwner: actionData.roomOwner,
|
||||
roomState: ROOM_STATES.READY,
|
||||
roomToken: actionData.roomToken,
|
||||
roomUrl: actionData.roomUrl,
|
||||
@ -420,7 +417,6 @@ loop.store.ActiveRoomStore = (function() {
|
||||
*/
|
||||
updateRoomInfo: function(actionData) {
|
||||
var newState = {
|
||||
roomOwner: actionData.roomOwner,
|
||||
roomUrl: actionData.roomUrl
|
||||
};
|
||||
// Iterate over the optional fields that _may_ be present on the actionData
|
||||
@ -456,7 +452,6 @@ loop.store.ActiveRoomStore = (function() {
|
||||
urls: roomData.decryptedContext.urls,
|
||||
description: roomData.decryptedContext.description,
|
||||
roomName: roomData.decryptedContext.roomName,
|
||||
roomOwner: roomData.roomOwner,
|
||||
roomUrl: roomData.roomUrl
|
||||
}));
|
||||
},
|
||||
|
@ -378,9 +378,13 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
|
||||
render: function() {
|
||||
var messageList;
|
||||
var showingRoomName = false;
|
||||
|
||||
if (this.props.showRoomName) {
|
||||
messageList = this.state.messageList;
|
||||
showingRoomName = this.state.messageList.some(function(item) {
|
||||
return item.contentType === CHAT_CONTENT_TYPES.ROOM_NAME;
|
||||
});
|
||||
} else {
|
||||
messageList = this.state.messageList.filter(function(item) {
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL ||
|
||||
@ -394,6 +398,7 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
});
|
||||
|
||||
var textChatViewClasses = React.addons.classSet({
|
||||
"showing-room-name": showingRoomName,
|
||||
"text-chat-view": true,
|
||||
"text-chat-disabled": !this.state.textChatEnabled,
|
||||
"text-chat-entries-empty": !messageList.length
|
||||
|
@ -378,9 +378,13 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
|
||||
render: function() {
|
||||
var messageList;
|
||||
var showingRoomName = false;
|
||||
|
||||
if (this.props.showRoomName) {
|
||||
messageList = this.state.messageList;
|
||||
showingRoomName = this.state.messageList.some(function(item) {
|
||||
return item.contentType === CHAT_CONTENT_TYPES.ROOM_NAME;
|
||||
});
|
||||
} else {
|
||||
messageList = this.state.messageList.filter(function(item) {
|
||||
return item.type !== CHAT_MESSAGE_TYPES.SPECIAL ||
|
||||
@ -394,6 +398,7 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
});
|
||||
|
||||
var textChatViewClasses = React.addons.classSet({
|
||||
"showing-room-name": showingRoomName,
|
||||
"text-chat-view": true,
|
||||
"text-chat-disabled": !this.state.textChatEnabled,
|
||||
"text-chat-entries-empty": !messageList.length
|
||||
|
@ -857,6 +857,9 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
disabled: React.PropTypes.bool,
|
||||
label: React.PropTypes.string,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
// If true, this will cause the label to be cut off at the end of the
|
||||
// first line with an ellipsis, and a tooltip supplied.
|
||||
useEllipsis: React.PropTypes.bool,
|
||||
// If `value` is not supplied, the consumer should rely on the boolean
|
||||
// `checked` state changes.
|
||||
value: React.PropTypes.string
|
||||
@ -868,6 +871,7 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
checked: false,
|
||||
disabled: false,
|
||||
label: null,
|
||||
useEllipsis: false,
|
||||
value: ""
|
||||
};
|
||||
},
|
||||
@ -910,6 +914,11 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
checked: this.state.checked,
|
||||
disabled: this.props.disabled
|
||||
};
|
||||
var labelClasses = {
|
||||
"checkbox-label": true,
|
||||
"ellipsis": this.props.useEllipsis
|
||||
};
|
||||
|
||||
if (this.props.additionalClass) {
|
||||
wrapperClasses[this.props.additionalClass] = true;
|
||||
}
|
||||
@ -918,9 +927,13 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
disabled: this.props.disabled,
|
||||
onClick: this._handleClick},
|
||||
React.createElement("div", {className: cx(checkClasses)}),
|
||||
this.props.label ?
|
||||
React.createElement("label", null, this.props.label) :
|
||||
null
|
||||
|
||||
this.props.label ?
|
||||
React.createElement("div", {className: cx(labelClasses),
|
||||
title: this.props.useEllipsis ? this.props.label : ""},
|
||||
this.props.label
|
||||
) : null
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -857,6 +857,9 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
disabled: React.PropTypes.bool,
|
||||
label: React.PropTypes.string,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
// If true, this will cause the label to be cut off at the end of the
|
||||
// first line with an ellipsis, and a tooltip supplied.
|
||||
useEllipsis: React.PropTypes.bool,
|
||||
// If `value` is not supplied, the consumer should rely on the boolean
|
||||
// `checked` state changes.
|
||||
value: React.PropTypes.string
|
||||
@ -868,6 +871,7 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
checked: false,
|
||||
disabled: false,
|
||||
label: null,
|
||||
useEllipsis: false,
|
||||
value: ""
|
||||
};
|
||||
},
|
||||
@ -910,6 +914,11 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
checked: this.state.checked,
|
||||
disabled: this.props.disabled
|
||||
};
|
||||
var labelClasses = {
|
||||
"checkbox-label": true,
|
||||
"ellipsis": this.props.useEllipsis
|
||||
};
|
||||
|
||||
if (this.props.additionalClass) {
|
||||
wrapperClasses[this.props.additionalClass] = true;
|
||||
}
|
||||
@ -918,9 +927,13 @@ loop.shared.views = (function(_, mozL10n) {
|
||||
disabled={this.props.disabled}
|
||||
onClick={this._handleClick}>
|
||||
<div className={cx(checkClasses)} />
|
||||
{this.props.label ?
|
||||
<label>{this.props.label}</label> :
|
||||
null}
|
||||
{
|
||||
this.props.label ?
|
||||
<div className={cx(labelClasses)}
|
||||
title={this.props.useEllipsis ? this.props.label : ""}>
|
||||
{this.props.label}
|
||||
</div> : null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ loop.StandaloneMozLoop = (function(mozL10n) {
|
||||
try {
|
||||
// We currently only require things we need rather than everything possible.
|
||||
callback(null, validate(responseData, {
|
||||
roomOwner: String,
|
||||
roomUrl: String
|
||||
}));
|
||||
} catch (err) {
|
||||
|
@ -310,7 +310,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
decryptedContext: {
|
||||
roomName: "Monkeys"
|
||||
},
|
||||
roomOwner: "Alfred",
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
|
||||
@ -353,7 +352,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
roomDescription: undefined,
|
||||
roomToken: fakeToken,
|
||||
roomName: fakeRoomData.decryptedContext.roomName,
|
||||
roomOwner: fakeRoomData.roomOwner,
|
||||
roomUrl: fakeRoomData.roomUrl,
|
||||
socialShareProviders: []
|
||||
}));
|
||||
@ -426,7 +424,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
|
||||
it("should dispatch an UpdateRoomInfo message with 'no data' failure if neither roomName nor context are supplied", function() {
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, {
|
||||
roomOwner: "Dan",
|
||||
roomUrl: "http://invalid"
|
||||
});
|
||||
|
||||
@ -436,7 +433,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo({
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA,
|
||||
roomOwner: "Dan",
|
||||
roomState: ROOM_STATES.READY,
|
||||
roomUrl: "http://invalid"
|
||||
}));
|
||||
@ -446,8 +442,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
it("should dispatch UpdateRoomInfo if mozLoop.rooms.get is successful", function() {
|
||||
var roomDetails = {
|
||||
roomName: "fakeName",
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "gavin"
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
|
||||
@ -470,12 +465,10 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
context: {
|
||||
value: "fakeContext"
|
||||
},
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "Mark"
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
expectedDetails = {
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "Mark"
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
|
||||
@ -597,7 +590,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
beforeEach(function() {
|
||||
fakeRoomInfo = {
|
||||
roomName: "Its a room",
|
||||
roomOwner: "Me",
|
||||
roomToken: "fakeToken",
|
||||
roomUrl: "http://invalid",
|
||||
socialShareProviders: []
|
||||
@ -615,7 +607,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
|
||||
var state = store.getStoreState();
|
||||
expect(state.roomName).eql(fakeRoomInfo.roomName);
|
||||
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
|
||||
expect(state.roomToken).eql(fakeRoomInfo.roomToken);
|
||||
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
|
||||
expect(state.socialShareProviders).eql([]);
|
||||
@ -628,7 +619,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
beforeEach(function() {
|
||||
fakeRoomInfo = {
|
||||
roomName: "Its a room",
|
||||
roomOwner: "Me",
|
||||
roomUrl: "http://invalid",
|
||||
urls: [{
|
||||
description: "fake site",
|
||||
@ -643,7 +633,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
|
||||
var state = store.getStoreState();
|
||||
expect(state.roomName).eql(fakeRoomInfo.roomName);
|
||||
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
|
||||
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
|
||||
expect(state.roomContextUrls).eql(fakeRoomInfo.urls);
|
||||
});
|
||||
@ -1503,7 +1492,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
beforeEach(function() {
|
||||
store.setupRoomInfo(new sharedActions.SetupRoomInfo({
|
||||
roomName: "Its a room",
|
||||
roomOwner: "Me",
|
||||
roomToken: "fakeToken",
|
||||
roomUrl: "http://invalid",
|
||||
socialShareProviders: []
|
||||
@ -1521,7 +1509,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
fake: "url"
|
||||
}
|
||||
},
|
||||
roomOwner: "you",
|
||||
roomUrl: "original"
|
||||
};
|
||||
|
||||
@ -1532,7 +1519,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
new sharedActions.UpdateRoomInfo({
|
||||
description: "fakeDescription",
|
||||
roomName: fakeRoomData.decryptedContext.roomName,
|
||||
roomOwner: fakeRoomData.roomOwner,
|
||||
roomUrl: fakeRoomData.roomUrl,
|
||||
urls: {
|
||||
fake: "url"
|
||||
@ -1549,7 +1535,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
fake: "url"
|
||||
}
|
||||
},
|
||||
roomOwner: "you",
|
||||
roomUrl: "original"
|
||||
};
|
||||
|
||||
@ -1564,7 +1549,6 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
decryptedContext: {
|
||||
roomName: "Its a room"
|
||||
},
|
||||
roomOwner: "Me",
|
||||
roomToken: "fakeToken",
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
|
@ -156,7 +156,6 @@ describe("loop.store.TextChatStore", function () {
|
||||
it("should add the room name to the list", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "Let's share!",
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake"
|
||||
}));
|
||||
|
||||
@ -173,7 +172,6 @@ describe("loop.store.TextChatStore", function () {
|
||||
it("should add the context to the list", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "Let's share!",
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake",
|
||||
urls: [{
|
||||
description: "A wonderful event",
|
||||
@ -206,7 +204,6 @@ describe("loop.store.TextChatStore", function () {
|
||||
|
||||
it("should not add more than one context message", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake",
|
||||
urls: [{
|
||||
description: "A wonderful event",
|
||||
@ -228,7 +225,6 @@ describe("loop.store.TextChatStore", function () {
|
||||
}]);
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake",
|
||||
urls: [{
|
||||
description: "A wonderful event2",
|
||||
@ -253,7 +249,6 @@ describe("loop.store.TextChatStore", function () {
|
||||
it("should not dispatch a LoopChatMessageAppended event", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "Let's share!",
|
||||
roomOwner: "Mark",
|
||||
roomUrl: "fake"
|
||||
}));
|
||||
|
||||
|
@ -453,6 +453,40 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
expect(view.getDOMNode().classList.contains("text-chat-entries-empty")).eql(false);
|
||||
});
|
||||
|
||||
it("should add a showing room name class when the view shows room names and it has a room name", function() {
|
||||
view = mountTestComponent({
|
||||
showRoomName: true
|
||||
});
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "Study",
|
||||
roomUrl: "Fake"
|
||||
}));
|
||||
|
||||
expect(view.getDOMNode().classList.contains("showing-room-name")).eql(true);
|
||||
});
|
||||
|
||||
it("shouldn't add a showing room name class when the view doesn't show room names", function() {
|
||||
view = mountTestComponent({
|
||||
showRoomName: false
|
||||
});
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "Study",
|
||||
roomUrl: "Fake"
|
||||
}));
|
||||
|
||||
expect(view.getDOMNode().classList.contains("showing-room-name")).eql(false);
|
||||
});
|
||||
|
||||
it("shouldn't add a showing room name class when the view doesn't have a name", function() {
|
||||
view = mountTestComponent({
|
||||
showRoomName: true
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().classList.contains("showing-room-name")).eql(false);
|
||||
});
|
||||
|
||||
it("should show timestamps from msgs sent more than 1 min apart", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
@ -540,7 +574,6 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "A wonderful surprise!",
|
||||
roomOwner: "Chris",
|
||||
roomUrl: "Fake"
|
||||
}));
|
||||
|
||||
@ -558,7 +591,6 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "A Very Long Conversation Name",
|
||||
roomOwner: "fake",
|
||||
roomUrl: "http://showcase",
|
||||
urls: [{
|
||||
description: "A wonderful page!",
|
||||
|
@ -941,7 +941,7 @@ describe("loop.shared.views", function() {
|
||||
view = mountTestComponent({ label: "Some label" });
|
||||
|
||||
var node = view.getDOMNode();
|
||||
expect(node.lastChild.localName).to.eql("label");
|
||||
expect(node.lastChild.localName).to.eql("div");
|
||||
expect(node.lastChild.textContent).to.eql("Some label");
|
||||
});
|
||||
|
||||
@ -974,6 +974,26 @@ describe("loop.shared.views", function() {
|
||||
var checkbox = view.getDOMNode().querySelector(".checkbox");
|
||||
expect(checkbox.classList.contains("checked")).eql(false);
|
||||
});
|
||||
|
||||
it("should add an ellipsis class when the prop is set", function() {
|
||||
view = mountTestComponent({
|
||||
label: "Some label",
|
||||
useEllipsis: true
|
||||
});
|
||||
|
||||
var label = view.getDOMNode().querySelector(".checkbox-label");
|
||||
expect(label.classList.contains("ellipsis")).eql(true);
|
||||
});
|
||||
|
||||
it("should not add an ellipsis class when the prop is not set", function() {
|
||||
view = mountTestComponent({
|
||||
label: "Some label",
|
||||
useEllipsis: false
|
||||
});
|
||||
|
||||
var label = view.getDOMNode().querySelector(".checkbox-label");
|
||||
expect(label.classList.contains("ellipsis")).eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#_handleClick", function() {
|
||||
|
@ -83,8 +83,7 @@ describe("loop.StandaloneMozLoop", function() {
|
||||
|
||||
var roomDetails = {
|
||||
roomName: "fakeName",
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "gavin"
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
|
@ -15,7 +15,6 @@ var fakeRooms = [
|
||||
}]
|
||||
},
|
||||
"roomUrl": "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
"roomOwner": "Alexis",
|
||||
"maxSize": 2,
|
||||
"creationTime": 1405517546,
|
||||
"ctime": 1405517546,
|
||||
@ -28,7 +27,6 @@ var fakeRooms = [
|
||||
"roomName": "Second Room Name"
|
||||
},
|
||||
"roomUrl": "http://localhost:3000/rooms/QzBbvGmIZWU",
|
||||
"roomOwner": "Alexis",
|
||||
"maxSize": 2,
|
||||
"creationTime": 1405517546,
|
||||
"ctime": 1405517546,
|
||||
@ -41,7 +39,6 @@ var fakeRooms = [
|
||||
"roomName": "UX Discussion"
|
||||
},
|
||||
"roomUrl": "http://localhost:3000/rooms/3jKS_Els9IU",
|
||||
"roomOwner": "Alexis",
|
||||
"maxSize": 2,
|
||||
"clientMaxSize": 2,
|
||||
"creationTime": 1405517546,
|
||||
|
@ -385,7 +385,6 @@
|
||||
// Update the text chat store with the room info.
|
||||
textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "A Very Long Conversation Name",
|
||||
roomOwner: "fake",
|
||||
roomUrl: "http://showcase",
|
||||
urls: [{
|
||||
description: "A wonderful page!",
|
||||
|
@ -385,7 +385,6 @@
|
||||
// Update the text chat store with the room info.
|
||||
textChatStore.updateRoomInfo(new sharedActions.UpdateRoomInfo({
|
||||
roomName: "A Very Long Conversation Name",
|
||||
roomOwner: "fake",
|
||||
roomUrl: "http://showcase",
|
||||
urls: [{
|
||||
description: "A wonderful page!",
|
||||
|
@ -27,7 +27,6 @@ support-files =
|
||||
[browser_bookmarkProperties_addKeywordForThisSearch.js]
|
||||
[browser_bookmarkProperties_readOnlyRoot.js]
|
||||
[browser_bookmarksProperties.js]
|
||||
skip-if = (os == 'win' && os_version == "6.2") # Bug 1178709
|
||||
[browser_drag_bookmarks_on_toolbar.js]
|
||||
skip-if = e10s # Bug ?????? - test fails - "Number of dragged items should be the same. - Got 0, expected 1"
|
||||
[browser_forgetthissite_single.js]
|
||||
|
@ -404,9 +404,8 @@ function open_properties_dialog() {
|
||||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
ww.unregisterNotification(windowObserver);
|
||||
var win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
win.addEventListener("focus", function (event) {
|
||||
win.removeEventListener("focus", arguments.callee, false);
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
waitForFocus(() => {
|
||||
// Windows has been loaded, execute our test now.
|
||||
executeSoon(function () {
|
||||
// Ensure overlay is loaded
|
||||
@ -418,7 +417,7 @@ function open_properties_dialog() {
|
||||
ok(false, "An error occured during test run: " + ex.message);
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}, win);
|
||||
}
|
||||
ww.registerNotification(windowObserver);
|
||||
|
||||
|
@ -260,7 +260,7 @@ let gSyncPane = {
|
||||
gSyncPane.signIn();
|
||||
return false;
|
||||
});
|
||||
setEventListener("verifiedManage", "command",
|
||||
setEventListener("verifiedManage", "click",
|
||||
gSyncPane.manageFirefoxAccount);
|
||||
setEventListener("fxaUnlinkButton", "click", function () {
|
||||
gSyncPane.unlinkFirefoxAccount(true);
|
||||
|
@ -36,9 +36,9 @@ body[globalTpEnabled] .showGlobalTpDisabled {
|
||||
background-size: 47px 26px;
|
||||
padding-inline-start: 87px;
|
||||
color: white;
|
||||
font-size: 24pt;
|
||||
font-size: 1.5em;
|
||||
font-weight: 200;
|
||||
line-height: 60pt;
|
||||
line-height: 2.5em;
|
||||
}
|
||||
|
||||
#main {
|
||||
@ -62,7 +62,7 @@ body[globalTpEnabled] .showGlobalTpDisabled {
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: 0;
|
||||
margin: 0;
|
||||
padding-inline-start: 8px;
|
||||
}
|
||||
|
||||
@ -100,10 +100,11 @@ li {
|
||||
}
|
||||
|
||||
#list-area > div {
|
||||
margin-inline-end: 1em;
|
||||
margin-inline-end: 3em;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
margin-bottom: 0.4em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@ -156,8 +157,7 @@ li {
|
||||
}
|
||||
|
||||
#startTour {
|
||||
display: inline-block;
|
||||
width: 16em;
|
||||
display: block;
|
||||
border-radius: 2px;
|
||||
background-color: var(--in-content-primary-button-background);
|
||||
color: var(--in-content-selected-text);
|
||||
|
@ -34,7 +34,7 @@
|
||||
<div id="bar" class="showPrivate">&privateBrowsing.title;</div>
|
||||
<div id="main" class="showPrivate">
|
||||
<div id="privateBrowsingSection"
|
||||
style="width: &aboutPrivateBrowsing.width;">
|
||||
style="width: &aboutPrivateBrowsing.width1;">
|
||||
<div class="sectionHeader">&aboutPrivateBrowsing.title;</div>
|
||||
<p>&aboutPrivateBrowsing.subtitle;</p>
|
||||
<div id="list-area">
|
||||
@ -59,7 +59,7 @@
|
||||
<a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore;</a>
|
||||
</div>
|
||||
<div id="trackingProtectionSection"
|
||||
style="width: &trackingProtection.width;">
|
||||
style="width: &trackingProtection.width1;">
|
||||
<div class="sectionHeader">&trackingProtection.title;
|
||||
<span id="tpEnabled"
|
||||
style="width: &trackingProtection.state.width;"
|
||||
|
@ -237,7 +237,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
// We make sure that the targeted node is selected
|
||||
// because we want to use the nodemenu that only works
|
||||
// for inspector.selection
|
||||
this.selection.setNodeFront(node, "breadcrumbs");
|
||||
this.navigateTo(node);
|
||||
|
||||
// Build a list of extra menu items that will be appended at the end of the
|
||||
// inspector node context menu.
|
||||
@ -262,10 +262,10 @@ HTMLBreadcrumbs.prototype = {
|
||||
item.setAttribute("type", "radio");
|
||||
item.setAttribute("label", this.prettyPrintNodeAsText(nodes[i]));
|
||||
|
||||
let selection = this.selection;
|
||||
let self = this;
|
||||
item.onmouseup = (function(node) {
|
||||
return function() {
|
||||
selection.setNodeFront(node, "breadcrumbs");
|
||||
self.navigateTo(node);
|
||||
};
|
||||
})(nodes[i]);
|
||||
|
||||
@ -353,39 +353,37 @@ HTMLBreadcrumbs.prototype = {
|
||||
* @param {DOMEvent} event.
|
||||
*/
|
||||
handleKeyPress: function(event) {
|
||||
let node = null;
|
||||
this._keyPromise = this._keyPromise || promise.resolve(null);
|
||||
let navigate = promise.resolve(null);
|
||||
|
||||
this._keyPromise = (this._keyPromise || promise.resolve(null)).then(() => {
|
||||
switch (event.keyCode) {
|
||||
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
|
||||
if (this.currentIndex != 0) {
|
||||
node = promise.resolve(this.nodeHierarchy[this.currentIndex - 1].node);
|
||||
navigate = promise.resolve(
|
||||
this.nodeHierarchy[this.currentIndex - 1].node);
|
||||
}
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
|
||||
if (this.currentIndex < this.nodeHierarchy.length - 1) {
|
||||
node = promise.resolve(this.nodeHierarchy[this.currentIndex + 1].node);
|
||||
navigate = promise.resolve(
|
||||
this.nodeHierarchy[this.currentIndex + 1].node);
|
||||
}
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_UP:
|
||||
node = this.walker.previousSibling(this.selection.nodeFront, {
|
||||
navigate = this.walker.previousSibling(this.selection.nodeFront, {
|
||||
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
|
||||
});
|
||||
break;
|
||||
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
|
||||
node = this.walker.nextSibling(this.selection.nodeFront, {
|
||||
navigate = this.walker.nextSibling(this.selection.nodeFront, {
|
||||
whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return node.then((node) => {
|
||||
if (node) {
|
||||
this.selection.setNodeFront(node, "breadcrumbs");
|
||||
}
|
||||
});
|
||||
return navigate.then(node => this.navigateTo(node));
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
},
|
||||
@ -470,6 +468,14 @@ HTMLBreadcrumbs.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
navigateTo: function(node) {
|
||||
if (node) {
|
||||
this.selection.setNodeFront(node, "breadcrumbs");
|
||||
} else {
|
||||
this.inspector.emit("breadcrumbs-navigation-cancelled");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Build a button representing the node.
|
||||
* @param {NodeFront} node The node from the page.
|
||||
@ -490,7 +496,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
};
|
||||
|
||||
button.onBreadcrumbsClick = () => {
|
||||
this.selection.setNodeFront(node, "breadcrumbs");
|
||||
this.navigateTo(node);
|
||||
};
|
||||
|
||||
button.onBreadcrumbsHover = () => {
|
||||
|
@ -31,6 +31,7 @@ support-files =
|
||||
|
||||
[browser_inspector_breadcrumbs.js]
|
||||
[browser_inspector_breadcrumbs_highlight_hover.js]
|
||||
[browser_inspector_breadcrumbs_keybinding.js]
|
||||
[browser_inspector_breadcrumbs_menu.js]
|
||||
[browser_inspector_breadcrumbs_mutations.js]
|
||||
[browser_inspector_delete-selected-node-01.js]
|
||||
|
@ -0,0 +1,109 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that the breadcrumbs keybindings work.
|
||||
|
||||
const TEST_URI = TEST_URL_ROOT + "doc_inspector_breadcrumbs.html";
|
||||
const TEST_DATA = [{
|
||||
desc: "Pressing left should select the parent <body>",
|
||||
key: "VK_LEFT",
|
||||
newSelection: "body"
|
||||
}, {
|
||||
desc: "Pressing left again should select the parent <html>",
|
||||
key: "VK_LEFT",
|
||||
newSelection: "html"
|
||||
}, {
|
||||
desc: "Pressing left again should stay on root <html>",
|
||||
key: "VK_LEFT",
|
||||
newSelection: "html"
|
||||
}, {
|
||||
desc: "Pressing right should go down to <body>",
|
||||
key: "VK_RIGHT",
|
||||
newSelection: "body"
|
||||
}, {
|
||||
desc: "Pressing right again should go down to #i2",
|
||||
key: "VK_RIGHT",
|
||||
newSelection: "#i2"
|
||||
}, {
|
||||
desc: "Continue down to #i21",
|
||||
key: "VK_RIGHT",
|
||||
newSelection: "#i21"
|
||||
}, {
|
||||
desc: "Continue down to #i211",
|
||||
key: "VK_RIGHT",
|
||||
newSelection: "#i211"
|
||||
}, {
|
||||
desc: "Continue down to #i2111",
|
||||
key: "VK_RIGHT",
|
||||
newSelection: "#i2111"
|
||||
}, {
|
||||
desc: "Pressing right once more should stay at leaf node #i2111",
|
||||
key: "VK_RIGHT",
|
||||
newSelection: "#i2111"
|
||||
}, {
|
||||
desc: "Go back to #i211",
|
||||
key: "VK_LEFT",
|
||||
newSelection: "#i211"
|
||||
}, {
|
||||
desc: "Go back to #i21",
|
||||
key: "VK_LEFT",
|
||||
newSelection: "#i21"
|
||||
}, {
|
||||
desc: "Pressing down should move to next sibling #i22",
|
||||
key: "VK_DOWN",
|
||||
newSelection: "#i22"
|
||||
}, {
|
||||
desc: "Pressing up should move to previous sibling #i21",
|
||||
key: "VK_UP",
|
||||
newSelection: "#i21"
|
||||
}, {
|
||||
desc: "Pressing up again should stay on #i21 as there's no previous sibling",
|
||||
key: "VK_UP",
|
||||
newSelection: "#i21"
|
||||
}, {
|
||||
desc: "Going back down to #i22",
|
||||
key: "VK_DOWN",
|
||||
newSelection: "#i22"
|
||||
}, {
|
||||
desc: "Pressing down again should stay on #i22 as there's no next sibling",
|
||||
key: "VK_DOWN",
|
||||
newSelection: "#i22"
|
||||
}];
|
||||
|
||||
add_task(function*() {
|
||||
let {inspector} = yield openInspectorForURL(TEST_URI);
|
||||
|
||||
info("Selecting the test node");
|
||||
yield selectNode("#i2", inspector);
|
||||
|
||||
info("Clicking on the corresponding breadcrumbs node to focus it");
|
||||
let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
|
||||
|
||||
let button = container.querySelector("button[checked]");
|
||||
button.click();
|
||||
|
||||
let currentSelection = "#id2";
|
||||
for (let {desc, key, newSelection} of TEST_DATA) {
|
||||
info(desc);
|
||||
|
||||
let onUpdated;
|
||||
if (newSelection !== currentSelection) {
|
||||
info("Expecting a new node to be selected");
|
||||
onUpdated = inspector.once("breadcrumbs-updated");
|
||||
} else {
|
||||
info("Expecting the same node to remain selected");
|
||||
onUpdated = inspector.once("breadcrumbs-navigation-cancelled");
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey(key, {});
|
||||
yield onUpdated;
|
||||
|
||||
let newNodeFront = yield getNodeFront(newSelection, inspector);
|
||||
is(newNodeFront, inspector.selection.nodeFront,
|
||||
"The current selection is correct");
|
||||
|
||||
currentSelection = newSelection;
|
||||
}
|
||||
});
|
@ -1056,6 +1056,10 @@ let UI = {
|
||||
},
|
||||
|
||||
updateToolboxFullscreenState: function() {
|
||||
if (projectList.sidebarsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let panel = document.querySelector("#deck").selectedPanel;
|
||||
let nbox = document.querySelector("#notificationbox");
|
||||
if (panel && panel.id == "deck-panel-details" &&
|
||||
|
@ -51,7 +51,7 @@
|
||||
ok(win.UI.toolboxIframe, "Toolbox iframe exists");
|
||||
|
||||
let nbox = win.document.querySelector("#notificationbox");
|
||||
ok(nbox.hasAttribute("toolboxfullscreen"), "Toolbox is fullsreen");
|
||||
ok(!nbox.hasAttribute("toolboxfullscreen"), "Toolbox is not fullscreen");
|
||||
|
||||
win.Cmds.showRuntimeDetails();
|
||||
|
||||
|
@ -8,10 +8,13 @@
|
||||
|
||||
<!ENTITY privateBrowsing.title "Private Browsing">
|
||||
|
||||
<!-- LOCALIZATION NOTE (aboutPrivateBrowsing.width):
|
||||
Width of the Private Browsing section.
|
||||
<!-- LOCALIZATION NOTE (aboutPrivateBrowsing.width1):
|
||||
Width of the Private Browsing section. This should depend primarily on the
|
||||
length of the headers and text, but should be roughly 1.5 times the width
|
||||
of the Tracking Protection section, and in general not much larger than
|
||||
30em to prevent the sections from wrapping on smaller window sizes.
|
||||
-->
|
||||
<!ENTITY aboutPrivateBrowsing.width "25em">
|
||||
<!ENTITY aboutPrivateBrowsing.width1 "30em">
|
||||
|
||||
<!-- LOCALIZATION NOTE (aboutPrivateBrowsing.subtitle,
|
||||
aboutPrivateBrowsing.info.forgotten, aboutPrivateBrowsing.info.kept):
|
||||
@ -36,11 +39,15 @@
|
||||
<!ENTITY aboutPrivateBrowsing.note1 "Please note that your employer or Internet service provider can still track the pages you visit.">
|
||||
<!ENTITY aboutPrivateBrowsing.learnMore "Learn More.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (trackingProtection.width):
|
||||
Width of the Tracking Protection section. This should be enough to
|
||||
accommodate the title as well as the enabled or disabled indicator.
|
||||
<!-- LOCALIZATION NOTE (trackingProtection.width1):
|
||||
Width of the Tracking Protection section. It is fine for the enabled or
|
||||
disabled indicator or the words in the title to wrap to the next line, but
|
||||
you can expand or reduce this section to fit better, as long as the width
|
||||
of the Private Browsing section is roughly 1.5 times the width of this one.
|
||||
Note that the required space may vary between platforms because fonts are
|
||||
different, so testing on Windows, Mac, and Linux is encouraged.
|
||||
-->
|
||||
<!ENTITY trackingProtection.width "22em">
|
||||
<!ENTITY trackingProtection.width1 "22em">
|
||||
<!ENTITY trackingProtection.title "Tracking Protection">
|
||||
|
||||
<!-- LOCALIZATION NOTE (trackingProtection.state.width):
|
||||
|
@ -546,6 +546,10 @@ box.requests-menu-status[code^="5"] {
|
||||
-moz-padding-start: 1em;
|
||||
}
|
||||
|
||||
.theme-dark #security-error-message {
|
||||
color: var(--theme-selection-color);
|
||||
}
|
||||
|
||||
#security-tabpanel {
|
||||
overflow: auto;
|
||||
}
|
||||
|
@ -28,6 +28,9 @@
|
||||
padding: 3px 5px;
|
||||
margin-inline-end: 4px;
|
||||
overflow: hidden;
|
||||
/* The latter two properties have a transition to handle the delayed hiding of
|
||||
the forward button when hovered. */
|
||||
transition: background-color 150ms ease, padding-left, padding-right;
|
||||
}
|
||||
|
||||
#identity-box:hover,
|
||||
@ -57,17 +60,15 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar > #identity-box {
|
||||
transition: padding-left, padding-right;
|
||||
}
|
||||
|
||||
@conditionalForwardWithUrlbar@ > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
|
||||
padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 4px);
|
||||
}
|
||||
|
||||
@conditionalForwardWithUrlbar@:hover:not([switchingtabs]) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
|
||||
/* forward button hiding is delayed when hovered */
|
||||
transition-delay: 100s;
|
||||
/* Forward button hiding is delayed when hovered, so we should use the same
|
||||
delay for the identity box. We handle both horizontal paddings (for LTR and
|
||||
RTL), the latter two delays here are for padding-left and padding-right. */
|
||||
transition-delay: 0s, 100s, 100s;
|
||||
}
|
||||
|
||||
@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #notification-popup-box[hidden] + #identity-box {
|
||||
|
@ -115,7 +115,7 @@
|
||||
.newtab-cell:not([ignorehover]) .newtab-link:hover,
|
||||
.newtab-site[dragged] {
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 0 6px 2px #4cb1ff;
|
||||
box-shadow: 0 0 6px 1px #add6ff;
|
||||
margin: -2px;
|
||||
}
|
||||
|
||||
@ -168,8 +168,8 @@
|
||||
|
||||
.newtab-site:hover .newtab-title {
|
||||
color: white;
|
||||
background-color: black;
|
||||
border: 1px solid black;
|
||||
background-color: #333;
|
||||
border: 1px solid #333;
|
||||
border-top: 1px solid white;
|
||||
}
|
||||
|
||||
|
@ -438,9 +438,8 @@
|
||||
#tabbrowser-tabs:not([overflow]) > .tabbrowser-tab[last-visible-tab]:not([visuallyselected]):not([beforehovered]):not(:hover)::after {
|
||||
width: 1px;
|
||||
-moz-margin-start: -1px;
|
||||
padding-top: calc(var(--tab-separator-margin) + 1px);
|
||||
padding-bottom: var(--tab-separator-margin);
|
||||
background-clip: content-box;
|
||||
margin-top: calc(var(--tab-separator-margin) + 1px);
|
||||
margin-bottom: var(--tab-separator-margin);
|
||||
background-color: currentColor;
|
||||
opacity: var(--tab-separator-opacity);
|
||||
content: "";
|
||||
|
@ -8,22 +8,23 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.db.BrowserContract.Clients;
|
||||
import org.mozilla.gecko.db.BrowserContract.Tabs;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class TabsProvider extends SharedBrowserDatabaseProvider {
|
||||
private static final long ONE_DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
|
||||
private static final long ONE_WEEK_IN_MILLISECONDS = 7 * ONE_DAY_IN_MILLISECONDS;
|
||||
private static final long THREE_WEEKS_IN_MILLISECONDS = 3 * ONE_WEEK_IN_MILLISECONDS;
|
||||
|
||||
static final String TABLE_TABS = "tabs";
|
||||
static final String TABLE_CLIENTS = "clients";
|
||||
|
||||
@ -33,6 +34,31 @@ public class TabsProvider extends SharedBrowserDatabaseProvider {
|
||||
static final int CLIENTS_ID = 603;
|
||||
static final int CLIENTS_RECENCY = 604;
|
||||
|
||||
// Exclude clients that are more than three weeks old and also any duplicates that are older than one week old.
|
||||
static final String EXCLUDE_STALE_CLIENTS_SUBQUERY =
|
||||
"(SELECT " + Clients.GUID +
|
||||
", " + Clients.NAME +
|
||||
", " + Clients.LAST_MODIFIED +
|
||||
", " + Clients.DEVICE_TYPE +
|
||||
" FROM " + TABLE_CLIENTS +
|
||||
" WHERE " + Clients.LAST_MODIFIED + " > %1$s " +
|
||||
" GROUP BY " + Clients.NAME +
|
||||
" UNION ALL " +
|
||||
" SELECT c." + Clients.GUID + " AS " + Clients.GUID +
|
||||
", c." + Clients.NAME + " AS " + Clients.NAME +
|
||||
", c." + Clients.LAST_MODIFIED + " AS " + Clients.LAST_MODIFIED +
|
||||
", c." + Clients.DEVICE_TYPE + " AS " + Clients.DEVICE_TYPE +
|
||||
" FROM " + TABLE_CLIENTS + " AS c " +
|
||||
" JOIN (" +
|
||||
" SELECT " + Clients.GUID +
|
||||
", " + "MAX( " + Clients.LAST_MODIFIED + ") AS " + Clients.LAST_MODIFIED +
|
||||
" FROM " + TABLE_CLIENTS +
|
||||
" WHERE (" + Clients.LAST_MODIFIED + " < %1$s" + " AND " + Clients.LAST_MODIFIED + " > %2$s) AND " +
|
||||
Clients.NAME + " NOT IN " + "( SELECT " + Clients.NAME + " FROM " + TABLE_CLIENTS + " WHERE " + Clients.LAST_MODIFIED + " > %1$s)" +
|
||||
" GROUP BY " + Clients.NAME +
|
||||
") AS c2" +
|
||||
" ON c." + Clients.GUID + " = c2." + Clients.GUID + ")";
|
||||
|
||||
static final String DEFAULT_TABS_SORT_ORDER = Clients.LAST_MODIFIED + " DESC, " + Tabs.LAST_USED + " DESC";
|
||||
static final String DEFAULT_CLIENTS_SORT_ORDER = Clients.LAST_MODIFIED + " DESC";
|
||||
static final String DEFAULT_CLIENTS_RECENCY_SORT_ORDER = "COALESCE(MAX(" + Tabs.LAST_USED + "), " + Clients.LAST_MODIFIED + ") DESC";
|
||||
@ -295,8 +321,15 @@ public class TabsProvider extends SharedBrowserDatabaseProvider {
|
||||
debug("Using sort order " + sortOrder + ".");
|
||||
}
|
||||
|
||||
final long oneWeekAgo = System.currentTimeMillis() - ONE_WEEK_IN_MILLISECONDS;
|
||||
final long threeWeeksAgo = System.currentTimeMillis() - THREE_WEEKS_IN_MILLISECONDS;
|
||||
|
||||
final String excludeStaleClientsTable = String.format(EXCLUDE_STALE_CLIENTS_SUBQUERY, oneWeekAgo, threeWeeksAgo);
|
||||
|
||||
qb.setProjectionMap(CLIENTS_RECENCY_PROJECTION_MAP);
|
||||
qb.setTables(TABLE_CLIENTS + " LEFT OUTER JOIN " + TABLE_TABS +
|
||||
|
||||
// Use a subquery to quietly exclude stale duplicate client records.
|
||||
qb.setTables(excludeStaleClientsTable + " AS " + TABLE_CLIENTS + " LEFT OUTER JOIN " + TABLE_TABS +
|
||||
" ON (" + projectColumn(TABLE_CLIENTS, Clients.GUID) +
|
||||
" = " + projectColumn(TABLE_TABS,Tabs.CLIENT_GUID) + ")");
|
||||
groupBy = projectColumn(TABLE_CLIENTS, Clients.GUID);
|
||||
|
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 178 B |
Before Width: | Height: | Size: 186 B After Width: | Height: | Size: 186 B |
Before Width: | Height: | Size: 178 B |
Before Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 204 B After Width: | Height: | Size: 204 B |
Before Width: | Height: | Size: 278 B After Width: | Height: | Size: 278 B |
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B |
@ -7,12 +7,12 @@
|
||||
|
||||
<!-- pressed state -->
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@drawable/tablet_tab_close_active"/>
|
||||
android:drawable="@drawable/tab_close_active"/>
|
||||
|
||||
<item android:state_checked="true"
|
||||
android:drawable="@drawable/tablet_tab_close_active"/>
|
||||
android:drawable="@drawable/tab_close_active"/>
|
||||
|
||||
<!-- normal mode -->
|
||||
<item android:drawable="@drawable/tablet_tab_close"/>
|
||||
<item android:drawable="@drawable/tab_close"/>
|
||||
|
||||
</selector>
|
@ -46,7 +46,7 @@
|
||||
android:background="@android:color/transparent"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/close_tab"
|
||||
android:src="@drawable/tablet_tab_close"
|
||||
android:src="@drawable/tab_close"
|
||||
android:duplicateParentState="true"/>
|
||||
|
||||
</merge>
|
||||
|
@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<org.mozilla.gecko.tabs.TabsLayoutItemView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/TabsItem"
|
||||
android:focusable="true"
|
||||
android:id="@+id/info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="6dip"
|
||||
android:paddingBottom="6dip"
|
||||
android:paddingLeft="1dip"
|
||||
android:paddingRight="1dip"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- We set state_private on this View dynamically in TabsListLayout. -->
|
||||
<org.mozilla.gecko.widget.TabThumbnailWrapper
|
||||
android:id="@+id/wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="6dip"
|
||||
android:padding="4dip"
|
||||
android:background="@drawable/tab_thumbnail"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<org.mozilla.gecko.tabs.TabsPanelThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tab_thumbnail_height"/>
|
||||
|
||||
<LinearLayout android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="#EFFF"
|
||||
android:layout_below="@id/thumbnail"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:padding="4dip"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:singleLine="true"
|
||||
android:duplicateParentState="true"/>
|
||||
|
||||
<ImageButton android:id="@+id/audio_playing"
|
||||
android:visibility="gone"
|
||||
android:layout_width="20dip"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/action_bar_button_inverse"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/tab_audio_playing"
|
||||
android:src="@drawable/tab_audio_playing"/>
|
||||
|
||||
<ImageButton android:id="@+id/close"
|
||||
style="@style/TabsItemClose"
|
||||
android:layout_width="32dip"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/action_bar_button_inverse"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/close_tab"
|
||||
android:src="@drawable/tab_close"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</org.mozilla.gecko.widget.TabThumbnailWrapper>
|
||||
|
||||
</org.mozilla.gecko.tabs.TabsLayoutItemView>
|
@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<org.mozilla.gecko.tabs.TabsLayoutItemView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/TabsItem"
|
||||
android:focusable="true"
|
||||
android:id="@+id/info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="12dip"
|
||||
android:paddingTop="6dip"
|
||||
android:paddingBottom="6dip"
|
||||
android:background="@drawable/tab_row">
|
||||
|
||||
<!-- We set state_private on this View dynamically in TabsListLayout. -->
|
||||
<org.mozilla.gecko.widget.TabThumbnailWrapper
|
||||
android:id="@+id/wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dip"
|
||||
android:background="@drawable/tab_thumbnail"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<org.mozilla.gecko.tabs.TabsPanelThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tab_thumbnail_height"/>
|
||||
|
||||
</org.mozilla.gecko.widget.TabThumbnailWrapper>
|
||||
|
||||
<LinearLayout android:layout_width="0dip"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1.0"
|
||||
android:paddingTop="4dip"
|
||||
android:paddingLeft="8dip"
|
||||
android:paddingRight="4dip">
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1.0"
|
||||
style="@style/TabLayoutItemTextAppearance"
|
||||
android:textColor="#FFFFFFFF"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="false"
|
||||
android:maxLines="4"
|
||||
android:duplicateParentState="true"/>
|
||||
|
||||
<ImageButton android:id="@+id/audio_playing"
|
||||
android:visibility="gone"
|
||||
android:layout_width="20dip"
|
||||
android:layout_height="20dip"
|
||||
android:gravity="bottom"
|
||||
android:background="@drawable/action_bar_button_inverse"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/tab_audio_playing"
|
||||
android:src="@drawable/tab_audio_playing"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton android:id="@+id/close"
|
||||
style="@style/TabsItemClose"
|
||||
android:layout_width="34dip"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/action_bar_button_inverse"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/close_tab"
|
||||
android:src="@drawable/tab_close"/>
|
||||
|
||||
</org.mozilla.gecko.tabs.TabsLayoutItemView>
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources>
|
||||
<item type="layout" name="tabs_layout_item_view">@layout/tabs_item_cell</item>
|
||||
</resources>
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources>
|
||||
<item type="layout" name="tabs_layout_item_view">@layout/tabs_item_cell</item>
|
||||
</resources>
|
@ -4,13 +4,10 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<resources>
|
||||
<item type="layout" name="tabs_layout_item_view">@layout/tabs_item_row</item>
|
||||
|
||||
<!-- These items are v11+ resources but are referenced in code shipped with
|
||||
API 9 builds. Since v11+ resources don't ship on API 9 builds, in order
|
||||
for the resource ID to be found (and thus compilation to succeed), we
|
||||
provide dummy values below. -->
|
||||
<item type="layout" name="tab_strip">@null</item>
|
||||
<item type="layout" name="tablet_tabs_item_cell">@null</item>
|
||||
<item type="layout" name="tabs_panel_back_button">@null</item>
|
||||
</resources>
|
||||
|
@ -8,6 +8,7 @@ package org.mozilla.gecko.restrictions;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.restrictions.RestrictedProfileConfiguration;
|
||||
import org.mozilla.gecko.restrictions.Restriction;
|
||||
import org.mozilla.gecko.sync.setup.Constants;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
@ -53,6 +54,10 @@ public class RestrictionProvider extends BroadcastReceiver {
|
||||
ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
|
||||
|
||||
for (Restriction restriction : RestrictedProfileConfiguration.DEFAULT_RESTRICTIONS) {
|
||||
if (restriction == Restriction.DISALLOW_LOCATION_SERVICE && !AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RestrictionEntry entry = createRestrictionEntryWithDefaultValue(context, restriction,
|
||||
oldRestrictions.getBoolean(restriction.name, true));
|
||||
entries.add(entry);
|
||||
|
@ -421,7 +421,7 @@ class TabsGridLayout extends GridView
|
||||
final private Button.OnClickListener mCloseClickListener;
|
||||
|
||||
public TabsGridLayoutAdapter(Context context) {
|
||||
super(context, R.layout.tablet_tabs_item_cell);
|
||||
super(context, R.layout.tabs_layout_item_view);
|
||||
|
||||
mCloseClickListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
|
@ -102,9 +102,7 @@ public class TabsLayoutItemView extends LinearLayout
|
||||
mAudioPlayingButton = (ImageView) findViewById(R.id.audio_playing);
|
||||
mThumbnailWrapper = (TabThumbnailWrapper) findViewById(R.id.wrapper);
|
||||
|
||||
if (HardwareUtils.isTablet()) {
|
||||
growCloseButtonHitArea();
|
||||
}
|
||||
growCloseButtonHitArea();
|
||||
|
||||
mAudioPlayingButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -6,21 +6,25 @@ package org.mozilla.tests.browser.junit3;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.background.db.CursorDumper;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.db.LocalTabsAccessor;
|
||||
import org.mozilla.gecko.db.RemoteClient;
|
||||
import org.mozilla.gecko.db.TabsAccessor;
|
||||
import org.mozilla.gecko.sync.repositories.android.BrowserContractHelpers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TestRemoteTabs extends InstrumentationTestCase {
|
||||
private static final long ONE_DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
|
||||
private static final long ONE_WEEK_IN_MILLISECONDS = 7 * ONE_DAY_IN_MILLISECONDS;
|
||||
private static final long THREE_WEEKS_IN_MILLISECONDS = 3 * ONE_WEEK_IN_MILLISECONDS;
|
||||
|
||||
public void testGetClientsWithoutTabsByRecencyFromCursor() throws Exception {
|
||||
final Uri uri = BrowserContractHelpers.CLIENTS_CONTENT_URI;
|
||||
final ContentResolver cr = getInstrumentation().getTargetContext().getContentResolver();
|
||||
@ -124,4 +128,89 @@ public class TestRemoteTabs extends InstrumentationTestCase {
|
||||
cpc.release();
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetRecentRemoteClientsUpToOneWeekOld() throws Exception {
|
||||
final Uri uri = BrowserContractHelpers.CLIENTS_CONTENT_URI;
|
||||
final Context context = getInstrumentation().getTargetContext();
|
||||
final String profileName = GeckoProfile.get(context).getName();
|
||||
final ContentResolver cr = context.getContentResolver();
|
||||
final ContentProviderClient cpc = cr.acquireContentProviderClient(uri);
|
||||
final LocalTabsAccessor accessor = new LocalTabsAccessor(profileName);
|
||||
|
||||
try {
|
||||
// Start Clean
|
||||
cpc.delete(uri, null, null);
|
||||
final Cursor allClients = cpc.query(uri, null, null, null, null);
|
||||
try {
|
||||
assertEquals(0, allClients.getCount());
|
||||
} finally {
|
||||
allClients.close();
|
||||
}
|
||||
|
||||
// Insert a local and remote1 client record, neither with tabs.
|
||||
final long now = System.currentTimeMillis();
|
||||
// Local client has GUID = null.
|
||||
final ContentValues local = new ContentValues();
|
||||
local.put(BrowserContract.Clients.NAME, "local");
|
||||
local.put(BrowserContract.Clients.LAST_MODIFIED, now + 1);
|
||||
// Remote clients have GUID != null.
|
||||
final ContentValues remote1 = new ContentValues();
|
||||
remote1.put(BrowserContract.Clients.GUID, "guid1");
|
||||
remote1.put(BrowserContract.Clients.NAME, "remote1");
|
||||
remote1.put(BrowserContract.Clients.LAST_MODIFIED, now + 2);
|
||||
|
||||
// Insert a Remote Client that is 6 days old.
|
||||
final ContentValues remote2 = new ContentValues();
|
||||
remote2.put(BrowserContract.Clients.GUID, "guid2");
|
||||
remote2.put(BrowserContract.Clients.NAME, "remote2");
|
||||
remote2.put(BrowserContract.Clients.LAST_MODIFIED, now - ONE_WEEK_IN_MILLISECONDS + ONE_DAY_IN_MILLISECONDS);
|
||||
|
||||
// Insert a Remote Client with the same name as previous but with more than 3 weeks old
|
||||
final ContentValues remote3 = new ContentValues();
|
||||
remote3.put(BrowserContract.Clients.GUID, "guid21");
|
||||
remote3.put(BrowserContract.Clients.NAME, "remote2");
|
||||
remote3.put(BrowserContract.Clients.LAST_MODIFIED, now - THREE_WEEKS_IN_MILLISECONDS - ONE_DAY_IN_MILLISECONDS);
|
||||
|
||||
// Insert another remote client with the same name as previous but with 3 weeks - 1 day old.
|
||||
final ContentValues remote4 = new ContentValues();
|
||||
remote4.put(BrowserContract.Clients.GUID, "guid22");
|
||||
remote4.put(BrowserContract.Clients.NAME, "remote2");
|
||||
remote4.put(BrowserContract.Clients.LAST_MODIFIED, now - THREE_WEEKS_IN_MILLISECONDS + ONE_DAY_IN_MILLISECONDS);
|
||||
|
||||
// Insert a Remote Client that is exactly one week old.
|
||||
final ContentValues remote5 = new ContentValues();
|
||||
remote5.put(BrowserContract.Clients.GUID, "guid3");
|
||||
remote5.put(BrowserContract.Clients.NAME, "remote3");
|
||||
remote5.put(BrowserContract.Clients.LAST_MODIFIED, now - ONE_WEEK_IN_MILLISECONDS);
|
||||
|
||||
ContentValues[] values = new ContentValues[]{local, remote1, remote2, remote3, remote4, remote5};
|
||||
int inserted = cpc.bulkInsert(uri, values);
|
||||
assertEquals(values.length, inserted);
|
||||
|
||||
final Cursor remoteClients =
|
||||
accessor.getRemoteClientsByRecencyCursor(context);
|
||||
|
||||
try {
|
||||
CursorDumper.dumpCursor(remoteClients);
|
||||
// Local client is not included.
|
||||
// (remote1, guid1), (remote2, guid2), (remote3, guid3) are expected.
|
||||
assertEquals(3, remoteClients.getCount());
|
||||
|
||||
// Check the inner data, according to recency.
|
||||
List<RemoteClient> recentRemoteClientsList =
|
||||
accessor.getClientsWithoutTabsByRecencyFromCursor(remoteClients);
|
||||
assertEquals(3, recentRemoteClientsList.size());
|
||||
assertEquals("remote1", recentRemoteClientsList.get(0).name);
|
||||
assertEquals("guid1", recentRemoteClientsList.get(0).guid);
|
||||
assertEquals("remote2", recentRemoteClientsList.get(1).name);
|
||||
assertEquals("guid2", recentRemoteClientsList.get(1).guid);
|
||||
assertEquals("remote3", recentRemoteClientsList.get(2).name);
|
||||
assertEquals("guid3", recentRemoteClientsList.get(2).guid);
|
||||
} finally {
|
||||
remoteClients.close();
|
||||
}
|
||||
} finally {
|
||||
cpc.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,8 +79,9 @@ Services.cpmm.addMessageListener("performance-stats-service-release", function(m
|
||||
if (!isContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep only the probes that do not appear in the payload
|
||||
let probes = gMonitor.getProbeNames
|
||||
let probes = gMonitor.probeNames
|
||||
.filter(x => msg.data.payload.indexOf(x) == -1);
|
||||
gMonitor = PerformanceStats.getMonitor(probes);
|
||||
});
|
||||
|
@ -41,6 +41,7 @@ Structure::
|
||||
loadPath: <string>, // where the engine line is located; missing if no default
|
||||
submissionURL: <string> // missing if no default or for user-installed engines
|
||||
},
|
||||
searchCohort: <string>, // optional, contains an identifier for any active search A/B experiments
|
||||
e10sEnabled: <bool>, // whether e10s is on, i.e. browser tabs open by default in a different process
|
||||
telemetryEnabled: <bool>, // false on failure
|
||||
isInOptoutSample: <bool>, // whether this client is part of the opt-out sample
|
||||
|