MozReview-Commit-ID: 4CJDJBAcVPL
This commit is contained in:
Wes Kocher 2017-09-08 13:41:21 -07:00
Родитель e9b2678c1d 2a30786cad
Коммит 5ee13ebe8a
263 изменённых файлов: 3616 добавлений и 2374 удалений

Просмотреть файл

@ -8,6 +8,7 @@
# UITour
origin uitour 1 https://www.mozilla.org
origin uitour 1 https://screenshots.firefox.com
origin uitour 1 https://support.mozilla.org
origin uitour 1 https://addons.mozilla.org
origin uitour 1 https://discovery.addons.mozilla.org

Просмотреть файл

@ -1153,7 +1153,7 @@ var PlacesToolbarHelper = {
return document.getElementById("PlacesToolbar");
},
init: function PTH_init(forceToolbarOverflowCheck) {
init: function PTH_init() {
let viewElt = this._viewElt;
if (!viewElt || viewElt._placesView)
return;
@ -1180,15 +1180,12 @@ var PlacesToolbarHelper = {
}
new PlacesToolbar(this._place);
if (forceToolbarOverflowCheck) {
viewElt._placesView.updateOverflowStatus();
}
},
handleEvent(event) {
switch (event.type) {
case "toolbarvisibilitychange":
if (event.target == this._viewElt.parentNode.parentNode)
if (event.target == this._getParentToolbar(this._viewElt))
this._resetView();
break;
}
@ -1214,7 +1211,7 @@ var PlacesToolbarHelper = {
customizeDone: function PTH_customizeDone() {
this._isCustomizing = false;
this.init(true);
this.init();
},
onPlaceholderCommand() {
@ -1265,7 +1262,7 @@ var PlacesToolbarHelper = {
if (this._viewElt._placesView) {
this._viewElt._placesView.uninit();
}
this.init(true);
this.init();
}
},
};

Просмотреть файл

@ -7494,20 +7494,20 @@ var gIdentityHandler = {
}
// Push the appropriate strings out to the UI
this._connectionIcon.tooltipText = tooltip;
this._connectionIcon.setAttribute("tooltiptext", tooltip);
if (this._isExtensionPage) {
let extensionName = extensionNameFromURI(this._uri);
this._extensionIcon.tooltipText = gNavigatorBundle.getFormattedString(
"identity.extension.tooltip", [extensionName]);
this._extensionIcon.setAttribute("tooltiptext",
gNavigatorBundle.getFormattedString("identity.extension.tooltip", [extensionName]));
}
this._identityIconLabels.tooltipText = tooltip;
this._identityIcon.tooltipText = gNavigatorBundle.getString("identity.icon.tooltip");
this._identityIconLabel.value = icon_label;
this._identityIconCountryLabel.value = icon_country_label;
this._identityIconLabels.setAttribute("tooltiptext", tooltip);
this._identityIcon.setAttribute("tooltiptext", gNavigatorBundle.getString("identity.icon.tooltip"));
this._identityIconLabel.setAttribute("value", icon_label);
this._identityIconCountryLabel.setAttribute("value", icon_country_label);
// Set cropping and direction
this._identityIconLabel.crop = icon_country_label ? "end" : "center";
this._identityIconLabel.setAttribute("crop", icon_country_label ? "end" : "center");
this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
// Hide completely if the organization label is empty
this._identityIconLabel.parentNode.collapsed = !icon_label;

Просмотреть файл

@ -35,6 +35,8 @@ input[type=button] {
transition: opacity 100ms ease-out;
-moz-box-align: center;
-moz-box-pack: center;
position: relative;
left: -50%;
}
#newtab-undo-container[undo-disabled] {
@ -65,7 +67,7 @@ input[type=button] {
#newtab-margin-undo-container {
display: -moz-box;
left: 6px;
left: 50%;
position: absolute;
top: 6px;
z-index: 1;

Просмотреть файл

@ -6,8 +6,6 @@ skip-if = asan || debug # Bug 1382809, bug 1369959
[browser_startup.js]
[browser_startup_content.js]
skip-if = !e10s
[browser_startup_images.js]
skip-if = !debug
[browser_tabclose_grow_reflows.js]
[browser_tabclose_reflows.js]
[browser_tabopen_reflows.js]

Просмотреть файл

@ -42,7 +42,13 @@ const whitelist = [
{
file: "chrome://browser/skin/tabbrowser/tabDragIndicator.png",
hidpi: "chrome://browser/skin/tabbrowser/tabDragIndicator@2x.png",
platforms: ["linux", "win", "macosx"],
platforms: ["macosx"],
},
{
file: "chrome://browser/skin/tabbrowser/tabDragIndicator.png",
hidpi: "<not loaded>",
platforms: ["linux", "win"],
},
{
@ -68,6 +74,10 @@ const whitelist = [
];
add_task(async function() {
if (!AppConstants.DEBUG) {
ok(false, "You need to run this test on a debug build.");
}
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
await startupRecorder.done;

Просмотреть файл

@ -0,0 +1,6 @@
[DEFAULT]
prefs =
layout.css.devPixelsPerPx='2'
[../browser_startup_images.js]
skip-if = !debug || (os == 'win' && os_version == '6.1') # hidpi results in the toolbar overflowing on Win 7

Просмотреть файл

@ -0,0 +1,6 @@
[DEFAULT]
prefs =
layout.css.devPixelsPerPx='1'
[../browser_startup_images.js]
skip-if = !debug

Просмотреть файл

@ -213,5 +213,19 @@ var tests = [
notification.remove();
goNext();
}
}
},
// the main action button should apply non-default(no highlight) style.
{ id: "Test#11",
run() {
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.mainAction.disableHighlight = true;
this.notifyObj.secondaryActions = undefined;
this.notification = showNotification(this.notifyObj);
},
onShown(popup) {
checkPopup(popup, this.notifyObj);
dismissNotification(popup);
},
onHidden() { }
},
];

Просмотреть файл

@ -210,6 +210,9 @@ function checkPopup(popup, notifyObj) {
"main action label matches");
is(notification.getAttribute("buttonaccesskey"),
notifyObj.mainAction.accessKey, "main action accesskey matches");
is(notification.getAttribute("buttonhighlight"),
(!notifyObj.mainAction.disableHighlight).toString(),
"main action highlight matches");
}
if (notifyObj.secondaryActions && notifyObj.secondaryActions.length > 0) {
let secondaryAction = notifyObj.secondaryActions[0];

Просмотреть файл

@ -28,6 +28,8 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/newtab/browser.ini',
'content/test/pageinfo/browser.ini',
'content/test/performance/browser.ini',
'content/test/performance/hidpi/browser.ini',
'content/test/performance/lowdpi/browser.ini',
'content/test/permissions/browser.ini',
'content/test/plugins/browser.ini',
'content/test/popupNotifications/browser.ini',

Просмотреть файл

@ -879,7 +879,7 @@ this.PanelMultiView = class {
// direction that the panel originally opened in. This property resets
// every time the popup closes, which is why we have to set it each time.
this._panel.autoPosition = false;
if (this.panelViews) {
if (this.panelViews && !this.node.hasAttribute("disablekeynav")) {
this.window.addEventListener("keydown", this);
this._panel.addEventListener("mousemove", this);
}

Просмотреть файл

@ -332,7 +332,7 @@
position="bottomcenter topright"
photon="true"
hidden="true">
<photonpanelmultiview mainViewId="widget-overflow-mainView">
<photonpanelmultiview mainViewId="widget-overflow-mainView" disablekeynav="true">
<panelview id="widget-overflow-mainView"
context="toolbar-context-menu">
<vbox class="panel-subview-body">

Просмотреть файл

@ -11,6 +11,7 @@ const SESSION = {
add_task(async function() {
SessionStore.setBrowserState(JSON.stringify(SESSION));
await promiseWindowRestored(window);
const tab = gBrowser.tabs[1];
is(tab.getAttribute("pending"), "true", "The tab is pending restore");

Просмотреть файл

@ -20,6 +20,7 @@
* promiseContentDimensions alterContent
* promisePrefChangeObserved openContextMenuInFrame
* promiseAnimationFrame getCustomizableUIPanelID
* promiseWindowRestored
*/
// There are shutdown issues for which multiple rejections are left uncaught.
@ -448,3 +449,7 @@ function promisePrefChangeObserved(pref) {
resolve();
}));
}
function promiseWindowRestored(window) {
return new Promise(resolve => window.addEventListener("SSWindowRestored", resolve, {once: true}));
}

Просмотреть файл

@ -1678,7 +1678,7 @@ BrowserGlue.prototype = {
// eslint-disable-next-line complexity
_migrateUI: function BG__migrateUI() {
const UI_VERSION = 52;
const UI_VERSION = 53;
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
let currentUIVersion;
@ -2056,6 +2056,19 @@ BrowserGlue.prototype = {
}
}
// Update user customizations that will interfere with the Safe Browsing V2
// to V4 migration (bug 1395419).
if (currentUIVersion < 53) {
const MALWARE_PREF = "urlclassifier.malwareTable";
if (Services.prefs.prefHasUserValue(MALWARE_PREF)) {
let malwareList = Services.prefs.getCharPref(MALWARE_PREF);
if (malwareList.indexOf("goog-malware-shavar") != -1) {
malwareList.replace("goog-malware-shavar", "goog-malware-proto");
Services.prefs.setCharPref(MALWARE_PREF, malwareList);
}
}
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},

Просмотреть файл

@ -1061,6 +1061,8 @@ PlacesToolbar.prototype = {
this._insertNewItem(this._resultNode.getChild(i), fragment);
}
this._rootElt.appendChild(fragment);
this.updateNodesVisibility();
});
}
@ -1158,7 +1160,7 @@ PlacesToolbar.prototype = {
// and the chevron popup when a window resize does not change
// the overflow status of the toolbar.
if (aEvent.target == aEvent.currentTarget) {
this.updateChevron();
this.updateNodesVisibility();
}
break;
case "overflow":
@ -1173,7 +1175,7 @@ PlacesToolbar.prototype = {
break;
case "TabOpen":
case "TabClose":
this.updateChevron();
this.updateNodesVisibility();
break;
case "dragstart":
this._onDragStart(aEvent);
@ -1210,14 +1212,6 @@ PlacesToolbar.prototype = {
}
},
updateOverflowStatus() {
if (this._rootElt.scrollLeftMin != this._rootElt.scrollLeftMax) {
this._onOverflow();
} else {
this._onUnderflow();
}
},
_isOverflowStateEventRelevant: function PT_isOverflowStateEventRelevant(aEvent) {
// Ignore events not aimed at ourselves, as well as purely vertical ones:
return aEvent.target == aEvent.currentTarget && aEvent.detail > 0;
@ -1231,28 +1225,24 @@ PlacesToolbar.prototype = {
this._chevronPopup.setAttribute("type", "places");
}
this._chevron.collapsed = false;
this.updateChevron();
this.updateNodesVisibility();
},
_onUnderflow: function PT_onUnderflow() {
this.updateChevron();
this.updateNodesVisibility();
this._chevron.collapsed = true;
},
updateChevron: function PT_updateChevron() {
// If the chevron is collapsed there's nothing to update.
if (this._chevron.collapsed)
return;
updateNodesVisibility: function PT_updateNodesVisibility() {
// Update the chevron on a timer. This will avoid repeated work when
// lot of changes happen in a small timeframe.
if (this._updateChevronTimer)
this._updateChevronTimer.cancel();
if (this._updateNodesVisibilityTimer)
this._updateNodesVisibilityTimer.cancel();
this._updateChevronTimer = this._setTimer(100);
this._updateNodesVisibilityTimer = this._setTimer(100);
},
_updateChevronTimerCallback: function PT__updateChevronTimerCallback() {
_updateNodesVisibilityTimerCallback: function PT__updateNodesVisibilityTimerCallback() {
let scrollRect = this._rootElt.getBoundingClientRect();
let childOverflowed = false;
for (let child of this._rootElt.childNodes) {
@ -1276,7 +1266,7 @@ PlacesToolbar.prototype = {
// We rebuild the chevron on popupShowing, so if it is open
// we must update it.
if (this._chevron.open)
if (!this._chevron.collapsed && this._chevron.open)
this._updateChevronPopupNodesVisibility();
let event = new CustomEvent("BookmarksToolbarVisibilityUpdated", {bubbles: true});
this._viewElt.dispatchEvent(event);
@ -1314,7 +1304,7 @@ PlacesToolbar.prototype = {
let icon = aPlacesNode.icon;
if (icon)
button.setAttribute("image", icon);
this.updateChevron();
this.updateNodesVisibility();
}
return;
}
@ -1343,7 +1333,7 @@ PlacesToolbar.prototype = {
this._rootElt);
}
if (!overflowed)
this.updateChevron();
this.updateNodesVisibility();
return;
}
@ -1391,12 +1381,12 @@ PlacesToolbar.prototype = {
// The chevron view may get nodeMoved after the toolbar. In such a case,
// we should ensure (by manually swapping menuitems) that the actual nodes
// are in the final position before updateChevron tries to updates their
// visibility, or the chevron may go out of sync.
// Luckily updateChevron runs on a timer, so, by the time it updates
// are in the final position before updateNodesVisibility tries to update
// their visibility, or the chevron may go out of sync.
// Luckily updateNodesVisibility runs on a timer, so, by the time it updates
// nodes, the menu has already handled the notification.
this.updateChevron();
this.updateNodesVisibility();
return;
}
@ -1446,7 +1436,7 @@ PlacesToolbar.prototype = {
if (elt.parentNode == this._rootElt) { // Node is on the toolbar.
if (elt.style.visibility != "hidden")
this.updateChevron();
this.updateNodesVisibility();
}
},
@ -1605,13 +1595,13 @@ PlacesToolbar.prototype = {
},
notify: function PT_notify(aTimer) {
if (aTimer == this._updateChevronTimer) {
this._updateChevronTimer = null;
if (aTimer == this._updateNodesVisibilityTimer) {
this._updateNodesVisibilityTimer = null;
// TODO: This should use promiseLayoutFlushed("layout"), so that
// _updateChevronTimerCallback can use getBoundsWithoutFlush. But for
// yet unknown reasons doing that causes intermittent failures, apparently
// due the flush not happening in a meaningful time.
window.requestAnimationFrame(this._updateChevronTimerCallback.bind(this));
// _updateNodesVisibilityTimerCallback can use getBoundsWithoutFlush. But
// for yet unknown reasons doing that causes intermittent failures,
// apparently due the flush not happening in a meaningful time.
window.requestAnimationFrame(this._updateNodesVisibilityTimerCallback.bind(this));
} else if (aTimer == this._ibTimer) {
// * Timer to turn off indicator bar.
this._dropIndicator.collapsed = true;

Просмотреть файл

@ -35,6 +35,7 @@ add_task(async function setup() {
await promiseSetToolbarVisibility(gToolbar, false);
}
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
});
});
@ -67,6 +68,36 @@ add_task(async function() {
"visible", undefined);
});
add_task(async function test_newWindow_noOverflow() {
info("Check toolbar in a new widow when it was already visible and not overflowed");
await PlacesUtils.bookmarks.eraseEverything();
// Add a single bookmark.
await PlacesUtils.bookmarks.insert({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
url: "http://toolbar.overflow/",
title: "Example"
});
// Add a favicon for the bookmark.
let favicon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
"AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
await PlacesTestUtils.addFavicons(new Map([["http://toolbar.overflow/", favicon]]));
let win = await BrowserTestUtils.openNewBrowserWindow();
try {
let toolbar = win.document.getElementById("PersonalToolbar");
Assert.ok(!toolbar.collapsed, "The toolbar is not collapsed");
let content = win.document.getElementById("PlacesToolbarItems");
await BrowserTestUtils.waitForCondition(() => {
return content.childNodes.length == 1 &&
content.childNodes[0].hasAttribute("image");
});
let chevron = win.document.getElementById("PlacesChevron");
Assert.ok(chevron.collapsed, "The chevron should be collapsed");
} finally {
await BrowserTestUtils.closeWindow(win);
}
});
async function test_index(desc, index, expected) {
info(desc);
let children = gToolbarContent.childNodes;

Просмотреть файл

@ -208,6 +208,15 @@ function debug(aMsg) {
*/
var gResistFingerprintingEnabled = false;
/**
* Return a promise that will be resolved once it receives event
* |SSBrowserWindowShowing| which is dispatched in onBrowserWindowShown.
*/
function promiseWindowShowing(window) {
return new Promise(resolve => window.addEventListener("SSBrowserWindowShowing",
() => resolve(window), {once: true}));
}
this.SessionStore = {
get promiseInitialized() {
return SessionStoreInternal.promiseInitialized;
@ -692,8 +701,9 @@ var SessionStoreInternal = {
delete state.windows[0].hidden;
// Since nothing is hidden in the first window, it cannot be a popup
delete state.windows[0].isPopup;
// We don't want to minimize and then open a window at startup.
if (state.windows[0].sizemode == "minimized")
// We don't want to minimize and then open a window at startup. However,
// when we're restoring, we should preserve previous windows sizemode.
if (state.windows[0].sizemode == "minimized" && !ss.doRestore())
state.windows[0].sizemode = "normal";
// clear any lastSessionWindowID attributes since those don't matter
// during normal restore
@ -1179,9 +1189,11 @@ var SessionStoreInternal = {
}
// this window was opened by _openWindowWithState
} else if (!this._isWindowLoaded(aWindow)) {
let state = this._statesToRestore[aWindow.__SS_restoreID];
let options = {overwriteTabs: true, isFollowUp: state.windows.length == 1};
this.restoreWindow(aWindow, state.windows[0], options);
// We used to restore window when it is opened. However, we
// want to restore windows after all windows are opened
// (bug 1034036). So the restoration process has been moved to
// function restoreWindowsFeaturesAndTabs.
// The user opened another, non-private window after starting up with
// a single private one. Let's restore the session we actually wanted to
// restore at startup.
@ -1273,6 +1285,10 @@ var SessionStoreInternal = {
// Register the window.
this.onLoad(aWindow);
// Dispatch a custom event to tell that a new window is about to be shown.
let evt = new aWindow.CustomEvent("SSBrowserWindowShowing");
aWindow.dispatchEvent(evt);
// Just call initializeWindow() directly if we're initialized already.
if (this._sessionInitialized) {
this.initializeWindow(aWindow);
@ -1347,7 +1363,7 @@ var SessionStoreInternal = {
aWindow.__SSi = this._generateWindowID();
}
this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID].windows[0];
delete this._statesToRestore[aWindow.__SS_restoreID];
delete aWindow.__SS_restoreID;
}
@ -2513,6 +2529,7 @@ var SessionStoreInternal = {
let window = this._openWindowWithState(state);
this.windowToFocus = window;
promiseWindowShowing(window).then(win => this.restoreWindows(win, state, {overwriteTabs: true}));
// Notify of changes to closed objects.
this._notifyOfClosedObjectsChange();
@ -2766,6 +2783,9 @@ var SessionStoreInternal = {
// Restore session cookies.
SessionCookies.restore(lastSessionState.cookies || []);
let openedWindows = [];
let remainingWindows = [];
// Restore into windows or open new ones as needed.
for (let i = 0; i < lastSessionState.windows.length; i++) {
let winState = lastSessionState.windows[i];
@ -2794,17 +2814,28 @@ var SessionStoreInternal = {
curWinState._closedTabs.splice(this._max_tabs_undo, curWinState._closedTabs.length);
}
// Restore into that window - pretend it's a followup since we'll already
// have a focused window.
// XXXzpao This is going to merge extData together (taking what was in
// winState over what is in the window already.
let options = {overwriteTabs: canOverwriteTabs, isFollowUp: true};
this.restoreWindow(windowToUse, winState, options);
// We don't restore window right away, just store its data.
// Later, these windows will be restored with newly opened windows.
if ("zIndex" in winState) {
windowToUse.__SS_zIndex = winState.zIndex;
}
this._updateWindowRestoreState(windowToUse, {windows: [winState]});
windowToUse.__SS_restoreOptions = {overwriteTabs: canOverwriteTabs};
openedWindows.push(windowToUse);
} else {
this._openWindowWithState({ windows: [winState] });
remainingWindows.push(winState);
}
}
// Actually restore windows in reversed z-order.
this.openWindows({windows: remainingWindows}).then(wins => {
wins = openedWindows.concat(wins);
this.restoreWindowsInReversedZOrder(wins, this._statesToRestore, false);
});
// Merge closed windows from this session with ones from last session
if (lastSessionState._closedWindows) {
this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows);
@ -3055,6 +3086,19 @@ var SessionStoreInternal = {
/* ........ Saving Functionality .............. */
/**
* Return z-index of a window.
* If a window is minimized, its z-index is -1.
*
* @param aWindow
* Window reference
* @return z-index of that window
*/
_getZIndex(window) {
let isMinimized = this._getWindowDimension(window, "sizemode") == "minimized";
return isMinimized ? -1 : window.__SS_zIndex;
},
/**
* Store window dimensions, visibility, sidebar
* @param aWindow
@ -3067,6 +3111,15 @@ var SessionStoreInternal = {
winData[aAttr] = this._getWindowDimension(aWindow, aAttr);
}, this);
// We only update zIndex when a valid zIndex is found in a window.
// There will be a case (flushAllWindowsAsync) where this function
// is called outside |forEachBrowserWindow|, therefore, no zIndex
// is found.
let zIndex = this._getZIndex(aWindow);
if (zIndex) {
winData.zIndex = this._getZIndex(aWindow);
}
var hidden = WINDOW_HIDEABLE_FEATURES.filter(function(aItem) {
return aWindow[aItem] && !aWindow[aItem].visible;
});
@ -3260,6 +3313,24 @@ var SessionStoreInternal = {
/* ........ Restoring Functionality .............. */
/**
* Open windows with data
*
* @param root
* Windows data
* @returns a promise resolved when all windows have been opened
*/
openWindows(root) {
let openedWindowPromises = [];
for (let winData of root.windows) {
if (winData && winData.tabs && winData.tabs[0]) {
let window = this._openWindowWithState({ windows: [winData] });
openedWindowPromises.push(promiseWindowShowing(window));
}
}
return Promise.all(openedWindowPromises);
},
/**
* restore features to a single window
* @param aWindow
@ -3268,20 +3339,14 @@ var SessionStoreInternal = {
* JS object
* @param aOptions
* {overwriteTabs: true} to overwrite existing tabs w/ new ones
* {isFollowUp: true} if this is not the restoration of the 1st window
* {firstWindow: true} if this is the first non-private window we're
* restoring in this session, that might open an
* external link as well
*/
restoreWindow: function ssi_restoreWindow(aWindow, winData, aOptions = {}) {
let overwriteTabs = aOptions && aOptions.overwriteTabs;
let isFollowUp = aOptions && aOptions.isFollowUp;
let firstWindow = aOptions && aOptions.firstWindow;
if (isFollowUp) {
this.windowToFocus = aWindow;
}
// initialize window if necessary
if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
this.onLoad(aWindow);
@ -3418,7 +3483,6 @@ var SessionStoreInternal = {
aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
if (overwriteTabs) {
this.restoreWindowFeatures(aWindow, winData);
delete this._windows[aWindow.__SSi].extData;
}
@ -3516,6 +3580,55 @@ var SessionStoreInternal = {
}
},
/**
* This function will restore window features and then retore window data.
*
* @param windows
* array of windows to be restored into
* @param statesToRestore
* states of windows to be restored
*/
restoreWindowsFeaturesAndTabs(windows, statesToRestore) {
// First, we restore window features, so that when users
// interacting with a window, we don't steal the window focus.
windows.forEach((window) => {
let state = statesToRestore[window.__SS_restoreID];
this.restoreWindowFeatures(window, state.windows[0]);
});
// Then we restore data into windows.
for (let i = 0; i < windows.length; ++i) {
let state = statesToRestore[windows[i].__SS_restoreID];
let option = windows[i].__SS_restoreOptions || {overwriteTabs: true};
this.restoreWindow(windows[i], state.windows[0], option);
delete windows[i].__SS_restoreOptions;
delete windows[i].__SS_zIndex;
}
},
/**
* This function will restore window in reversed z-index, so that users
* will be presented with most recently used window first.
*
* @param windows
* array of windows to be restored into
* @param statesToRestore
* states of windows to be restored
* @param areFollowUps
* a flag indicate these windows are follow-up windows
*/
restoreWindowsInReversedZOrder(windows, statesToRestore, areFollowUps) {
if (windows.some(window => !!window.__SS_zIndex)) {
windows.sort((a, b) => b.__SS_zIndex - a.__SS_zIndex);
}
if (!areFollowUps) {
this.windowToFocus = windows[0];
}
this.restoreWindowsFeaturesAndTabs(windows, statesToRestore);
},
/**
* Restore multiple windows using the provided state.
* @param aWindow
@ -3525,18 +3638,11 @@ var SessionStoreInternal = {
* JS object or JSON string
* @param aOptions
* {overwriteTabs: true} to overwrite existing tabs w/ new ones
* {isFollowUp: true} if this is not the restoration of the 1st window
* {firstWindow: true} if this is the first non-private window we're
* restoring in this session, that might open an
* external link as well
*/
restoreWindows: function ssi_restoreWindows(aWindow, aState, aOptions = {}) {
let isFollowUp = aOptions && aOptions.isFollowUp;
if (isFollowUp) {
this.windowToFocus = aWindow;
}
// initialize window if necessary
if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
this.onLoad(aWindow);
@ -3562,24 +3668,28 @@ var SessionStoreInternal = {
return;
}
if (!root.selectedWindow || root.selectedWindow > root.windows.length) {
root.selectedWindow = 0;
// Store z-index to current window so that it can be restored in reversed z-order.
let firstWindowData = root.windows.splice(0, 1);
if ("zIndex" in firstWindowData[0]) {
aWindow.__SS_zIndex = firstWindowData[0].zIndex;
}
// open new windows for all further window entries of a multi-window session
// (unless they don't contain any tab data)
let winData;
for (var w = 1; w < root.windows.length; w++) {
winData = root.windows[w];
if (winData && winData.tabs && winData.tabs[0]) {
var window = this._openWindowWithState({ windows: [winData] });
if (w == root.selectedWindow - 1) {
this.windowToFocus = window;
}
}
}
// Store the restore state and restore option of the current window,
// so that the window can be restored in reversed z-order.
this._updateWindowRestoreState(aWindow, {windows: firstWindowData});
aWindow.__SS_restoreOptions = aOptions;
this.restoreWindow(aWindow, root.windows[0], aOptions);
// Begin the restoration: First open all windows in creation order.
// After all windows are opened, we restore states to windows in
// reversed z-order.
this.openWindows(root).then(windows => {
// We want to add current window to opened window, so that this window will be
// restored in reversed z-order. (We add the window to first position, in case
// no z-indices are found, that window will be restored first.)
windows.unshift(aWindow);
this.restoreWindowsInReversedZOrder(windows, this._statesToRestore, false);
});
DevToolsShim.restoreDevToolsSession(aState);
},
@ -4167,18 +4277,42 @@ var SessionStoreInternal = {
},
/**
* call a callback for all currently opened browser windows
* A boolean flag indicates whether we can iterate over all windows
* in their z order.
*/
get isWMZOrderBroken() {
let broken_wm_z_order = AppConstants.platform != "macosx" && AppConstants.platform != "win";
delete this.isWMZOrderBroken;
return this.isWMZOrderBroken = broken_wm_z_order;
},
/**
* Call a callback for all currently opened browser windows.
* This will iterate the windows in z-index from front to back,
* and assign z-index to the window.
* (might miss the most recent one)
* @param aFunc
* Callback each window is passed to
*/
_forEachBrowserWindow: function ssi_forEachBrowserWindow(aFunc) {
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
let windowsEnum = this.isWMZOrderBroken ?
Services.wm.getEnumerator("navigator:browser") :
Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
let mostRecentWindow = this.isWMZOrderBroken ? this._getMostRecentBrowserWindow() : null;
// We want to start zIndex at 1, so that, in _updateWindowFeatures, if no z-index is found
// in a window, we can just check with a simple condition if: `if (zIndex)`.
let zIndex = 1;
while (windowsEnum.hasMoreElements()) {
var window = windowsEnum.getNext();
let window = windowsEnum.getNext();
if (window.__SSi && !window.closed) {
if (this.isWMZOrderBroken) {
window.__SS_zIndex = mostRecentWindow.__SSi === window.__SSi ? 2 : 1;
} else {
window.__SS_zIndex = zIndex++;
}
aFunc.call(this, window);
delete window.__SS_zIndex;
}
}
},
@ -4209,6 +4343,23 @@ var SessionStoreInternal = {
return Promise.all(promises);
},
/**
* Store a restore state of a window to this._statesToRestore. The window
* will be given an id that can be used to get the restore state from
* this._statesToRestore.
*
* @param window
* a reference to a window that has a state to restore
* @param state
* an object containing session data
*/
_updateWindowRestoreState(window, state) {
do {
var ID = "window" + Math.random();
} while (ID in this._statesToRestore);
this._statesToRestore[(window.__SS_restoreID = ID)] = state;
},
/**
* open a new browser window for a given session state
* called when restoring a multi-window session
@ -4237,10 +4388,12 @@ var SessionStoreInternal = {
Services.ww.openWindow(null, this._prefBranch.getCharPref("chromeURL"),
"_blank", features, argString);
do {
var ID = "window" + Math.random();
} while (ID in this._statesToRestore);
this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
// Store z-index, so that windows can be restored in reversed z-order.
if ("zIndex" in aState.windows[0]) {
window.__SS_zIndex = aState.windows[0].zIndex;
}
this._updateWindowRestoreState(window, aState);
return window;
},

Просмотреть файл

@ -265,4 +265,4 @@ skip-if = !crashreporter || !e10s # Tabs can't crash without e10s
[browser_cookies_legacy.js]
[browser_cookies_privacy.js]
[browser_speculative_connect.js]
[browser_restore_reversed_z_order.js]

Просмотреть файл

@ -37,6 +37,7 @@ add_task(async function() {
// restore the window state
ss.setBrowserState(state);
await promiseWindowRestored(window);
// at this point, the cookie should be restored...
enumerator = Services.cookies.enumerator;

Просмотреть файл

@ -38,47 +38,49 @@ function test() {
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
let closedTabs = SessionStore.getClosedTabData(newWin, false);
promiseWindowRestored(newWin).then(() => {
let closedTabs = SessionStore.getClosedTabData(newWin, false);
// Verify that non JSON serialized data is the same as JSON serialized data.
is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
"Non-serialized data is the same as serialized data")
// Verify that non JSON serialized data is the same as JSON serialized data.
is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
"Non-serialized data is the same as serialized data")
is(closedTabs.length, test_state.windows[0]._closedTabs.length,
"Closed tab list has the expected length");
is(countByTitle(closedTabs, FORGET),
test_state.windows[0]._closedTabs.length - remember_count,
"The correct amout of tabs are to be forgotten");
is(countByTitle(closedTabs, REMEMBER), remember_count,
"Everything is set up");
is(closedTabs.length, test_state.windows[0]._closedTabs.length,
"Closed tab list has the expected length");
is(countByTitle(closedTabs, FORGET),
test_state.windows[0]._closedTabs.length - remember_count,
"The correct amout of tabs are to be forgotten");
is(countByTitle(closedTabs, REMEMBER), remember_count,
"Everything is set up");
// All of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE.
ok(testForError(() => ss.forgetClosedTab({}, 0)),
"Invalid window for forgetClosedTab throws");
ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
"Invalid tab for forgetClosedTab throws");
ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
"Invalid tab for forgetClosedTab throws");
// All of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE.
ok(testForError(() => ss.forgetClosedTab({}, 0)),
"Invalid window for forgetClosedTab throws");
ok(testForError(() => ss.forgetClosedTab(newWin, -1)),
"Invalid tab for forgetClosedTab throws");
ok(testForError(() => ss.forgetClosedTab(newWin, test_state.windows[0]._closedTabs.length + 1)),
"Invalid tab for forgetClosedTab throws");
// Remove third tab, then first tab.
ss.forgetClosedTab(newWin, 2);
ss.forgetClosedTab(newWin, null);
// Remove third tab, then first tab.
ss.forgetClosedTab(newWin, 2);
ss.forgetClosedTab(newWin, null);
closedTabs = SessionStore.getClosedTabData(newWin, false);
closedTabs = SessionStore.getClosedTabData(newWin, false);
// Verify that non JSON serialized data is the same as JSON serialized data.
is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
"Non-serialized data is the same as serialized data")
// Verify that non JSON serialized data is the same as JSON serialized data.
is(JSON.stringify(closedTabs), SessionStore.getClosedTabData(newWin),
"Non-serialized data is the same as serialized data")
is(closedTabs.length, remember_count,
"The correct amout of tabs was removed");
is(countByTitle(closedTabs, FORGET), 0,
"All tabs specifically forgotten were indeed removed");
is(countByTitle(closedTabs, REMEMBER), remember_count,
"... and tabs not specifically forgetten weren't");
is(closedTabs.length, remember_count,
"The correct amout of tabs was removed");
is(countByTitle(closedTabs, FORGET), 0,
"All tabs specifically forgotten were indeed removed");
is(countByTitle(closedTabs, REMEMBER), remember_count,
"... and tabs not specifically forgetten weren't");
// Clean up.
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
BrowserTestUtils.closeWindow(newWin).then(finish);
// Clean up.
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
BrowserTestUtils.closeWindow(newWin).then(finish);
});
});
}

Просмотреть файл

@ -57,6 +57,7 @@ add_task(async function() {
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
await promiseWindowRestored(newWin);
let closedTabs = JSON.parse(ss.getClosedTabData(newWin));
is(closedTabs.length, test_state.windows[0]._closedTabs.length,

Просмотреть файл

@ -22,24 +22,30 @@ function test() {
newState.windows[0].extData[uniqueKey2] = uniqueValue2;
ss.setWindowState(newWin, JSON.stringify(newState), false);
is(newWin.gBrowser.tabs.length, 2,
"original tab wasn't overwritten");
is(ss.getWindowValue(newWin, uniqueKey1), uniqueValue1,
"window value wasn't overwritten when the tabs weren't");
is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue2,
"new window value was correctly added");
promiseWindowRestored(newWin).then(() => {
is(newWin.gBrowser.tabs.length, 2,
"original tab wasn't overwritten");
is(ss.getWindowValue(newWin, uniqueKey1), uniqueValue1,
"window value wasn't overwritten when the tabs weren't");
is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue2,
"new window value was correctly added");
newState.windows[0].extData[uniqueKey2] = uniqueValue1;
ss.setWindowState(newWin, JSON.stringify(newState), true);
newState.windows[0].extData[uniqueKey2] = uniqueValue1;
ss.setWindowState(newWin, JSON.stringify(newState), true);
is(newWin.gBrowser.tabs.length, 1,
"original tabs were overwritten");
is(ss.getWindowValue(newWin, uniqueKey1), "",
"window value was cleared");
is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue1,
"window value was correctly overwritten");
promiseWindowRestored(newWin).then(() => {
is(newWin.gBrowser.tabs.length, 1,
"original tabs were overwritten");
is(ss.getWindowValue(newWin, uniqueKey1), "",
"window value was cleared");
is(ss.getWindowValue(newWin, uniqueKey2), uniqueValue1,
"window value was correctly overwritten");
// clean up
BrowserTestUtils.closeWindow(newWin).then(finish);
// clean up
BrowserTestUtils.closeWindow(newWin).then(finish);
});
});
});
}

Просмотреть файл

@ -25,36 +25,43 @@ function test() {
"window value was set before the window was overwritten");
ss.setWindowState(newWin, JSON.stringify(newState), true);
// use newWin.setTimeout(..., 0) to mirror sss_restoreWindowFeatures
newWin.setTimeout(function() {
is(ss.getWindowValue(newWin, uniqueKey), "",
"window value was implicitly cleared");
is(newWin.windowState, newWin.STATE_MAXIMIZED,
"the window was maximized");
is(JSON.parse(ss.getClosedTabData(newWin)).length, 1,
"the closed tab was added before the window was overwritten");
delete newState.windows[0]._closedTabs;
delete newState.windows[0].sizemode;
ss.setWindowState(newWin, JSON.stringify(newState), true);
promiseWindowRestored(newWin).then(() => {
// use newWin.setTimeout(..., 0) to mirror sss_restoreWindowFeatures
newWin.setTimeout(function() {
is(JSON.parse(ss.getClosedTabData(newWin)).length, 0,
"closed tabs were implicitly cleared");
is(ss.getWindowValue(newWin, uniqueKey), "",
"window value was implicitly cleared");
is(newWin.windowState, newWin.STATE_MAXIMIZED,
"the window remains maximized");
newState.windows[0].sizemode = "normal";
"the window was maximized");
is(JSON.parse(ss.getClosedTabData(newWin)).length, 1,
"the closed tab was added before the window was overwritten");
delete newState.windows[0]._closedTabs;
delete newState.windows[0].sizemode;
ss.setWindowState(newWin, JSON.stringify(newState), true);
newWin.setTimeout(function() {
isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
"the window was explicitly unmaximized");
promiseWindowRestored(newWin).then(() => {
newWin.setTimeout(function() {
is(JSON.parse(ss.getClosedTabData(newWin)).length, 0,
"closed tabs were implicitly cleared");
BrowserTestUtils.closeWindow(newWin).then(finish);
}, 0);
is(newWin.windowState, newWin.STATE_MAXIMIZED,
"the window remains maximized");
newState.windows[0].sizemode = "normal";
ss.setWindowState(newWin, JSON.stringify(newState), true);
promiseWindowRestored(newWin).then(() => {
newWin.setTimeout(function() {
isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
"the window was explicitly unmaximized");
BrowserTestUtils.closeWindow(newWin).then(finish);
}, 0);
});
}, 0);
});
}, 0);
}, 0);
});
});
}

Просмотреть файл

@ -51,6 +51,7 @@ add_task(async function test_bug_490040() {
let win = await BrowserTestUtils.openNewBrowserWindow();
ss.setWindowState(win, JSON.stringify(state.windowState), true);
await promiseWindowRestored(win);
if (state.windowState.windows[0].tabs.length) {
await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
}

Просмотреть файл

@ -85,35 +85,38 @@ function test() {
test_state._closedWindows.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
let closedWindows = JSON.parse(ss.getClosedWindowData());
is(closedWindows.length, test_state._closedWindows.length,
"Closed window list has the expected length");
is(countByTitle(closedWindows, FORGET),
test_state._closedWindows.length - remember_count,
"The correct amount of windows are to be forgotten");
is(countByTitle(closedWindows, REMEMBER), remember_count,
"Everything is set up.");
promiseWindowRestored(newWin).then(() => {
let closedWindows = JSON.parse(ss.getClosedWindowData());
is(closedWindows.length, test_state._closedWindows.length,
"Closed window list has the expected length");
is(countByTitle(closedWindows, FORGET),
test_state._closedWindows.length - remember_count,
"The correct amount of windows are to be forgotten");
is(countByTitle(closedWindows, REMEMBER), remember_count,
"Everything is set up.");
// all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
ok(testForError(() => ss.forgetClosedWindow(-1)),
"Invalid window for forgetClosedWindow throws");
ok(testForError(() => ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
"Invalid window for forgetClosedWindow throws");
// all of the following calls with illegal arguments should throw NS_ERROR_ILLEGAL_VALUE
ok(testForError(() => ss.forgetClosedWindow(-1)),
"Invalid window for forgetClosedWindow throws");
ok(testForError(() => ss.forgetClosedWindow(test_state._closedWindows.length + 1)),
"Invalid window for forgetClosedWindow throws");
// Remove third window, then first window
ss.forgetClosedWindow(2);
ss.forgetClosedWindow(null);
// Remove third window, then first window
ss.forgetClosedWindow(2);
ss.forgetClosedWindow(null);
closedWindows = JSON.parse(ss.getClosedWindowData());
is(closedWindows.length, remember_count,
"The correct amount of windows were removed");
is(countByTitle(closedWindows, FORGET), 0,
"All windows specifically forgotten were indeed removed");
is(countByTitle(closedWindows, REMEMBER), remember_count,
"... and windows not specifically forgetten weren't.");
closedWindows = JSON.parse(ss.getClosedWindowData());
is(closedWindows.length, remember_count,
"The correct amount of windows were removed");
is(countByTitle(closedWindows, FORGET), 0,
"All windows specifically forgotten were indeed removed");
is(countByTitle(closedWindows, REMEMBER), remember_count,
"... and windows not specifically forgetten weren't.");
// clean up
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
BrowserTestUtils.closeWindow(newWin).then(finish);
// clean up
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
BrowserTestUtils.closeWindow(newWin).then(finish);
});
});
}

Просмотреть файл

@ -22,16 +22,18 @@ function test() {
promiseWindowLoaded(win).then(() => {
is(win.gURLBar.readOnly, false,
"URL bar should not be read-only before setting the state");
"URL bar should not be read-only before setting the state");
is(win.gURLBar.getAttribute("enablehistory"), "true",
"URL bar autocomplete should be enabled before setting the state");
"URL bar autocomplete should be enabled before setting the state");
ss.setWindowState(win, state, true);
is(win.gURLBar.readOnly, expected.readOnly,
"URL bar read-only state should be restored correctly");
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
"URL bar autocomplete state should be restored correctly");
promiseWindowRestored(win).then(() => {
is(win.gURLBar.readOnly, expected.readOnly,
"URL bar read-only state should be restored correctly");
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
"URL bar autocomplete state should be restored correctly");
BrowserTestUtils.closeWindow(win).then(callback);
BrowserTestUtils.closeWindow(win).then(callback);
});
});
}

Просмотреть файл

@ -28,8 +28,10 @@ function test() {
if (/NS_ERROR_MALFORMED_URI/.test(e))
gotError = true;
}
ok(!gotError, "Didn't get a malformed URI error.");
BrowserTestUtils.closeWindow(theWin).then(finish);
promiseWindowRestored(theWin).then(() => {
ok(!gotError, "Didn't get a malformed URI error.");
BrowserTestUtils.closeWindow(theWin).then(finish);
});
});
}, {once: true});
}

Просмотреть файл

@ -83,6 +83,7 @@ add_task(async function() {
// Set the test state.
ss.setBrowserState(JSON.stringify(state));
await promiseWindowRestored(window);
// Wait until the selected tab is restored and all others are pending.
await Promise.all(Array.map(gBrowser.tabs, tab => {

Просмотреть файл

@ -42,6 +42,7 @@ add_task(async function test() {
let backupState = SessionStore.getBrowserState();
SessionStore.setBrowserState(JSON.stringify(TEST_STATE));
let win = await promiseWindow;
await promiseWindowRestored(win);
// The window has now been opened. Check the state that is returned,
// this should come from the cache while the window isn't restored, yet.

Просмотреть файл

@ -37,12 +37,13 @@ var state = {windows: [{tabs: [{entries: [
}
]}]}]}
function test() {
add_task(async function test() {
registerCleanupFunction(function() {
ss.setBrowserState(stateBackup);
});
/* This test fails by hanging. */
ss.setBrowserState(JSON.stringify(state));
await promiseWindowRestored(window);
ok(true, "Didn't hang!");
}
});

Просмотреть файл

@ -24,9 +24,12 @@ function test() {
info(ex);
}
ok(!gotError, "ss.setWindowState did not throw an error");
promiseWindowRestored(window).then(() => {
ok(!gotError, "ss.setWindowState did not throw an error");
// Make sure that we reset the state. Use a full state just in case things get crazy.
let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }]}]};
waitForBrowserState(blankState, finish);
// Make sure that we reset the state. Use a full state just in case things get crazy.
let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }]}]};
waitForBrowserState(blankState, finish);
});
}

Просмотреть файл

@ -101,19 +101,22 @@ add_task(async function() {
// Now, test that we don't record history if the iframe is added dynamically
add_task(async function() {
// Start with an empty history
let blankState = JSON.stringify({
windows: [{
tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }],
_closedTabs: []
}],
_closedWindows: []
});
ss.setBrowserState(blankState);
let blankState = JSON.stringify({
windows: [{
tabs: [{ entries: [{ url: "about:blank", triggeringPrincipal_base64 }] }],
_closedTabs: []
}],
_closedWindows: []
});
ss.setBrowserState(blankState);
await promiseWindowRestored(window);
let testURL = getRootDirectory(gTestPath) + "browser_frame_history_index_blank.html";
let tab = BrowserTestUtils.addTab(gBrowser, testURL);
gBrowser.selectedTab = tab;
dump("Wait here\n");
await waitForLoadsInBrowser(tab.linkedBrowser, 1);
dump("Finished Wait here\n");
info("dynamic: Opening a page with an iframe containing three frames, 4 dynamic loads should take place");
let doc = tab.linkedBrowser.contentDocument;

Просмотреть файл

@ -37,11 +37,13 @@ add_task(async function() {
// Open a new window and restore it to an initial state.
let win = await promiseNewWindowLoaded({private: false});
SessionStore.setWindowState(win, JSON.stringify(initialState), true);
await promiseWindowRestored(win);
is(SessionStore.getClosedTabCount(win), 2, "2 closed tabs after restoring initial state");
// Restore the new state but do not overwrite existing tabs (this should
// cause the closed tabs to be merged).
SessionStore.setWindowState(win, JSON.stringify(restoreState), false);
await promiseWindowRestored(win);
// Verify the windows closed tab data is correct.
let iClosed = initialState.windows[0]._closedTabs;

Просмотреть файл

@ -135,6 +135,7 @@ async function runScenarios(scenarios) {
scenario.selectedTab);
SessionStore.setWindowState(win, state, true);
await promiseWindowRestored(win);
for (let i = 0; i < scenario.expectedRemoteness.length; ++i) {
let expectedRemoteness = scenario.expectedRemoteness[i];

Просмотреть файл

@ -135,6 +135,7 @@ add_task(async function run_test() {
// Restore window with session cookies that have no originAttributes.
ss.setWindowState(win, SESSION_DATA, true);
await promiseWindowRestored(win);
let enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
let cookie;
@ -155,6 +156,7 @@ add_task(async function run_test() {
// Restore window with session cookies that have originAttributes within.
ss.setWindowState(win, SESSION_DATA_OA, true);
await promiseWindowRestored(win);
enumerator = Services.cookies.getCookiesFromHost(TEST_HOST, {});
cookieCount = 0;

Просмотреть файл

@ -0,0 +1,155 @@
const TEST_URLS_MAP = {
"about:about": "About About",
"about:license": "Licenses",
"about:profiles": "About Profiles",
"about:mozilla": "The Book of Mozilla, 15:1"
};
const TEST_URLS = Object.keys(TEST_URLS_MAP);
const TEST_LABELS = Object.values(TEST_URLS_MAP);
const Paths = SessionFile.Paths;
const BROKEN_WM_Z_ORDER = AppConstants.platform != "macosx" && AppConstants.platform != "win";
let source;
let state;
function promiseProvideWindow(url, features) {
return new Promise(resolve => provideWindow(resolve, url, features));
}
add_task(async function init() {
// Make sure that we start with only primary window.
await promiseAllButPrimaryWindowClosed();
let promises = [];
for (let i = 0; i < 4; ++i) {
let url = Object.keys(TEST_URLS_MAP)[i];
let window = await promiseProvideWindow();
BrowserTestUtils.loadURI(window.gBrowser, url);
if (i == 2) {
window.minimize();
}
// We want to get the lastest state from each window.
await BrowserTestUtils.waitForLocationChange(window.gBrowser, url);
await BrowserTestUtils.browserStopped(window.gBrowser.selectedBrowser, url);
promises.push(TabStateFlusher.flushWindow(window));
}
// Wait until we get the lastest history from all windows.
await Promise.all(promises);
// Force save state and read the written state.
source = await promiseRecoveryFileContents();
state = JSON.parse(source);
// Close all windows as we don't need them.
await promiseAllButPrimaryWindowClosed();
});
add_task(async function test_z_indices_are_saved_correctly() {
is(state.windows.length, 4, "Correct number of windows saved");
// Check if we saved state in correct order of creation.
for (let i = 0; i < TEST_URLS.length; ++i) {
is(state.windows[i].tabs[0].entries[0].url, TEST_URLS[i],
`Window #${i} is stored in correct creation order`);
}
// Check if we saved a valid zIndex (no null, no undefined or no 0).
for (let win of state.windows) {
ok(win.zIndex, "A valid zIndex is stored");
}
if (BROKEN_WM_Z_ORDER) {
// Last window should have zIndex of 2.
is(state.windows[3].zIndex, 2, "Currently using window has correct z-index");
// Other windows should have zIndex of 1.
is(state.windows[0].zIndex, 1, "Window #1 has correct z-index");
is(state.windows[1].zIndex, 1, "Window #2 has correct z-index");
// Minimized window should have zIndex of -1.
is(state.windows[2].zIndex, -1, "Minimized window has correct z-index");
} else {
is(state.windows[2].zIndex, -1, "Minimzed window has correct z-index");
ok(state.windows[0].zIndex != -1, "Window #1 shouldn't has z-index -1");
ok(state.windows[1].zIndex > state.windows[0].zIndex, "Window #2 has correct z-index");
ok(state.windows[3].zIndex > state.windows[1].zIndex, "Currently using window has correct z-index");
}
});
add_task(async function test_windows_are_restored_in_reversed_z_index() {
let windowsOpened = 1;
let windows = [window];
let windowsRestored = 0;
let tabsRestoredLabels = [];
// A defer promise that will be resolved once
// we restored all tabs.
let defer = {};
defer.promise = new Promise((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
function allTabsRestored() {
is(tabsRestoredLabels[0], TEST_LABELS[3], "First restored tab should be previous used tab");
// We don't care about restoration order of windows in between the first and last window
// when the OS has broken vm z-order.
if (!BROKEN_WM_Z_ORDER) {
is(tabsRestoredLabels[1], TEST_LABELS[1], "Second restored tab is correct");
is(tabsRestoredLabels[2], TEST_LABELS[0], "Third restored tab is correct");
}
is(tabsRestoredLabels[3], TEST_LABELS[2], "Last restored tab should be in minimized window");
// Finish the test.
defer.resolve();
}
function onSSWindowRestored(aEvent) {
if (++windowsRestored == 4) {
// Remove the event listener from each window
windows.forEach(function(win) {
win.removeEventListener("SSWindowRestored", onSSWindowRestored, true);
});
executeSoon(allTabsRestored);
}
tabsRestoredLabels.push(aEvent.target.gBrowser.tabs[0].label);
}
// Used to add our listener to further windows so we can catch SSWindowRestored
// coming from them when creating a multi-window state.
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let newWindow = aSubject.QueryInterface(Ci.nsIDOMWindow);
newWindow.addEventListener("load", function() {
if (++windowsOpened == 3) {
Services.ww.unregisterNotification(windowObserver);
}
// Track this window so we can remove the progress listener later.
windows.push(newWindow);
// Add the progress listener.
newWindow.addEventListener("SSWindowRestored", onSSWindowRestored, true);
}, { once: true });
}
}
Services.ww.registerNotification(windowObserver);
registerCleanupFunction(function() {
windows.forEach(function(win) {
win.removeEventListener("SSWindowRestored", onSSWindowRestored, true);
if (win !== window) {
BrowserTestUtils.closeWindow(win);
}
});
});
window.addEventListener("SSWindowRestored", onSSWindowRestored, true);
// Restore states.
ss.setBrowserState(source);
await defer.promise;
});

Просмотреть файл

@ -42,6 +42,7 @@ add_task(async function() {
}
ss.setWindowState(win2, JSON.stringify(winState), true);
await promiseWindowRestored(win2);
for (let i = 0; i < 4; i++) {
let browser = win2.gBrowser.tabs[i].linkedBrowser;
@ -102,6 +103,7 @@ add_task(async function() {
await TabStateFlusher.flush(win2.gBrowser.tabs[0].linkedBrowser);
ss.setWindowState(win2, JSON.stringify(winState), true);
await promiseWindowRestored(win2);
for (let i = 0; i < 2; i++) {
let browser = win2.gBrowser.tabs[i].linkedBrowser;

Просмотреть файл

@ -186,6 +186,10 @@ function promiseTabState(tab, state) {
return promise;
}
function promiseWindowRestored(win) {
return new Promise(resolve => win.addEventListener("SSWindowRestored", resolve, {once: true}));
}
/**
* Wait for a content -> chrome message.
*/

Просмотреть файл

@ -120,6 +120,7 @@ if (typeof Mozilla == "undefined") {
* <li>privateWindow
* <li>quit
* <li>readerMode-urlBar
* <li>screenshots
* <li>search
* <li>searchIcon
* <li>searchPrefsLink

Просмотреть файл

@ -257,6 +257,12 @@ this.UITour = {
return aDocument.getElementById("pageAction-urlbar-sendToDevice") ||
aDocument.getElementById("pageAction-panel-sendToDevice");
},
}],
["screenshots", {
query: (aDocument) => {
return aDocument.getElementById("pageAction-urlbar-screenshots") ||
aDocument.getElementById("pageAction-panel-screenshots");
},
}]
]),

Просмотреть файл

@ -29,6 +29,7 @@ function getExpectedTargets() {
"privateWindow",
...(hasQuit ? ["quit"] : []),
"readerMode-urlBar",
"screenshots",
"trackingProtection",
"urlbar",
];
@ -37,6 +38,7 @@ function getExpectedTargets() {
add_task(setup_UITourTest);
add_UITour_task(async function test_availableTargets() {
await ensureScreenshotsEnabled();
let data = await getConfigurationPromise("availableTargets");
let expecteds = getExpectedTargets();
ok_targets(data, expecteds);
@ -80,6 +82,7 @@ add_UITour_task(async function test_availableTargets_removeUrlbarPageActionsAll(
ok_targets(data, expecteds);
let expectedActions = [
[ "pocket", "pageAction-panel-pocket" ],
[ "screenshots", "pageAction-panel-screenshots" ],
[ "pageAction-bookmark", "pageAction-panel-bookmark" ],
[ "pageAction-copyURL", "pageAction-panel-copyURL" ],
[ "pageAction-emailLink", "pageAction-panel-emailLink" ],
@ -99,6 +102,7 @@ add_UITour_task(async function test_availableTargets_addUrlbarPageActionsAll() {
ok_targets(data, expecteds);
let expectedActions = [
[ "pocket", "pocket-button-box" ],
[ "screenshots", "pageAction-urlbar-screenshots" ],
[ "pageAction-bookmark", "star-button-box" ],
[ "pageAction-copyURL", "pageAction-urlbar-copyURL" ],
[ "pageAction-emailLink", "pageAction-urlbar-emailLink" ],
@ -150,3 +154,14 @@ var pageActionsHelper = {
this._originalStates = null;
}
};
function ensureScreenshotsEnabled() {
SpecialPowers.pushPrefEnv({ set: [
[ "extensions.screenshots.system", false ],
[ "extensions.screenshots.system-disabled", false ]
]});
return BrowserTestUtils.waitForCondition(() => {
return PageActions.actionForID("screenshots") &&
!CustomizableUI.getWidget("screenshots_mozilla_org-browser-action");
}, "Should enable Screenshots");
}

Просмотреть файл

@ -114,8 +114,8 @@ function TopSites(prevState = INITIAL_STATE.TopSites, action) {
}
newRows = prevState.rows.map(site => {
if (site && site.url === action.data.url) {
const {bookmarkGuid, bookmarkTitle, lastModified} = action.data;
return Object.assign({}, site, {bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified});
const {bookmarkGuid, bookmarkTitle, dateAdded} = action.data;
return Object.assign({}, site, {bookmarkGuid, bookmarkTitle, bookmarkDateCreated: dateAdded});
}
return site;
});
@ -203,12 +203,16 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
let order;
let index;
if (prevState.length > 0) {
order = action.data.order || prevState[0].order - 1;
order = action.data.order !== undefined ? action.data.order : prevState[0].order - 1;
index = newState.findIndex(section => section.order >= order);
if (index === -1) {
index = newState.length;
}
} else {
order = action.data.order || 1;
order = action.data.order !== undefined ? action.data.order : 0;
index = 0;
}
const section = Object.assign({title: "", rows: [], order, enabled: false}, action.data, {initialized});
newState.splice(index, 0, section);
}
@ -231,8 +235,11 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
rows: section.rows.map(item => {
// find the item within the rows that is attempted to be bookmarked
if (item.url === action.data.url) {
const {bookmarkGuid, bookmarkTitle, lastModified} = action.data;
Object.assign(item, {bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified});
const {bookmarkGuid, bookmarkTitle, dateAdded} = action.data;
Object.assign(item, {bookmarkGuid, bookmarkTitle, bookmarkDateCreated: dateAdded});
if (!item.type || item.type === "history") {
item.type = "bookmark";
}
}
return item;
})
@ -249,6 +256,9 @@ function Sections(prevState = INITIAL_STATE.Sections, action) {
delete newSite.bookmarkGuid;
delete newSite.bookmarkTitle;
delete newSite.bookmarkDateCreated;
if (!newSite.type || newSite.type === "bookmark") {
newSite.type = "history";
}
return newSite;
}
return item;

Просмотреть файл

@ -459,9 +459,9 @@ function TopSites() {
var _action$data2 = action.data;
const bookmarkGuid = _action$data2.bookmarkGuid,
bookmarkTitle = _action$data2.bookmarkTitle,
lastModified = _action$data2.lastModified;
dateAdded = _action$data2.dateAdded;
return Object.assign({}, site, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
return Object.assign({}, site, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: dateAdded });
}
return site;
});
@ -558,12 +558,16 @@ function Sections() {
let order;
let index;
if (prevState.length > 0) {
order = action.data.order || prevState[0].order - 1;
order = action.data.order !== undefined ? action.data.order : prevState[0].order - 1;
index = newState.findIndex(section => section.order >= order);
if (index === -1) {
index = newState.length;
}
} else {
order = action.data.order || 1;
order = action.data.order !== undefined ? action.data.order : 0;
index = 0;
}
const section = Object.assign({ title: "", rows: [], order, enabled: false }, action.data, { initialized });
newState.splice(index, 0, section);
}
@ -589,9 +593,12 @@ function Sections() {
var _action$data3 = action.data;
const bookmarkGuid = _action$data3.bookmarkGuid,
bookmarkTitle = _action$data3.bookmarkTitle,
lastModified = _action$data3.lastModified;
dateAdded = _action$data3.dateAdded;
Object.assign(item, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: lastModified });
Object.assign(item, { bookmarkGuid, bookmarkTitle, bookmarkDateCreated: dateAdded });
if (!item.type || item.type === "history") {
item.type = "bookmark";
}
}
return item;
})
@ -608,6 +615,9 @@ function Sections() {
delete newSite.bookmarkGuid;
delete newSite.bookmarkTitle;
delete newSite.bookmarkDateCreated;
if (!newSite.type || newSite.type === "bookmark") {
newSite.type = "history";
}
return newSite;
}
return item;
@ -2985,7 +2995,7 @@ module.exports = {
},
bookmark: {
intlID: "type_label_bookmarked",
icon: "bookmark"
icon: "bookmark-added"
},
trending: {
intlID: "type_label_recommended",

Просмотреть файл

@ -78,6 +78,8 @@ input {
background-image: url("chrome://browser/skin/check.svg"); }
.icon.icon-webextension {
background-image: url("assets/glyph-webextension-16.svg"); }
.icon.icon-highlights {
background-image: url("assets/glyph-highlights-16.svg"); }
html,
body,
@ -656,8 +658,6 @@ main {
background-image: url("chrome://browser/skin/forward.svg");
fill: #FFF;
-moz-context-properties: fill; }
.search-wrapper input[aria-expanded="true"] {
border-radius: 4px 4px 0 0; }
.search-wrapper .search-label {
background: url("assets/glyph-search-16.svg") no-repeat center center/20px;
fill: rgba(12, 12, 13, 0.6);
@ -687,7 +687,6 @@ main {
transform: scaleX(-1); }
.search-wrapper .contentSearchSuggestionTable {
border: 0;
box-shadow: 0 0 0 2px #0060DF;
transform: translateY(2px); }
.context-menu {
@ -802,8 +801,9 @@ main {
margin: 0; }
.prefs-pane .actions {
background-color: #F9F9FA;
border-left: 1px solid #D7D7DB;
bottom: 0;
offset-inline-end: -1px;
offset-inline-end: 0;
padding: 20px;
position: fixed;
width: 400px; }

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M9.5 3s.428 2.43 1.249 3.251S14 7.5 14 7.5s-2.43.394-3.251 1.215S9.5 12 9.5 12s-.394-2.464-1.215-3.285S5 7.5 5 7.5s2.464-.428 3.285-1.249S9.5 3 9.5 3m0-2h-.014a2 2 0 0 0-1.96 1.68 7.536 7.536 0 0 1-.659 2.154 7.9 7.9 0 0 1-2.212.7 2 2 0 0 0 .029 3.945 7.733 7.733 0 0 1 2.183.658 7.74 7.74 0 0 1 .658 2.185A2 2 0 0 0 9.489 14H9.5a2 2 0 0 0 1.971-1.657 7.891 7.891 0 0 1 .7-2.209 7.566 7.566 0 0 1 2.154-.659 2 2 0 0 0 .027-3.944 7.694 7.694 0 0 1-2.181-.7 7.731 7.731 0 0 1-.7-2.181A2 2 0 0 0 9.5 1zM3 15.5a.5.5 0 0 1-.49-.421 3.047 3.047 0 0 0-.4-1.186 3.047 3.047 0 0 0-1.186-.4.5.5 0 0 1-.007-.986 3.147 3.147 0 0 0 1.192-.417 3.051 3.051 0 0 0 .4-1.171A.5.5 0 0 1 3 10.5a.5.5 0 0 1 .492.413 3.094 3.094 0 0 0 .417 1.179 3.142 3.142 0 0 0 1.178.416.5.5 0 0 1-.007.985 3.007 3.007 0 0 0-1.172.4 3.166 3.166 0 0 0-.416 1.192A.5.5 0 0 1 3 15.5zm-.5-11a.5.5 0 0 1-.49-.42 2.344 2.344 0 0 0-.265-.82 2.344 2.344 0 0 0-.82-.265.5.5 0 0 1-.007-.986 2.41 2.41 0 0 0 .827-.277A2.306 2.306 0 0 0 2.007.92.5.5 0 0 1 2.5.5a.5.5 0 0 1 .492.412 2.353 2.353 0 0 0 .278.818 2.372 2.372 0 0 0 .816.276.5.5 0 0 1-.007.985 2.306 2.306 0 0 0-.811.266 2.41 2.41 0 0 0-.277.827.5.5 0 0 1-.491.416z" fill="context-fill"/></svg>

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -8,7 +8,7 @@
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
<em:version>2017.09.01.1439-222d033f</em:version>
<em:version>2017.09.08.0882-3dbf720c</em:version>
<em:name>Activity Stream</em:name>
<em:description>A rich visual history feed and a reimagined home page make it easier than ever to find exactly what you're looking for in Firefox.</em:description>
<em:multiprocessCompatible>true</em:multiprocessCompatible>

Просмотреть файл

@ -22,6 +22,7 @@ const {SystemTickFeed} = Cu.import("resource://activity-stream/lib/SystemTickFee
const {TelemetryFeed} = Cu.import("resource://activity-stream/lib/TelemetryFeed.jsm", {});
const {TopSitesFeed} = Cu.import("resource://activity-stream/lib/TopSitesFeed.jsm", {});
const {TopStoriesFeed} = Cu.import("resource://activity-stream/lib/TopStoriesFeed.jsm", {});
const {HighlightsFeed} = Cu.import("resource://activity-stream/lib/HighlightsFeed.jsm", {});
const DEFAULT_SITES = new Map([
// This first item is the global list fallback for any unexpected geos
@ -149,6 +150,12 @@ const FEEDS_DATA = [
title: "Manages sections",
value: true
},
{
name: "section.highlights",
factory: () => new HighlightsFeed(),
title: "Fetches content recommendations from places db",
value: true
},
{
name: "section.topstories",
factory: () => new TopStoriesFeed(),

Просмотреть файл

@ -0,0 +1,96 @@
/* 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";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const {actionTypes: at} = Cu.import("resource://activity-stream/common/Actions.jsm", {});
const {shortURL} = Cu.import("resource://activity-stream/lib/ShortURL.jsm", {});
const {SectionsManager} = Cu.import("resource://activity-stream/lib/SectionsManager.jsm", {});
const {Dedupe} = Cu.import("resource://activity-stream/common/Dedupe.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
"resource://gre/modules/NewTabUtils.jsm");
const HIGHLIGHTS_MAX_LENGTH = 9;
const HIGHLIGHTS_UPDATE_TIME = 15 * 60 * 1000; // 15 minutes
const SECTION_ID = "highlights";
this.HighlightsFeed = class HighlightsFeed {
constructor() {
this.highlightsLastUpdated = 0;
this.highlights = [];
this.dedupe = new Dedupe(this._dedupeKey);
}
_dedupeKey(site) {
return site && site.url;
}
init() {
SectionsManager.onceInitialized(this.postInit.bind(this));
}
postInit() {
SectionsManager.enableSection(SECTION_ID);
}
uninit() {
SectionsManager.disableSection(SECTION_ID);
}
async fetchHighlights(broadcast = false) {
this.highlights = await NewTabUtils.activityStreamLinks.getHighlights();
for (let highlight of this.highlights) {
highlight.hostname = shortURL(Object.assign({}, highlight, {url: highlight.url}));
highlight.image = highlight.preview_image_url;
if (highlight.bookmarkGuid) {
highlight.type = "bookmark";
}
}
// Remove any Highlights that are in Top Sites already
const deduped = this.dedupe.group(this.store.getState().TopSites.rows, this.highlights);
this.highlights = deduped[1];
SectionsManager.updateSection(SECTION_ID, {rows: this.highlights}, this.highlightsLastUpdated === 0 || broadcast);
this.highlightsLastUpdated = Date.now();
}
onAction(action) {
switch (action.type) {
case at.INIT:
this.init();
break;
case at.NEW_TAB_LOAD:
if (this.highlights.length < HIGHLIGHTS_MAX_LENGTH) {
// If we haven't filled the highlights grid yet, fetch again.
this.fetchHighlights(true);
} else if (Date.now() - this.highlightsLastUpdated >= HIGHLIGHTS_UPDATE_TIME) {
// If the last time we refreshed the data is greater than 15 minutes, fetch again.
this.fetchHighlights(false);
}
break;
case at.MIGRATION_COMPLETED:
case at.PLACES_HISTORY_CLEARED:
case at.PLACES_LINK_DELETED:
case at.PLACES_LINK_BLOCKED:
this.fetchHighlights(true);
break;
case at.PLACES_BOOKMARK_ADDED:
case at.PLACES_BOOKMARK_REMOVED:
case at.TOP_SITES_UPDATED:
this.fetchHighlights(false);
break;
case at.UNINIT:
this.uninit();
break;
}
}
};
this.HIGHLIGHTS_UPDATE_TIME = HIGHLIGHTS_UPDATE_TIME;
this.EXPORTED_SYMBOLS = ["HighlightsFeed", "HIGHLIGHTS_UPDATE_TIME", "SECTION_ID"];

Просмотреть файл

@ -88,19 +88,24 @@ class BookmarksObserver extends Observer {
* @param {int} dateAdded
* @param {str} guid The unique id of the bookmark
*/
async onItemAdded(...args) {
onItemAdded(...args) {
const type = args[3];
const guid = args[7];
if (type !== PlacesUtils.bookmarks.TYPE_BOOKMARK) {
return;
}
try {
// bookmark: {bookmarkGuid, bookmarkTitle, lastModified, url}
const bookmark = await NewTabUtils.activityStreamProvider.getBookmark(guid);
this.dispatch({type: at.PLACES_BOOKMARK_ADDED, data: bookmark});
} catch (e) {
Cu.reportError(e);
}
const uri = args[4];
const bookmarkTitle = args[5];
const dateAdded = args[6];
const bookmarkGuid = args[7];
this.dispatch({
type: at.PLACES_BOOKMARK_ADDED,
data: {
bookmarkGuid,
bookmarkTitle,
dateAdded,
url: uri.spec
}
});
}
/**

Просмотреть файл

@ -34,7 +34,26 @@ const BUILT_IN_SECTIONS = {
emptyState: {
message: {id: "topstories_empty_state", values: {provider: options.provider_name}},
icon: "check"
}
},
order: 0
}),
"feeds.section.highlights": options => ({
id: "highlights",
pref: {
titleString: {id: "settings_pane_highlights_header"},
descString: {id: "settings_pane_highlights_body2"}
},
shouldHidePref: false,
eventSource: "HIGHLIGHTS",
icon: "highlights",
title: {id: "header_highlights"},
maxRows: 3,
availableContextMenuOptions: ["CheckBookmark", "SaveToPocket", "Separator", "OpenInNewWindow", "OpenInPrivateWindow", "Separator", "BlockUrl"],
emptyState: {
message: {id: "highlights_empty_state"},
icon: "highlights"
},
order: 1
})
};

Просмотреть файл

@ -159,10 +159,12 @@ this.TelemetryFeed = class TelemetryFeed {
* Lazily initialize PingCentre to send pings
*/
get pingCentre() {
const ACTIVITY_STREAM_ID = "activity-stream";
Object.defineProperty(this, "pingCentre",
{
value: new PingCentre({
topic: "activity-stream",
topic: ACTIVITY_STREAM_ID,
filter: ACTIVITY_STREAM_ID,
overrideEndpointPref: ACTIVITY_STREAM_ENDPOINT_PREF
})
});

Просмотреть файл

@ -24,6 +24,7 @@ function getPath(url) {
this.TippyTopProvider = class TippyTopProvider {
constructor() {
this._sitesByDomain = new Map();
this.initialized = false;
}
async init() {
// Load the Tippy Top sites from the json manifest.
@ -35,6 +36,7 @@ this.TippyTopProvider = class TippyTopProvider {
this._sitesByDomain.set(getDomain(url), site);
}
}
this.initialized = true;
} catch (error) {
Cu.reportError("Failed to load tippy top manifest.");
}

Просмотреть файл

@ -81,6 +81,10 @@ this.TopSitesFeed = class TopSitesFeed {
return pinned.slice(0, TOP_SITES_SHOWMORE_LENGTH);
}
async refresh(target = null) {
if (!this._tippyTopProvider.initialized) {
await this._tippyTopProvider.init();
}
const links = await this.getLinksWithDefaults();
// First, cache existing screenshots in case we need to reuse them
@ -165,7 +169,6 @@ this.TopSitesFeed = class TopSitesFeed {
async onAction(action) {
switch (action.type) {
case at.INIT:
await this._tippyTopProvider.init();
this.refresh();
break;
case at.NEW_TAB_LOAD:

Просмотреть файл

@ -68,7 +68,7 @@ describe("Reducers", () => {
url: "bar.com",
bookmarkGuid: "bookmark123",
bookmarkTitle: "Title for bar.com",
lastModified: 1234567
dateAdded: 1234567
}
};
const nextState = TopSites(oldState, action);
@ -77,7 +77,7 @@ describe("Reducers", () => {
assert.equal(newRow.url, action.data.url);
assert.equal(newRow.bookmarkGuid, action.data.bookmarkGuid);
assert.equal(newRow.bookmarkTitle, action.data.bookmarkTitle);
assert.equal(newRow.bookmarkDateCreated, action.data.lastModified);
assert.equal(newRow.bookmarkDateCreated, action.data.dateAdded);
// old row is unchanged
assert.equal(nextState.rows[0], oldState.rows[0]);
@ -92,7 +92,7 @@ describe("Reducers", () => {
url: "bar.com",
bookmarkGuid: "bookmark123",
bookmarkTitle: "Title for bar.com",
lastModified: 123456
dateAdded: 123456
}]
};
const action = {type: at.PLACES_BOOKMARK_REMOVED, data: {url: "bar.com"}};
@ -211,7 +211,8 @@ describe("Reducers", () => {
title: `Foo Bar ${i}`,
initialized: false,
rows: [{url: "www.foo.bar"}, {url: "www.other.url"}],
order: i
order: i,
type: "history"
}));
});
@ -254,6 +255,24 @@ describe("Reducers", () => {
assert.equal(newState[0].id, newSection.id);
assert.ok(newState[0].order < newState[1].order);
});
it("should insert sections with a 0 `order` at the top on SECTION_REGISTER", () => {
const newSection = {id: "new_section", order: 0};
const action = {type: at.SECTION_REGISTER, data: newSection};
const newState = Sections(oldState, action);
assert.equal(newState[0].id, newSection.id);
});
it("should insert sections with a 1 `order` in the right spot on SECTION_REGISTER", () => {
const newSection = {id: "new_section", order: 1};
const action = {type: at.SECTION_REGISTER, data: newSection};
const newState = Sections(oldState, action);
assert.equal(newState[1].id, newSection.id);
});
it("should insert sections with higher `order` than any existing at the bottom on SECTION_REGISTER", () => {
const newSection = {id: "new_section", order: 10000};
const action = {type: at.SECTION_REGISTER, data: newSection};
const newState = Sections(oldState, action);
assert.equal(newState[newState.length - 1].id, newSection.id);
});
it("should set newSection.rows === [] if no rows are provided on SECTION_REGISTER", () => {
const action = {type: at.SECTION_REGISTER, data: {id: "foo_bar_5", title: "Foo Bar 5"}};
const newState = Sections(oldState, action);
@ -328,7 +347,7 @@ describe("Reducers", () => {
url: "www.foo.bar",
bookmarkGuid: "bookmark123",
bookmarkTitle: "Title for bar.com",
lastModified: 1234567
dateAdded: 1234567
}
};
const nextState = Sections(oldState, action);
@ -338,9 +357,10 @@ describe("Reducers", () => {
// new row has bookmark data
assert.equal(newRow.url, action.data.url);
assert.equal(newRow.type, "bookmark");
assert.equal(newRow.bookmarkGuid, action.data.bookmarkGuid);
assert.equal(newRow.bookmarkTitle, action.data.bookmarkTitle);
assert.equal(newRow.bookmarkDateCreated, action.data.lastModified);
assert.equal(newRow.bookmarkDateCreated, action.data.dateAdded);
// old row is unchanged
assert.equal(oldRow, oldState[0].rows[1]);
@ -362,14 +382,16 @@ describe("Reducers", () => {
item.rows[0].bookmarkGuid = "bookmark123";
item.rows[0].bookmarkTitle = "Title for bar.com";
item.rows[0].bookmarkDateCreated = 1234567;
item.rows[0].type = "bookmark";
});
const nextState = Sections(oldState, action);
// check a section to ensure the correct bookmark was removed
const newRow = nextState[0].rows[0];
const oldRow = nextState[0].rows[1];
// new row has bookmark data
// new row isn't a bookmark
assert.equal(newRow.url, action.data.url);
assert.equal(newRow.type, "history");
assert.isUndefined(newRow.bookmarkGuid);
assert.isUndefined(newRow.bookmarkTitle);
assert.isUndefined(newRow.bookmarkDateCreated);

Просмотреть файл

@ -23,7 +23,8 @@ describe("ActivityStream", () => {
"lib/SystemTickFeed.jsm": {SystemTickFeed: Fake},
"lib/TelemetryFeed.jsm": {TelemetryFeed: Fake},
"lib/TopSitesFeed.jsm": {TopSitesFeed: Fake},
"lib/TopStoriesFeed.jsm": {TopStoriesFeed: Fake}
"lib/TopStoriesFeed.jsm": {TopStoriesFeed: Fake},
"lib/HighlightsFeed.jsm": {HighlightsFeed: Fake}
}));
as = new ActivityStream();
sandbox.stub(as.store, "init");

Просмотреть файл

@ -0,0 +1,177 @@
"use strict";
const injector = require("inject!lib/HighlightsFeed.jsm");
const {GlobalOverrider} = require("test/unit/utils");
const {actionTypes: at} = require("common/Actions.jsm");
const {Dedupe} = require("common/Dedupe.jsm");
const FAKE_LINKS = new Array(9).fill(null).map((v, i) => ({url: `http://www.site${i}.com`}));
describe("Top Sites Feed", () => {
let HighlightsFeed;
let HIGHLIGHTS_UPDATE_TIME;
let SECTION_ID;
let feed;
let globals;
let sandbox;
let links;
let clock;
let fakeNewTabUtils;
let sectionsManagerStub;
let shortURLStub;
beforeEach(() => {
globals = new GlobalOverrider();
sandbox = globals.sandbox;
fakeNewTabUtils = {activityStreamLinks: {getHighlights: sandbox.spy(() => Promise.resolve(links))}};
sectionsManagerStub = {
onceInitialized: sinon.stub().callsFake(callback => callback()),
enableSection: sinon.spy(),
disableSection: sinon.spy(),
updateSection: sinon.spy(),
sections: new Map([["highlights", {}]])
};
shortURLStub = sinon.stub().callsFake(site => site.url);
globals.set("NewTabUtils", fakeNewTabUtils);
({HighlightsFeed, HIGHLIGHTS_UPDATE_TIME, SECTION_ID} = injector({
"lib/ShortURL.jsm": {shortURL: shortURLStub},
"lib/SectionsManager.jsm": {SectionsManager: sectionsManagerStub},
"common/Dedupe.jsm": {Dedupe}
}));
feed = new HighlightsFeed();
feed.store = {dispatch: sinon.spy(), getState() { return {TopSites: {rows: Array(12).fill(null).map((v, i) => ({url: `http://www.topsite${i}.com`}))}}; }};
links = FAKE_LINKS;
clock = sinon.useFakeTimers();
});
afterEach(() => {
globals.restore();
clock.restore();
});
describe("#init", () => {
it("should create a HighlightsFeed", () => {
assert.instanceOf(feed, HighlightsFeed);
});
it("should call SectionsManager.onceInitialized on INIT", () => {
feed.onAction({type: at.INIT});
assert.calledOnce(sectionsManagerStub.onceInitialized);
});
it("should enable its section", () => {
feed.onAction({type: at.INIT});
assert.calledOnce(sectionsManagerStub.enableSection);
assert.calledWith(sectionsManagerStub.enableSection, SECTION_ID);
});
it("should *not* fetch highlights on init to avoid loading Places too early", () => {
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.INIT});
assert.notCalled(feed.fetchHighlights);
});
});
describe("#fetchHighlights", () => {
it("should add hostname and image to each link", async () => {
links = [{url: "https://mozilla.org", preview_image_url: "https://mozilla.org/preview.jog"}];
await feed.fetchHighlights();
assert.equal(feed.highlights[0].hostname, links[0].url);
assert.equal(feed.highlights[0].image, links[0].preview_image_url);
});
it("should not include any links already in Top Sites", async () => {
links = [
{url: "https://mozilla.org"},
{url: "http://www.topsite0.com"},
{url: "http://www.topsite1.com"},
{url: "http://www.topsite2.com"}
];
await feed.fetchHighlights();
assert.equal(feed.highlights.length, 1);
assert.deepEqual(feed.highlights[0], links[0]);
});
it("should set type to bookmark if there is a bookmarkGuid", async () => {
links = [{url: "https://mozilla.org", type: "history", bookmarkGuid: "1234567890"}];
await feed.fetchHighlights();
assert.equal(feed.highlights[0].type, "bookmark");
});
});
describe("#uninit", () => {
it("should disable its section", () => {
feed.onAction({type: at.UNINIT});
assert.calledOnce(sectionsManagerStub.disableSection);
assert.calledWith(sectionsManagerStub.disableSection, SECTION_ID);
});
});
describe("#onAction", () => {
it("should fetch highlights on NEW_TAB_LOAD after update interval", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.NEW_TAB_LOAD});
assert.notCalled(feed.fetchHighlights);
clock.tick(HIGHLIGHTS_UPDATE_TIME);
feed.onAction({type: at.NEW_TAB_LOAD});
assert.calledOnce(feed.fetchHighlights);
});
it("should fetch highlights on NEW_TAB_LOAD if grid is empty", async () => {
links = [];
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.NEW_TAB_LOAD});
assert.calledOnce(feed.fetchHighlights);
});
it("should fetch highlights on NEW_TAB_LOAD if grid isn't full", async () => {
links = new Array(8).fill(null).map((v, i) => ({url: `http://www.site${i}.com`}));
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.NEW_TAB_LOAD});
assert.calledOnce(feed.fetchHighlights);
});
it("should fetch highlights on MIGRATION_COMPLETED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.MIGRATION_COMPLETED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, true);
});
it("should fetch highlights on PLACES_HISTORY_CLEARED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_HISTORY_CLEARED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, true);
});
it("should fetch highlights on PLACES_LINK_DELETED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_LINK_DELETED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, true);
});
it("should fetch highlights on PLACES_LINK_BLOCKED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_LINK_BLOCKED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, true);
});
it("should fetch highlights on PLACES_BOOKMARK_ADDED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_BOOKMARK_ADDED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, false);
});
it("should fetch highlights on PLACES_BOOKMARK_REMOVED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.PLACES_BOOKMARK_REMOVED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, false);
});
it("should fetch highlights on TOP_SITES_UPDATED", async () => {
await feed.fetchHighlights();
feed.fetchHighlights = sinon.spy();
feed.onAction({type: at.TOP_SITES_UPDATED});
assert.calledOnce(feed.fetchHighlights);
assert.calledWith(feed.fetchHighlights, false);
});
});
});

Просмотреть файл

@ -3,7 +3,7 @@ const {HistoryObserver, BookmarksObserver} = PlacesFeed;
const {GlobalOverrider} = require("test/unit/utils");
const {actionTypes: at} = require("common/Actions.jsm");
const FAKE_BOOKMARK = {bookmarkGuid: "xi31", bookmarkTitle: "Foo", lastModified: 123214232, url: "foo.com"};
const FAKE_BOOKMARK = {bookmarkGuid: "xi31", bookmarkTitle: "Foo", dateAdded: 123214232, url: "foo.com"};
const TYPE_BOOKMARK = 0; // This is fake, for testing
const BLOCKED_EVENT = "newtab-linkBlocked"; // The event dispatched in NewTabUtils when a link is blocked;
@ -210,28 +210,18 @@ describe("PlacesFeed", () => {
});
describe("#onItemAdded", () => {
beforeEach(() => {
// Make sure getBookmark returns our fake bookmark if it is called with the expected guid
sandbox.stub(global.NewTabUtils.activityStreamProvider, "getBookmark")
.withArgs(FAKE_BOOKMARK.guid).returns(Promise.resolve(FAKE_BOOKMARK));
});
it("should dispatch a PLACES_BOOKMARK_ADDED action with the bookmark data", async () => {
// Yes, onItemAdded has at least 8 arguments. See function definition for docs.
const args = [null, null, null, TYPE_BOOKMARK, null, null, null, FAKE_BOOKMARK.guid];
const args = [null, null, null, TYPE_BOOKMARK,
{spec: FAKE_BOOKMARK.url}, FAKE_BOOKMARK.bookmarkTitle,
FAKE_BOOKMARK.dateAdded,
FAKE_BOOKMARK.bookmarkGuid
];
await observer.onItemAdded(...args);
assert.calledWith(dispatch, {type: at.PLACES_BOOKMARK_ADDED, data: FAKE_BOOKMARK});
});
it("should catch errors gracefully", async () => {
const e = new Error("test error");
global.NewTabUtils.activityStreamProvider.getBookmark.restore();
sandbox.stub(global.NewTabUtils.activityStreamProvider, "getBookmark")
.returns(Promise.reject(e));
const args = [null, null, null, TYPE_BOOKMARK, null, null, null, FAKE_BOOKMARK.guid];
await observer.onItemAdded(...args);
assert.calledWith(global.Components.utils.reportError, e);
});
it("should ignore events that are not of TYPE_BOOKMARK", async () => {
const args = [null, null, null, "nottypebookmark"];
await observer.onItemAdded(...args);

Просмотреть файл

@ -29,8 +29,9 @@ describe("SectionsManager", () => {
SectionsManager.sections.clear();
SectionsManager.initialized = false;
SectionsManager.init();
assert.equal(SectionsManager.sections.size, 1);
assert.equal(SectionsManager.sections.size, 2);
assert.ok(SectionsManager.sections.has("topstories"));
assert.ok(SectionsManager.sections.has("highlights"));
});
it("should set .initialized to true", () => {
SectionsManager.sections.clear();
@ -215,11 +216,14 @@ describe("SectionsFeed", () => {
it("should call onAddSection for any already added sections in SectionsManager", () => {
SectionsManager.init();
assert.ok(SectionsManager.sections.has("topstories"));
assert.ok(SectionsManager.sections.has("highlights"));
const topstories = SectionsManager.sections.get("topstories");
const highlights = SectionsManager.sections.get("highlights");
sinon.spy(feed, "onAddSection");
feed.init();
assert.calledOnce(feed.onAddSection);
assert.calledTwice(feed.onAddSection);
assert.calledWith(feed.onAddSection, SectionsManager.ADD_SECTION, "topstories", topstories);
assert.calledWith(feed.onAddSection, SectionsManager.ADD_SECTION, "highlights", highlights);
});
});
describe("#uninit", () => {

Просмотреть файл

@ -15,7 +15,7 @@ const FAKE_SCREENSHOT = "data123";
function FakeTippyTopProvider() {}
FakeTippyTopProvider.prototype = {
async init() {},
async init() { this.initialized = true; },
processSite(site) { return site; }
};
@ -247,6 +247,11 @@ describe("Top Sites Feed", () => {
});
});
describe("#refresh", () => {
it("should initialise _tippyTopProvider if it's not already initialised", async () => {
feed._tippyTopProvider.initialized = false;
await feed.refresh(action);
assert.ok(feed._tippyTopProvider.initialized);
});
it("should dispatch an action with the links returned", async () => {
sandbox.stub(feed, "getScreenshot");
await feed.refresh(action);

Просмотреть файл

@ -47,6 +47,7 @@ const CONTENT = {
label: GetStringFromName(changeAutofillOptsKey),
accessKey: "C",
callbackState: "open-pref",
disableHighlight: true,
},
options: {
persistWhileVisible: true,
@ -141,9 +142,9 @@ let FormAutofillDoorhanger = {
return [null, null];
}
let {label, accessKey, callbackState} = mainActionParams;
let {label, accessKey, disableHighlight, callbackState} = mainActionParams;
let callback = resolve.bind(null, callbackState);
let mainAction = {label, accessKey, callback};
let mainAction = {label, accessKey, callback, disableHighlight};
if (!secondaryActionParams) {
return [mainAction, null];

Просмотреть файл

@ -24,11 +24,11 @@
}
#onboarding-overlay-button {
padding: 0;
padding: 10px 0 0 0;
position: absolute;
cursor: pointer;
top: 34px;
offset-inline-start: 34px;
top: 4px;
offset-inline-start: 12px;
border: none;
/* Set to none so no grey contrast background in the high-contrast mode */
background: none;

Просмотреть файл

@ -22,7 +22,7 @@ const BRAND_SHORT_NAME = Services.strings
const PROMPT_COUNT_PREF = "browser.onboarding.notification.prompt-count";
const ONBOARDING_DIALOG_ID = "onboarding-overlay-dialog";
const ONBOARDING_MIN_WIDTH_PX = 960;
const SPEECH_BUBBLE_MIN_WIDTH_PX = 1150;
const SPEECH_BUBBLE_MIN_WIDTH_PX = 1130;
/**
* Add any number of tours, key is the tourId, value should follow the format below

Просмотреть файл

@ -11,6 +11,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ClientID",
"resource://gre/modules/ClientID.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
"resource://gre/modules/TelemetryEnvironment.jsm");
const PREF_BRANCH = "browser.ping-centre.";
@ -36,6 +38,7 @@ class PingCentre {
}
this._topic = options.topic;
this._filter = options.filter;
this._prefs = Services.prefs.getBranch("");
this._setPingEndpoint(options.topic, options.overrideEndpointPref);
@ -89,7 +92,23 @@ class PingCentre {
this._fhrEnabled = this._prefs.getBoolPref(prefKey);
}
_createExperimentsString(activeExperiments) {
let experimentsString = "";
for (let experimentID in activeExperiments) {
if (!activeExperiments[experimentID] ||
!activeExperiments[experimentID].branch ||
(this._filter && !experimentID.includes(this._filter))) {
continue;
}
let expString = `${experimentID}:${activeExperiments[experimentID].branch}`;
experimentsString = experimentsString.concat(`${expString};`);
}
return experimentsString;
}
async sendPing(data) {
let experiments = TelemetryEnvironment.getActiveExperiments();
let experimentsString = this._createExperimentsString(experiments);
if (!this.enabled) {
return Promise.resolve();
}
@ -98,6 +117,7 @@ class PingCentre {
const payload = Object.assign({
topic: this._topic,
client_id: clientID,
shield_id: experimentsString,
release_channel: AppConstants.MOZ_UPDATE_CHANNEL
}, data);

Просмотреть файл

@ -61,7 +61,6 @@ addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
addons_install_button=install
addons_uninstall_button=uninstall
addons_adb_label=ADB Helper Add-on
addons_adapters_label=Tools Adapters Add-on
addons_adb_warning=USB devices wont be detected without this add-on
addons_status_unknown=?
addons_status_installed=Installed

Просмотреть файл

@ -17,9 +17,6 @@ var {initCssProperties} = require("devtools/shared/fronts/css-properties");
loader.lazyGetter(this, "StyleSheetsFront",
() => require("devtools/shared/fronts/stylesheets").StyleSheetsFront);
loader.lazyGetter(this, "StyleEditorFront",
() => require("devtools/shared/fronts/styleeditor").StyleEditorFront);
var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
EventEmitter.decorate(this);
@ -54,12 +51,7 @@ StyleEditorPanel.prototype = {
this.target.on("close", this.destroy);
if (this.target.form.styleSheetsActor) {
this._debuggee = StyleSheetsFront(this.target.client, this.target.form);
} else {
/* We're talking to a pre-Firefox 29 server-side */
this._debuggee = StyleEditorFront(this.target.client, this.target.form);
}
this._debuggee = StyleSheetsFront(this.target.client, this.target.form);
// Initialize the CSS properties database.
const {cssProperties} = yield initCssProperties(this._toolbox);

Просмотреть файл

@ -33,7 +33,6 @@ function CloseUI() {
function BuildUI(addons) {
BuildItem(addons.adb, "adb");
BuildItem(addons.adapters, "adapters");
for (let addon of addons.simulators) {
BuildItem(addon, "simulator");
}
@ -82,16 +81,6 @@ function BuildItem(addon, type) {
li.setAttribute("addon", type);
name.textContent = Strings.GetStringFromName("addons_adb_label");
break;
case "adapters":
li.setAttribute("addon", type);
try {
name.textContent = Strings.GetStringFromName("addons_adapters_label");
} catch (e) {
// This code (bug 1081093) will be backported to Aurora, which doesn't
// contain this string.
name.textContent = "Tools Adapters Add-on";
}
break;
case "simulator":
li.setAttribute("addon", "simulator-" + addon.version);
let stability = Strings.GetStringFromName("addons_" + addon.stability);

Просмотреть файл

@ -87,22 +87,16 @@ var UI = {
this.reportError("error_appProjectsLoadFailed");
});
// Auto install the ADB Addon Helper and Tools Adapters. Only once.
// Auto install the ADB Addon Helper. Only once.
// If the user decides to uninstall any of this addon, we won't install it again.
let autoinstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
let autoinstallFxdtAdapters = Services.prefs.getBoolPref("devtools.webide.autoinstallFxdtAdapters");
if (autoinstallADBHelper) {
GetAvailableAddons().then(addons => {
addons.adb.install();
}, console.error);
}
if (autoinstallFxdtAdapters) {
GetAvailableAddons().then(addons => {
addons.adapters.install();
}, console.error);
}
Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", false);
this.setupDeck();

Просмотреть файл

@ -13,10 +13,8 @@ const ADDONS_URL = "devtools.webide.addonsURL";
var SIMULATOR_LINK = Services.prefs.getCharPref("devtools.webide.simulatorAddonsURL");
var ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
var ADAPTERS_LINK = Services.prefs.getCharPref("devtools.webide.adaptersAddonURL");
var SIMULATOR_ADDON_ID = Services.prefs.getCharPref("devtools.webide.simulatorAddonID");
var ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID");
var ADAPTERS_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adaptersAddonID");
var platform = Services.appShell.hiddenDOMWindow.navigator.platform;
var OS = "";
@ -38,7 +36,7 @@ addonsListener.onDisabled =
addonsListener.onInstalled =
addonsListener.onUninstalled = (updatedAddon) => {
GetAvailableAddons().then(addons => {
for (let a of [...addons.simulators, addons.adb, addons.adapters]) {
for (let a of [...addons.simulators, addons.adb]) {
if (a.addonID == updatedAddon.id) {
a.updateInstallStatus();
}
@ -62,7 +60,6 @@ var GetAvailableAddons = exports.GetAvailableAddons = function () {
}
}
addons.adb = new ADBAddon();
addons.adapters = new AdaptersAddon();
resolve(addons);
}, e => {
GetAvailableAddons_promise = null;
@ -186,11 +183,3 @@ function ADBAddon() {
this.updateInstallStatus();
}
ADBAddon.prototype = Object.create(Addon.prototype);
function AdaptersAddon() {
EventEmitter.decorate(this);
this.xpiLink = ADAPTERS_LINK.replace(/#OS#/g, OS);
this.addonID = ADAPTERS_ADDON_ID;
this.updateInstallStatus();
}
AdaptersAddon.prototype = Object.create(Addon.prototype);

Просмотреть файл

@ -166,7 +166,7 @@ ProjectList.prototype = {
try {
url = new URL(tab.url);
} catch (e) {
// Don't try to handle invalid URLs, especially from Valence.
// Don't try to handle invalid URLs
continue;
}
// Wanted to use nsIFaviconService here, but it only works for visited

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -26,10 +26,6 @@ support-files =
addons/adbhelper-linux64.xpi
addons/adbhelper-win32.xpi
addons/adbhelper-mac64.xpi
addons/fxdt-adapters-linux32.xpi
addons/fxdt-adapters-linux64.xpi
addons/fxdt-adapters-win32.xpi
addons/fxdt-adapters-mac64.xpi
build_app1/package.json
build_app2/manifest.webapp
build_app2/package.json

Просмотреть файл

@ -29,7 +29,6 @@ Services.prefs.setBoolPref("devtools.webide.enableLocalRuntime", true);
Services.prefs.setCharPref("devtools.webide.addonsURL", TEST_BASE + "addons/simulators.json");
Services.prefs.setCharPref("devtools.webide.simulatorAddonsURL", TEST_BASE + "addons/fxos_#SLASHED_VERSION#_simulator-#OS#.xpi");
Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi");
Services.prefs.setCharPref("devtools.webide.adaptersAddonURL", TEST_BASE + "addons/fxdt-adapters-#OS#.xpi");
Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json");
Services.prefs.setCharPref("devtools.devices.url", TEST_BASE + "browser_devices.json");
@ -40,7 +39,6 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webide.enabled");
Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
Services.prefs.clearUserPref("devtools.webide.busyTimeout");
Services.prefs.clearUserPref("devtools.webide.lastSelectedProject");
Services.prefs.clearUserPref("devtools.webide.lastConnectedRuntime");
@ -50,7 +48,6 @@ var openWebIDE = Task.async(function* (autoInstallAddons) {
info("opening WebIDE");
Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", !!autoInstallAddons);
Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", !!autoInstallAddons);
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
let win = ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);

Просмотреть файл

@ -4,7 +4,6 @@
pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
pref("devtools.webide.autoinstallADBHelper", true);
pref("devtools.webide.autoinstallFxdtAdapters", true);
pref("devtools.webide.autoConnectRuntime", true);
pref("devtools.webide.restoreLastProject", true);
pref("devtools.webide.enableLocalRuntime", false);
@ -14,8 +13,6 @@ pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozil
pref("devtools.webide.simulatorAddonRegExp", "fxos_(.*)_simulator@mozilla\\.org$");
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
pref("devtools.webide.adaptersAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/valence/#OS#/valence-#OS#-latest.xpi");
pref("devtools.webide.adaptersAddonID", "fxdevtools-adapters@mozilla.org");
pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
pref("devtools.webide.lastConnectedRuntime", "");
pref("devtools.webide.lastSelectedProject", "");

Просмотреть файл

@ -54,7 +54,6 @@ DevToolsModules(
'source.js',
'storage.js',
'string.js',
'styleeditor.js',
'styles.js',
'stylesheets.js',
'tab.js',
@ -101,7 +100,7 @@ with Files('source.js'):
with Files('storage.js'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Storage Inspector')
with Files('styleeditor.js'):
with Files('stylesheets.js'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Style Editor')
with Files('webaudio.js'):

Просмотреть файл

@ -1,497 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {Cc, Ci} = require("chrome");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const promise = require("promise");
const protocol = require("devtools/shared/protocol");
const {fetch} = require("devtools/shared/DevToolsUtils");
const {oldStyleSheetSpec, styleEditorSpec} = require("devtools/shared/specs/styleeditor");
loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic"));
var TRANSITION_CLASS = "moz-styleeditor-transitioning";
var TRANSITION_DURATION_MS = 500;
var TRANSITION_RULE = ":root.moz-styleeditor-transitioning, " +
":root.moz-styleeditor-transitioning * {\n" +
"transition-duration: " + TRANSITION_DURATION_MS +
"ms !important;\n" +
"transition-delay: 0ms !important;\n" +
"transition-timing-function: ease-out !important;\n" +
"transition-property: all !important;\n" +
"}";
var OldStyleSheetActor = protocol.ActorClassWithSpec(oldStyleSheetSpec, {
toString: function () {
return "[OldStyleSheetActor " + this.actorID + "]";
},
/**
* Window of target
*/
get window() {
return this._window || this.parentActor.window;
},
/**
* Document of target.
*/
get document() {
return this.window.document;
},
/**
* URL of underlying stylesheet.
*/
get href() {
return this.rawSheet.href;
},
/**
* Retrieve the index (order) of stylesheet in the document.
*
* @return number
*/
get styleSheetIndex() {
if (this._styleSheetIndex == -1) {
for (let i = 0; i < this.document.styleSheets.length; i++) {
if (this.document.styleSheets[i] == this.rawSheet) {
this._styleSheetIndex = i;
break;
}
}
}
return this._styleSheetIndex;
},
initialize: function (styleSheet, parentActor, window) {
protocol.Actor.prototype.initialize.call(this, null);
this.rawSheet = styleSheet;
this.parentActor = parentActor;
this.conn = this.parentActor.conn;
this._window = window;
// text and index are unknown until source load
this.text = null;
this._styleSheetIndex = -1;
this._transitionRefCount = 0;
// if this sheet has an @import, then it's rules are loaded async
let ownerNode = this.rawSheet.ownerNode;
if (ownerNode) {
let onSheetLoaded = (event) => {
ownerNode.removeEventListener("load", onSheetLoaded);
this._notifyPropertyChanged("ruleCount");
};
ownerNode.addEventListener("load", onSheetLoaded);
}
},
/**
* Get the current state of the actor
*
* @return {object}
* With properties of the underlying stylesheet, plus 'text',
* 'styleSheetIndex' and 'parentActor' if it's @imported
*/
form: function (detail) {
if (detail === "actorid") {
return this.actorID;
}
let docHref;
if (this.rawSheet.ownerNode) {
if (this.rawSheet.ownerNode instanceof Ci.nsIDOMHTMLDocument) {
docHref = this.rawSheet.ownerNode.location.href;
}
if (this.rawSheet.ownerNode.ownerDocument) {
docHref = this.rawSheet.ownerNode.ownerDocument.location.href;
}
}
let form = {
actor: this.actorID, // actorID is set when this actor is added to a pool
href: this.href,
nodeHref: docHref,
disabled: this.rawSheet.disabled,
title: this.rawSheet.title,
system: !CssLogic.isContentStylesheet(this.rawSheet),
styleSheetIndex: this.styleSheetIndex
};
try {
form.ruleCount = this.rawSheet.cssRules.length;
} catch (e) {
// stylesheet had an @import rule that wasn't loaded yet
}
return form;
},
/**
* Toggle the disabled property of the style sheet
*
* @return {object}
* 'disabled' - the disabled state after toggling.
*/
toggleDisabled: function () {
this.rawSheet.disabled = !this.rawSheet.disabled;
this._notifyPropertyChanged("disabled");
return this.rawSheet.disabled;
},
/**
* Send an event notifying that a property of the stylesheet
* has changed.
*
* @param {string} property
* Name of the changed property
*/
_notifyPropertyChanged: function (property) {
this.emit("property-change", property, this.form()[property]);
},
/**
* Fetch the source of the style sheet from its URL. Send a "sourceLoad"
* event when it's been fetched.
*/
fetchSource: function () {
this._getText().then((content) => {
this.emit("source-load", this.text);
});
},
/**
* Fetch the text for this stylesheet from the cache or network. Return
* cached text if it's already been fetched.
*
* @return {Promise}
* Promise that resolves with a string text of the stylesheet.
*/
_getText: function () {
if (this.text) {
return promise.resolve(this.text);
}
if (!this.href) {
// this is an inline <style> sheet
let content = this.rawSheet.ownerNode.textContent;
this.text = content;
return promise.resolve(content);
}
let options = {
policy: Ci.nsIContentPolicy.TYPE_INTERNAL_STYLESHEET,
window: this.window,
charset: this._getCSSCharset()
};
return fetch(this.href, options).then(({ content }) => {
this.text = content;
return content;
});
},
/**
* Get the charset of the stylesheet according to the character set rules
* defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
* Note that some of the algorithm is implemented in DevToolsUtils.fetch.
*/
_getCSSCharset: function () {
let sheet = this.rawSheet;
if (sheet) {
// Do we have a @charset rule in the stylesheet?
// step 2 of syndata.html (without the BOM check).
if (sheet.cssRules) {
let rules = sheet.cssRules;
if (rules.length
&& rules.item(0).type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
return rules.item(0).encoding;
}
}
// step 3: charset attribute of <link> or <style> element, if it exists
if (sheet.ownerNode && sheet.ownerNode.getAttribute) {
let linkCharset = sheet.ownerNode.getAttribute("charset");
if (linkCharset != null) {
return linkCharset;
}
}
// step 4 (1 of 2): charset of referring stylesheet.
let parentSheet = sheet.parentStyleSheet;
if (parentSheet && parentSheet.cssRules &&
parentSheet.cssRules[0].type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
return parentSheet.cssRules[0].encoding;
}
// step 4 (2 of 2): charset of referring document.
if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
return sheet.ownerNode.ownerDocument.characterSet;
}
}
// step 5: default to utf-8.
return "UTF-8";
},
/**
* Update the style sheet in place with new text.
*
* @param {object} request
* 'text' - new text
* 'transition' - whether to do CSS transition for change.
*/
update: function (text, transition) {
DOMUtils.parseStyleSheet(this.rawSheet, text);
this.text = text;
this._notifyPropertyChanged("ruleCount");
if (transition) {
this._insertTransistionRule();
} else {
this._notifyStyleApplied();
}
},
/**
* Insert a catch-all transition rule into the document. Set a timeout
* to remove the rule after a certain time.
*/
_insertTransistionRule: function () {
// Insert the global transition rule
// Use a ref count to make sure we do not add it multiple times.. and remove
// it only when all pending StyleEditor-generated transitions ended.
if (this._transitionRefCount == 0) {
this.rawSheet.insertRule(TRANSITION_RULE, this.rawSheet.cssRules.length);
this.document.documentElement.classList.add(TRANSITION_CLASS);
}
this._transitionRefCount++;
// Set up clean up and commit after transition duration (+10% buffer)
// @see _onTransitionEnd
this.window.setTimeout(this._onTransitionEnd.bind(this),
Math.floor(TRANSITION_DURATION_MS * 1.1));
},
/**
* This cleans up class and rule added for transition effect and then
* notifies that the style has been applied.
*/
_onTransitionEnd: function () {
if (--this._transitionRefCount == 0) {
this.document.documentElement.classList.remove(TRANSITION_CLASS);
this.rawSheet.deleteRule(this.rawSheet.cssRules.length - 1);
}
this.emit("style-applied");
}
});
exports.OldStyleSheetActor = OldStyleSheetActor;
/**
* Creates a StyleEditorActor. StyleEditorActor provides remote access to the
* stylesheets of a document.
*/
var StyleEditorActor = protocol.ActorClassWithSpec(styleEditorSpec, {
/**
* The window we work with, taken from the parent actor.
*/
get window() {
return this.parentActor.window;
},
/**
* The current content document of the window we work with.
*/
get document() {
return this.window.document;
},
form: function () {
return { actor: this.actorID };
},
initialize: function (conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
// keep a map of sheets-to-actors so we don't create two actors for one sheet
this._sheets = new Map();
},
/**
* Destroy the current StyleEditorActor instance.
*/
destroy: function () {
this._sheets.clear();
},
/**
* Called by client when target navigates to a new document.
* Adds load listeners to document.
*/
newDocument: function () {
// delete previous document's actors
this._clearStyleSheetActors();
// Note: listening for load won't be necessary once
// https://bugzilla.mozilla.org/show_bug.cgi?id=839103 is fixed
if (this.document.readyState == "complete") {
this._onDocumentLoaded();
} else {
this.window.addEventListener("load", this._onDocumentLoaded);
}
return {};
},
/**
* Event handler for document loaded event. Add actor for each stylesheet
* and send an event notifying of the load
*/
_onDocumentLoaded: function (event) {
if (event) {
this.window.removeEventListener("load", this._onDocumentLoaded);
}
let documents = [this.document];
let forms = [];
for (let doc of documents) {
let sheetForms = this._addStyleSheets(doc.styleSheets);
forms = forms.concat(sheetForms);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
documents.push(iframe.contentDocument);
}
}
this.emit("document-load", forms);
},
/**
* Add all the stylesheets to the map and create an actor for each one
* if not already created. Send event that there are new stylesheets.
*
* @param {[DOMStyleSheet]} styleSheets
* Stylesheets to add
* @return {[object]}
* Array of actors for each StyleSheetActor created
*/
_addStyleSheets: function (styleSheets) {
let sheets = [];
for (let i = 0; i < styleSheets.length; i++) {
let styleSheet = styleSheets[i];
sheets.push(styleSheet);
// Get all sheets, including imported ones
let imports = this._getImported(styleSheet);
sheets = sheets.concat(imports);
}
let actors = sheets.map(this._createStyleSheetActor.bind(this));
return actors;
},
/**
* Create a new actor for a style sheet, if it hasn't already been created.
*
* @param {DOMStyleSheet} styleSheet
* The style sheet to create an actor for.
* @return {StyleSheetActor}
* The actor for this style sheet
*/
_createStyleSheetActor: function (styleSheet) {
if (this._sheets.has(styleSheet)) {
return this._sheets.get(styleSheet);
}
let actor = new OldStyleSheetActor(styleSheet, this);
this.manage(actor);
this._sheets.set(styleSheet, actor);
return actor;
},
/**
* Get all the stylesheets @imported from a stylesheet.
*
* @param {DOMStyleSheet} styleSheet
* Style sheet to search
* @return {array}
* All the imported stylesheets
*/
_getImported: function (styleSheet) {
let imported = [];
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let rule = styleSheet.cssRules[i];
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due to
// duplicate @imports for the same URL.
if (!rule.styleSheet) {
continue;
}
imported.push(rule.styleSheet);
// recurse imports in this stylesheet as well
imported = imported.concat(this._getImported(rule.styleSheet));
} else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
// @import rules must precede all others except @charset
break;
}
}
return imported;
},
/**
* Clear all the current stylesheet actors in map.
*/
_clearStyleSheetActors: function () {
for (let actor in this._sheets) {
this.unmanage(this._sheets[actor]);
}
this._sheets.clear();
},
/**
* Create a new style sheet in the document with the given text.
* Return an actor for it.
*
* @param {object} request
* Debugging protocol request object, with 'text property'
* @return {object}
* Object with 'styelSheet' property for form on new actor.
*/
newStyleSheet: function (text) {
let parent = this.document.documentElement;
let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.setAttribute("type", "text/css");
if (text) {
style.appendChild(this.document.createTextNode(text));
}
parent.appendChild(style);
let actor = this._createStyleSheetActor(style.sheet);
return actor;
}
});
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
exports.StyleEditorActor = StyleEditorActor;

Просмотреть файл

@ -3419,7 +3419,6 @@ exports.CSS_PROPERTIES = {
"geometricprecision",
"grab",
"grabbing",
"grayscale",
"grid",
"groove",
"groupbox",
@ -6699,16 +6698,13 @@ exports.CSS_PROPERTIES = {
"grid-template-columns",
"grid-auto-flow",
"grid-auto-rows",
"grid-auto-columns",
"grid-row-gap",
"grid-column-gap"
"grid-auto-columns"
],
"supports": [
6,
8
],
"values": [
"calc",
"column",
"dense",
"inherit",

Просмотреть файл

@ -1,112 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { SimpleStringFront } = require("devtools/shared/fronts/string");
const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
const {
oldStyleSheetSpec,
styleEditorSpec
} = require("devtools/shared/specs/styleeditor");
const promise = require("promise");
const defer = require("devtools/shared/defer");
/**
* StyleSheetFront is the client-side counterpart to a StyleSheetActor.
*/
const OldStyleSheetFront = FrontClassWithSpec(oldStyleSheetSpec, {
initialize: function (conn, form, ctx, detail) {
Front.prototype.initialize.call(this, conn, form, ctx, detail);
this._onPropertyChange = this._onPropertyChange.bind(this);
this.on("property-change", this._onPropertyChange);
},
destroy: function () {
this.off("property-change", this._onPropertyChange);
Front.prototype.destroy.call(this);
},
_onPropertyChange: function (property, value) {
this._form[property] = value;
},
form: function (form, detail) {
if (detail === "actorid") {
this.actorID = form;
return;
}
this.actorID = form.actor;
this._form = form;
},
getText: function () {
let deferred = defer();
this.once("source-load", (source) => {
let longStr = new SimpleStringFront(source);
deferred.resolve(longStr);
});
this.fetchSource();
return deferred.promise;
},
getOriginalSources: function () {
return promise.resolve([]);
},
get href() {
return this._form.href;
},
get nodeHref() {
return this._form.nodeHref;
},
get disabled() {
return !!this._form.disabled;
},
get title() {
return this._form.title;
},
get isSystem() {
return this._form.system;
},
get styleSheetIndex() {
return this._form.styleSheetIndex;
},
get ruleCount() {
return this._form.ruleCount;
}
});
exports.OldStyleSheetFront = OldStyleSheetFront;
/**
* The corresponding Front object for the StyleEditorActor.
*/
const StyleEditorFront = FrontClassWithSpec(styleEditorSpec, {
initialize: function (client, tabForm) {
Front.prototype.initialize.call(this, client);
this.actorID = tabForm.styleEditorActor;
this.manage(this);
},
getStyleSheets: function () {
let deferred = defer();
this.once("document-load", (styleSheets) => {
deferred.resolve(styleSheets);
});
this.newDocument();
return deferred.promise;
},
addStyleSheet: function (text) {
return this.newStyleSheet(text);
}
});
exports.StyleEditorFront = StyleEditorFront;

Просмотреть файл

@ -37,7 +37,6 @@ DevToolsModules(
'source.js',
'storage.js',
'string.js',
'styleeditor.js',
'styles.js',
'stylesheets.js',
'timeline.js',

Просмотреть файл

@ -1,61 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
const oldStyleSheetSpec = generateActorSpec({
typeName: "old-stylesheet",
events: {
"property-change": {
type: "propertyChange",
property: Arg(0, "string"),
value: Arg(1, "json")
},
"source-load": {
type: "sourceLoad",
source: Arg(0, "string")
},
"style-applied": {
type: "styleApplied"
}
},
methods: {
toggleDisabled: {
response: { disabled: RetVal("boolean")}
},
fetchSource: {},
update: {
request: {
text: Arg(0, "string"),
transition: Arg(1, "boolean")
}
}
}
});
exports.oldStyleSheetSpec = oldStyleSheetSpec;
const styleEditorSpec = generateActorSpec({
typeName: "styleeditor",
events: {
"document-load": {
type: "documentLoad",
styleSheets: Arg(0, "array:old-stylesheet")
}
},
method: {
newDocument: {},
newStyleSheet: {
request: { text: Arg(0, "string") },
response: { styleSheet: RetVal("old-stylesheet") }
}
}
});
exports.styleEditorSpec = styleEditorSpec;

Просмотреть файл

@ -13678,7 +13678,8 @@ nsDocShell::EnsureFind()
// default to our window
nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
nsFocusManager::GetFocusedDescendant(ourWindow, true,
nsFocusManager::GetFocusedDescendant(ourWindow,
nsFocusManager::eIncludeAllDescendants,
getter_AddRefs(windowToSearch));
nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
@ -13993,7 +13994,8 @@ nsDocShell::GetControllerForCommand(const char* aCommand,
nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
return root->GetControllerForCommand(aCommand, aResult);
return root->GetControllerForCommand(aCommand, false /* for any window */,
aResult);
}
NS_IMETHODIMP

Просмотреть файл

@ -2158,7 +2158,9 @@ Element::ShouldBlur(nsIContent *aContent)
nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
nsIContent* contentToBlur =
nsFocusManager::GetFocusedDescendant(window, false, getter_AddRefs(focusedFrame));
nsFocusManager::GetFocusedDescendant(window,
nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedFrame));
if (contentToBlur == aContent)
return true;

Просмотреть файл

@ -3832,7 +3832,9 @@ Selection::NotifySelectionListeners()
nsFocusManager* fm = nsFocusManager::GetFocusManager();
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent =
fm->GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow));
nsFocusManager::GetFocusedDescendant(window,
nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
nsCOMPtr<Element> focusedElement = do_QueryInterface(focusedContent);
// When all selected ranges are in an editing host, it should take focus.
// But otherwise, we shouldn't move focus since Chromium doesn't move

Просмотреть файл

@ -3604,7 +3604,8 @@ nsIDocument::GetActiveElement()
if (nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow()) {
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent =
nsFocusManager::GetFocusedDescendant(window, false,
nsFocusManager::GetFocusedDescendant(window,
nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
// be safe and make sure the element is from this document
if (focusedContent && focusedContent->OwnerDoc() == this) {

Просмотреть файл

@ -299,7 +299,8 @@ GetCurrentWindow(nsIContent* aContent)
// static
nsIContent*
nsFocusManager::GetFocusedDescendant(nsPIDOMWindowOuter* aWindow, bool aDeep,
nsFocusManager::GetFocusedDescendant(nsPIDOMWindowOuter* aWindow,
SearchRange aSearchRange,
nsPIDOMWindowOuter** aFocusedWindow)
{
NS_ENSURE_TRUE(aWindow, nullptr);
@ -308,13 +309,34 @@ nsFocusManager::GetFocusedDescendant(nsPIDOMWindowOuter* aWindow, bool aDeep,
nsIContent* currentContent = nullptr;
nsPIDOMWindowOuter* window = aWindow;
while (window) {
for (;;) {
*aFocusedWindow = window;
currentContent = window->GetFocusedNode();
if (!currentContent || !aDeep)
if (!currentContent || aSearchRange == eOnlyCurrentWindow) {
break;
}
window = GetContentWindow(currentContent);
if (!window) {
break;
}
if (aSearchRange == eIncludeAllDescendants) {
continue;
}
MOZ_ASSERT(aSearchRange == eIncludeVisibleDescendants);
// If the child window doesn't have PresShell, it means the window is
// invisible.
nsIDocShell* docShell = window->GetDocShell();
if (!docShell) {
break;
}
nsIPresShell* presShell = docShell->GetPresShell();
if (!presShell) {
break;
}
}
NS_IF_ADDREF(*aFocusedWindow);
@ -628,7 +650,10 @@ nsFocusManager::GetFocusedElementForWindow(mozIDOMWindowProxy* aWindow,
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsCOMPtr<nsIContent> focusedContent =
GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow));
GetFocusedDescendant(window,
aDeep ? nsFocusManager::eIncludeAllDescendants :
nsFocusManager::eOnlyCurrentWindow,
getter_AddRefs(focusedWindow));
if (focusedContent)
CallQueryInterface(focusedContent, aElement);
@ -738,7 +763,8 @@ nsFocusManager::WindowRaised(mozIDOMWindowProxy* aWindow)
// retrieve the last focused element within the window that was raised
nsCOMPtr<nsPIDOMWindowOuter> currentWindow;
nsCOMPtr<nsIContent> currentFocus =
GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
GetFocusedDescendant(window, eIncludeAllDescendants,
getter_AddRefs(currentWindow));
NS_ASSERTION(currentWindow, "window raised with no window current");
if (!currentWindow)
@ -910,7 +936,8 @@ nsFocusManager::WindowShown(mozIDOMWindowProxy* aWindow, bool aNeedsFocus)
if (aNeedsFocus) {
nsCOMPtr<nsPIDOMWindowOuter> currentWindow;
nsCOMPtr<nsIContent> currentFocus =
GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
GetFocusedDescendant(window, eIncludeAllDescendants,
getter_AddRefs(currentWindow));
if (currentWindow)
Focus(currentWindow, currentFocus, 0, true, false, false, true);
}
@ -1213,7 +1240,8 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
nsCOMPtr<nsPIDOMWindowOuter> newWindow;
nsCOMPtr<nsPIDOMWindowOuter> subWindow = GetContentWindow(contentToFocus);
if (subWindow) {
contentToFocus = GetFocusedDescendant(subWindow, true, getter_AddRefs(newWindow));
contentToFocus = GetFocusedDescendant(subWindow, eIncludeAllDescendants,
getter_AddRefs(newWindow));
// since a window is being refocused, clear aFocusChanged so that the
// caret position isn't updated.
aFocusChanged = false;
@ -2290,7 +2318,8 @@ nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow)
// But on other platforms, we can just focus the toplevel widget to raise
// the window.
nsCOMPtr<nsPIDOMWindowOuter> childWindow;
GetFocusedDescendant(aWindow, true, getter_AddRefs(childWindow));
GetFocusedDescendant(aWindow, eIncludeAllDescendants,
getter_AddRefs(childWindow));
if (!childWindow)
childWindow = aWindow;
@ -2654,7 +2683,8 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindowOuter* aWindow,
// When moving between documents, make sure to get the right
// starting content in a descendant.
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
startContent = GetFocusedDescendant(aWindow, eIncludeAllDescendants,
getter_AddRefs(focusedWindow));
}
else if (aType != MOVEFOCUS_LASTDOC) {
// Otherwise, start at the focused node. If MOVEFOCUS_LASTDOC is used,

Просмотреть файл

@ -134,7 +134,17 @@ public:
*
* aWindow and aFocusedWindow must both be non-null.
*/
static nsIContent* GetFocusedDescendant(nsPIDOMWindowOuter* aWindow, bool aDeep,
enum SearchRange
{
// Return focused content in aWindow. So, aFocusedWindow is always aWindow.
eOnlyCurrentWindow,
// Return focused content in aWindow or one of all sub windows.
eIncludeAllDescendants,
// Return focused content in aWindow or one of visible sub windows.
eIncludeVisibleDescendants,
};
static nsIContent* GetFocusedDescendant(nsPIDOMWindowOuter* aWindow,
SearchRange aSearchRange,
nsPIDOMWindowOuter** aFocusedWindow);
/**

Просмотреть файл

@ -36,9 +36,20 @@ public:
virtual nsIDOMNode* GetPopupNode() = 0;
virtual void SetPopupNode(nsIDOMNode* aNode) = 0;
/**
* @param aForVisibleWindow true if caller needs controller which is
* associated with visible window.
*/
virtual nsresult GetControllerForCommand(const char *aCommand,
bool aForVisibleWindow,
nsIController** aResult) = 0;
virtual nsresult GetControllers(nsIControllers** aResult) = 0;
/**
* @param aForVisibleWindow true if caller needs controllers which are
* associated with visible window.
*/
virtual nsresult GetControllers(bool aForVisibleWindow,
nsIControllers** aResult) = 0;
virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
nsTArray<nsCString>& aDisabledCommands) = 0;

Просмотреть файл

@ -207,7 +207,8 @@ nsWindowRoot::GetWindow()
}
nsresult
nsWindowRoot::GetControllers(nsIControllers** aResult)
nsWindowRoot::GetControllers(bool aForVisibleWindow,
nsIControllers** aResult)
{
*aResult = nullptr;
@ -215,9 +216,13 @@ nsWindowRoot::GetControllers(nsIControllers** aResult)
// describes controllers, so this code would have no special
// knowledge of what object might have controllers.
nsFocusManager::SearchRange searchRange =
aForVisibleWindow ? nsFocusManager::eIncludeVisibleDescendants :
nsFocusManager::eIncludeAllDescendants;
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsIContent* focusedContent =
nsFocusManager::GetFocusedDescendant(mWindow, true, getter_AddRefs(focusedWindow));
nsFocusManager::GetFocusedDescendant(mWindow, searchRange,
getter_AddRefs(focusedWindow));
if (focusedContent) {
#ifdef MOZ_XUL
RefPtr<nsXULElement> xulElement = nsXULElement::FromContent(focusedContent);
@ -250,7 +255,8 @@ nsWindowRoot::GetControllers(nsIControllers** aResult)
}
nsresult
nsWindowRoot::GetControllerForCommand(const char * aCommand,
nsWindowRoot::GetControllerForCommand(const char* aCommand,
bool aForVisibleWindow,
nsIController** _retval)
{
NS_ENSURE_ARG_POINTER(_retval);
@ -258,7 +264,7 @@ nsWindowRoot::GetControllerForCommand(const char * aCommand,
{
nsCOMPtr<nsIControllers> controllers;
GetControllers(getter_AddRefs(controllers));
GetControllers(aForVisibleWindow, getter_AddRefs(controllers));
if (controllers) {
nsCOMPtr<nsIController> controller;
controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
@ -269,8 +275,12 @@ nsWindowRoot::GetControllerForCommand(const char * aCommand,
}
}
nsFocusManager::SearchRange searchRange =
aForVisibleWindow ? nsFocusManager::eIncludeVisibleDescendants :
nsFocusManager::eIncludeAllDescendants;
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsFocusManager::GetFocusedDescendant(mWindow, true, getter_AddRefs(focusedWindow));
nsFocusManager::GetFocusedDescendant(mWindow, searchRange,
getter_AddRefs(focusedWindow));
while (focusedWindow) {
nsCOMPtr<nsIControllers> controllers;
focusedWindow->GetControllers(getter_AddRefs(controllers));
@ -340,14 +350,16 @@ nsWindowRoot::GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
nsTHashtable<nsCharPtrHashKey> commandsHandled;
nsCOMPtr<nsIControllers> controllers;
GetControllers(getter_AddRefs(controllers));
GetControllers(false, getter_AddRefs(controllers));
if (controllers) {
GetEnabledDisabledCommandsForControllers(controllers, commandsHandled,
aEnabledCommands, aDisabledCommands);
}
nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
nsFocusManager::GetFocusedDescendant(mWindow, true, getter_AddRefs(focusedWindow));
nsFocusManager::GetFocusedDescendant(mWindow,
nsFocusManager::eIncludeAllDescendants,
getter_AddRefs(focusedWindow));
while (focusedWindow) {
focusedWindow->GetControllers(getter_AddRefs(controllers));
if (controllers) {

Просмотреть файл

@ -43,8 +43,10 @@ public:
virtual nsPIDOMWindowOuter* GetWindow() override;
virtual nsresult GetControllers(nsIControllers** aResult) override;
virtual nsresult GetControllers(bool aForVisibleWindow,
nsIControllers** aResult) override;
virtual nsresult GetControllerForCommand(const char * aCommand,
bool aForVisibleWindow,
nsIController** _retval) override;
virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,

28
dom/cache/DBSchema.cpp поставляемый
Просмотреть файл

@ -2084,7 +2084,17 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
rv = state->GetIsNull(6, &nullPadding);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
#ifdef NIGHTLY_BUILD
bool shouldUpdateTo26 = false;
if (nullPadding && aSavedResponseOut->mValue.type() == ResponseType::Opaque) {
// XXXtt: This should be removed in the future (e.g. Nightly 58) by
// bug 1398167.
shouldUpdateTo26 = true;
aSavedResponseOut->mValue.paddingSize() = 0;
} else if (nullPadding) {
#else
if (nullPadding) {
#endif // NIGHTLY_BUILD
MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut->mValue.type() !=
ResponseType::Opaque);
aSavedResponseOut->mValue.paddingSize() =
@ -2103,6 +2113,20 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
rv = state->GetBlobAsUTF8String(7, aSavedResponseOut->mValue.channelInfo().securityInfo());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
#ifdef NIGHTLY_BUILD
if (shouldUpdateTo26) {
// XXXtt: This is a quick fix for not updating properly in Nightly 57.
// Note: This should be removed in the future (e.g. Nightly 58) by
// bug 1398167.
rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"UPDATE entries SET response_padding_size = 0 "
"WHERE response_type = 4 " // opaque response
"AND response_padding_size IS NULL"
));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
#endif // NIGHTLY_BUILD
rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT "
"name, "
@ -3161,8 +3185,8 @@ nsresult MigrateFrom25To26(mozIStorageConnection* aConn, bool& aRewriteSchema)
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"UPDATE entries SET response_padding_size = 0"
"WHERE response_type = 4" // opaque response
"UPDATE entries SET response_padding_size = 0 "
"WHERE response_type = 4" // opaque response
));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }

Двоичные данные
dom/cache/test/xpcshell/schema_25_profile.zip поставляемый Normal file

Двоичный файл не отображается.

21
dom/cache/test/xpcshell/test_schema_26_upgrade.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,21 @@
/**
* The schema_25_profile.zip are made from local Nightly by following step
* 1. Go to any website
* 2. Open web console and type
* caches.open("test")
* .then(c => fetch("https://www.mozilla.org", {mode:"no-cors"})
* .then(r => c.put("https://www.mozilla.org", r)));
* 3. Go to profile directory and rename the website folder to "chrome"
*/
async function run_test() {
do_test_pending();
create_test_profile('schema_25_profile.zip');
let cache = await caches.open("test");
let response = await cache.match("https://www.mozilla.org");
ok(!!response, "Upgrade from 25 to 26 do succeed");
ok(response.type === 'opaque', "The response type does be opaque");
do_test_finished();
}

2
dom/cache/test/xpcshell/xpcshell.ini поставляемый
Просмотреть файл

@ -6,9 +6,11 @@
head = head.js
support-files =
schema_15_profile.zip
schema_25_profile.zip
# dummy test entry to generate profile zip files
[make_profile.js]
skip-if = true
[test_migration.js]
[test_schema_26_upgrade.js]

Просмотреть файл

@ -99,7 +99,6 @@ skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests
skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
[test_fuzzing_bugs.html]
[test_video_fastpath_mp4.html]
fail-if = (os == 'win' && os_version == '6.1' && !e10s)
[test_video_fastpath_theora.html]
[test_video_fastpath_vp8.html]
[test_video_fastpath_vp9.html]

Просмотреть файл

@ -256,5 +256,6 @@ nsCommandManager::GetControllerForCommand(const char* aCommand,
NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
// no target window; send command to focus controller
return root->GetControllerForCommand(aCommand, aResult);
return root->GetControllerForCommand(aCommand, false /* for any window */,
aResult);
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше