Merge fx-team to m-c
7
CLOBBER
@ -18,4 +18,9 @@
|
||||
# Modifying this file will now automatically clobber the buildbot machines \o/
|
||||
#
|
||||
|
||||
Bug 946047 - Windows needs a clobber for webidl changes.
|
||||
Bug 942231 needs a clobber -- JNI wrappers need to be re-generated.
|
||||
and
|
||||
Bug 934646 needs a clobber -- the icon resources previously copied
|
||||
into $OBJDIR/mobile/android/base/res will conflict with those in
|
||||
$BRANDING_DIRECTORY/res.
|
||||
|
||||
|
@ -102,9 +102,9 @@ var StarUI = {
|
||||
this.cancelButtonOnCommand();
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
if (aEvent.target.className == "expander-up" ||
|
||||
aEvent.target.className == "expander-down" ||
|
||||
aEvent.target.id == "editBMPanel_newFolderButton") {
|
||||
if (aEvent.target.classList.contains("expander-up") ||
|
||||
aEvent.target.classList.contains("expander-down") ||
|
||||
aEvent.target.id == "editBMPanel_newFolderButton") {
|
||||
//XXX Why is this necessary? The defaultPrevented check should
|
||||
// be enough.
|
||||
break;
|
||||
|
@ -2936,27 +2936,29 @@ const BrowserSearch = {
|
||||
|
||||
let searchBar = this.searchBar;
|
||||
let placement = CustomizableUI.getPlacementOfWidget("search-container");
|
||||
let focusSearchBar = () => {
|
||||
searchBar = this.searchBar;
|
||||
searchBar.select();
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
};
|
||||
if (placement && placement.area == CustomizableUI.AREA_PANEL) {
|
||||
PanelUI.show().then(() => {
|
||||
// The panel is not constructed until the first time it is shown.
|
||||
searchBar = this.searchBar;
|
||||
searchBar.select();
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
});
|
||||
return;
|
||||
} else if (placement.area == CustomizableUI.AREA_NAVBAR && searchBar &&
|
||||
searchBar.parentNode.classList.contains("overflowedItem")) {
|
||||
let navBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||
navBar.overflowable.show().then(() => {
|
||||
// The searchBar gets moved when the overflow panel opens.
|
||||
searchBar = this.searchBar;
|
||||
searchBar.select();
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
focusSearchBar();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (searchBar && window.fullScreen) {
|
||||
FullScreen.mouseoverToggle(true);
|
||||
if (placement.area == CustomizableUI.AREA_NAVBAR && searchBar &&
|
||||
searchBar.parentNode.classList.contains("overflowedItem")) {
|
||||
let navBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||
navBar.overflowable.show().then(() => {
|
||||
focusSearchBar();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (searchBar) {
|
||||
if (window.fullScreen)
|
||||
FullScreen.mouseoverToggle(true);
|
||||
searchBar.select();
|
||||
}
|
||||
openSearchPageIfFieldIsNotActive(searchBar);
|
||||
|
@ -17,3 +17,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/aurora/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
browser/branding/aurora/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
14
browser/branding/aurora/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #331e54;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #181327;
|
||||
}
|
||||
|
@ -17,3 +17,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/nightly/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
browser/branding/nightly/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
14
browser/branding/nightly/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #002147;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #0a111c;
|
||||
}
|
||||
|
@ -16,3 +16,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/official/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
browser/branding/official/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
14
browser/branding/official/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #0095dd;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #036fa4;
|
||||
}
|
||||
|
@ -17,3 +17,8 @@ browser.jar:
|
||||
content/branding/identity-icons-brand.png (identity-icons-brand.png)
|
||||
content/branding/identity-icons-brand@2x.png (identity-icons-brand@2x.png)
|
||||
content/branding/aboutDialog.css (aboutDialog.css)
|
||||
#ifdef MOZ_METRO
|
||||
content/branding/metro-about.css (metro-about.css)
|
||||
content/branding/metro-about-footer.png (metro-about-footer.png)
|
||||
content/branding/metro-about-wordmark.png (metro-about-wordmark.png)
|
||||
#endif
|
||||
|
BIN
browser/branding/unofficial/content/metro-about-footer.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
browser/branding/unofficial/content/metro-about-wordmark.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
14
browser/branding/unofficial/content/metro-about.css
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-color: #002147;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#about-policy-label:hover,
|
||||
#about-policy-label:active {
|
||||
background: #0a111c;
|
||||
}
|
||||
|
@ -85,6 +85,18 @@ let gTests = [
|
||||
ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Ctrl+K should focus the search bar if it is in the navbar and not overflowing.",
|
||||
run: function() {
|
||||
let searchbar = document.getElementById("searchbar");
|
||||
let placement = CustomizableUI.getPlacementOfWidget("search-container");
|
||||
is(placement.area, CustomizableUI.AREA_NAVBAR, "Should be in nav-bar");
|
||||
|
||||
sendWebSearchKeyCommand();
|
||||
logActiveElement();
|
||||
is(document.activeElement, searchbar.textbox.inputField, "The searchbar should be focused");
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function test() {
|
||||
|
@ -275,7 +275,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/connection.xul",
|
||||
"mozilla:connectionmanager",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
null);
|
||||
},
|
||||
|
||||
@ -441,7 +441,7 @@ var gAdvancedPane = {
|
||||
introText : bundlePreferences.getString("offlinepermissionstext") };
|
||||
openDialog("chrome://browser/content/preferences/permissions.xul",
|
||||
"Browser:Permissions",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
params);
|
||||
},
|
||||
|
||||
@ -814,7 +814,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://pippki/content/certManager.xul",
|
||||
"mozilla:certmanager",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -824,7 +824,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://mozapps/content/preferences/ocsp.xul",
|
||||
"mozilla:crlmanager",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -834,7 +834,7 @@ var gAdvancedPane = {
|
||||
{
|
||||
openDialog("chrome://pippki/content/device_manager.xul",
|
||||
"mozilla:devicemanager",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
}
|
||||
#ifdef HAVE_SHELL_SERVICE
|
||||
,
|
||||
|
@ -471,7 +471,7 @@ var gPrivacyPane = {
|
||||
introText : bundlePreferences.getString("cookiepermissionstext") };
|
||||
openDialog("chrome://browser/content/preferences/permissions.xul",
|
||||
"Browser:Permissions",
|
||||
"model=yes", params);
|
||||
"modal=yes", params);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -481,7 +481,7 @@ var gPrivacyPane = {
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/cookies.xul",
|
||||
"Browser:Cookies",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
// CLEAR PRIVATE DATA
|
||||
@ -500,7 +500,7 @@ var gPrivacyPane = {
|
||||
showClearPrivateDataSettings: function ()
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/sanitize.xul",
|
||||
"model=yes", null);
|
||||
"modal=yes", null);
|
||||
},
|
||||
|
||||
|
||||
|
@ -57,7 +57,7 @@ var gSecurityPane = {
|
||||
|
||||
openDialog("chrome://browser/content/preferences/permissions.xul",
|
||||
"Browser:Permissions",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
params);
|
||||
},
|
||||
|
||||
@ -111,7 +111,7 @@ var gSecurityPane = {
|
||||
{
|
||||
openDialog("chrome://passwordmgr/content/passwordManagerExceptions.xul",
|
||||
"Toolkit:PasswordManagerExceptions",
|
||||
"model=yes",
|
||||
"modal=yes",
|
||||
null);
|
||||
},
|
||||
|
||||
|
@ -86,10 +86,6 @@ const BROWSER_EVENTS = [
|
||||
// The number of milliseconds in a day
|
||||
const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0;
|
||||
|
||||
#ifndef XP_WIN
|
||||
#define BROKEN_WM_Z_ORDER
|
||||
#endif
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
|
||||
@ -104,14 +100,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gScreenManager",
|
||||
"@mozilla.org/gfx/screenmanager;1", "nsIScreenManager");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
|
||||
"resource:///modules/devtools/scratchpad-manager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
|
||||
"resource:///modules/sessionstore/DocShellCapabilities.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Messenger",
|
||||
"resource:///modules/sessionstore/Messenger.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageStyle",
|
||||
"resource:///modules/sessionstore/PageStyle.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
|
||||
"resource:///modules/devtools/scratchpad-manager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
|
||||
"resource:///modules/sessionstore/SessionSaver.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
|
||||
@ -2478,8 +2476,24 @@ let SessionStoreInternal = {
|
||||
this._windows[aWindow.__SSi].extData[key] = winData.extData[key];
|
||||
}
|
||||
}
|
||||
|
||||
let newClosedTabsData = winData._closedTabs || [];
|
||||
|
||||
if (overwriteTabs || firstWindow) {
|
||||
this._windows[aWindow.__SSi]._closedTabs = winData._closedTabs || [];
|
||||
// Overwrite existing closed tabs data when overwriteTabs=true
|
||||
// or we're the first window to be restored.
|
||||
this._windows[aWindow.__SSi]._closedTabs = newClosedTabsData;
|
||||
} else if (this._max_tabs_undo > 0) {
|
||||
// If we merge tabs, we also want to merge closed tabs data. We'll assume
|
||||
// the restored tabs were closed more recently and append the current list
|
||||
// of closed tabs to the new one...
|
||||
newClosedTabsData =
|
||||
newClosedTabsData.concat(this._windows[aWindow.__SSi]._closedTabs);
|
||||
|
||||
// ... and make sure that we don't exceed the max number of closed tabs
|
||||
// we can restore.
|
||||
this._windows[aWindow.__SSi]._closedTabs =
|
||||
newClosedTabsData.slice(0, this._max_tabs_undo);
|
||||
}
|
||||
|
||||
this.restoreTabs(aWindow, tabs, winData.tabs,
|
||||
@ -3199,32 +3213,7 @@ let SessionStoreInternal = {
|
||||
* @returns Window reference
|
||||
*/
|
||||
_getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() {
|
||||
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win)
|
||||
return null;
|
||||
if (!win.closed)
|
||||
return win;
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
win = null;
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
// this is oldest to newest, so this gets a bit ugly
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
let nextWin = windowsEnum.getNext();
|
||||
if (!nextWin.closed)
|
||||
win = nextWin;
|
||||
}
|
||||
return win;
|
||||
#else
|
||||
var windowsEnum =
|
||||
Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
win = windowsEnum.getNext();
|
||||
if (!win.closed)
|
||||
return win;
|
||||
}
|
||||
return null;
|
||||
#endif
|
||||
return RecentWindow.getMostRecentBrowserWindow({ allowPopups: true });
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -56,6 +56,7 @@ support-files =
|
||||
[browser_formdata_format.js]
|
||||
[browser_global_store.js]
|
||||
[browser_input.js]
|
||||
[browser_merge_closed_tabs.js]
|
||||
[browser_pageshow.js]
|
||||
[browser_pageStyle.js]
|
||||
[browser_sessionStorage.js]
|
||||
|
@ -1,53 +1,38 @@
|
||||
/* 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/. */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 597071 **/
|
||||
/**
|
||||
* Bug 597071 - Closed windows should only be resurrected when there is a single
|
||||
* popup window
|
||||
*/
|
||||
add_task(function test_close_last_nonpopup_window() {
|
||||
// Purge the list of closed windows.
|
||||
while (ss.getClosedWindowCount()) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
let oldState = ss.getWindowState(window);
|
||||
|
||||
// set the pref to 1 greater than it currently is so we have room for an extra
|
||||
// closed window
|
||||
let closedWindowCount = ss.getClosedWindowCount();
|
||||
Services.prefs.setIntPref("browser.sessionstore.max_windows_undo",
|
||||
closedWindowCount + 1);
|
||||
let popupState = {windows: [
|
||||
{tabs: [{entries: []}], isPopup: true, hidden: "toolbar"}
|
||||
]};
|
||||
|
||||
let currentState = ss.getBrowserState();
|
||||
let popupState = { windows:[
|
||||
{ tabs:[ {entries:[] }], isPopup: true, hidden: "toolbar" }
|
||||
] };
|
||||
|
||||
// set this window to be a popup.
|
||||
// Set this window to be a popup.
|
||||
ss.setWindowState(window, JSON.stringify(popupState), true);
|
||||
|
||||
// open a new non-popup window
|
||||
let newWin = openDialog(location, "", "chrome,all,dialog=no", "http://example.com");
|
||||
newWin.addEventListener("load", function(aEvent) {
|
||||
newWin.removeEventListener("load", arguments.callee, false);
|
||||
// Open a new window with a tab.
|
||||
let win = yield promiseNewWindowLoaded({private: false});
|
||||
let tab = win.gBrowser.addTab("http://example.com/");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
newWin.gBrowser.addEventListener("load", function(aEvent) {
|
||||
newWin.gBrowser.removeEventListener("load", arguments.callee, true);
|
||||
// Make sure sessionstore sees this window.
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows.length, 2, "sessionstore knows about this window");
|
||||
|
||||
newWin.gBrowser.addTab().linkedBrowser.stop();
|
||||
|
||||
// make sure sessionstore sees this window
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows.length, 2, "sessionstore knows about this window");
|
||||
|
||||
newWin.close();
|
||||
newWin.addEventListener("unload", function(aEvent) {
|
||||
newWin.removeEventListener("unload", arguments.callee, false);
|
||||
|
||||
is(ss.getClosedWindowCount(), closedWindowCount + 1,
|
||||
"increased closed window count");
|
||||
|
||||
Services.prefs.clearUserPref("browser.sessionstore.max_windows_undo");
|
||||
ss.setBrowserState(currentState);
|
||||
executeSoon(finish);
|
||||
|
||||
}, false);
|
||||
}, true);
|
||||
}, false);
|
||||
}
|
||||
// Closed the window and check the closed window count.
|
||||
yield promiseWindowClosed(win);
|
||||
is(ss.getClosedWindowCount(), 1, "correct closed window count");
|
||||
|
||||
// Cleanup.
|
||||
ss.setWindowState(window, oldState, true);
|
||||
});
|
||||
|
@ -0,0 +1,71 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This test ensures that closed tabs are merged when restoring
|
||||
* a window state without overwriting tabs.
|
||||
*/
|
||||
add_task(function () {
|
||||
const initialState = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:blank" }] }
|
||||
],
|
||||
_closedTabs: [
|
||||
{ state: { entries: [{ ID: 1000, url: "about:blank" }]} },
|
||||
{ state: { entries: [{ ID: 1001, url: "about:blank" }]} }
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
const restoreState = {
|
||||
windows: [{
|
||||
tabs: [
|
||||
{ entries: [{ url: "about:robots" }] }
|
||||
],
|
||||
_closedTabs: [
|
||||
{ state: { entries: [{ ID: 1002, url: "about:robots" }]} },
|
||||
{ state: { entries: [{ ID: 1003, url: "about:robots" }]} },
|
||||
{ state: { entries: [{ ID: 1004, url: "about:robots" }]} }
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
const maxTabsUndo = 4;
|
||||
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo", maxTabsUndo);
|
||||
|
||||
// Open a new window and restore it to an initial state.
|
||||
let win = yield promiseNewWindowLoaded({private: false});
|
||||
SessionStore.setWindowState(win, JSON.stringify(initialState), true);
|
||||
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);
|
||||
|
||||
// Verify the windows closed tab data is correct.
|
||||
let iClosed = initialState.windows[0]._closedTabs;
|
||||
let rClosed = restoreState.windows[0]._closedTabs;
|
||||
let cData = JSON.parse(SessionStore.getClosedTabData(win));
|
||||
|
||||
is(cData.length, Math.min(iClosed.length + rClosed.length, maxTabsUndo),
|
||||
"Number of closed tabs is correct");
|
||||
|
||||
// When the closed tabs are merged the restored tabs are considered to be
|
||||
// closed more recently.
|
||||
for (let i = 0; i < cData.length; i++) {
|
||||
if (i < rClosed.length) {
|
||||
is(cData[i].state.entries[0].ID, rClosed[i].state.entries[0].ID,
|
||||
"Closed tab entry matches");
|
||||
} else {
|
||||
is(cData[i].state.entries[0].ID, iClosed[i - rClosed.length].state.entries[0].ID,
|
||||
"Closed tab entry matches");
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
win.close();
|
||||
});
|
||||
|
||||
|
@ -412,6 +412,29 @@ function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
whenDelayedStartupFinished(win, () => aCallback(win));
|
||||
return win;
|
||||
}
|
||||
function promiseNewWindowLoaded(aOptions) {
|
||||
let deferred = Promise.defer();
|
||||
whenNewWindowLoaded(aOptions, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chrome windows aren't closed synchronously. Provide a helper method to close
|
||||
* a window and wait until we received the "domwindowclosed" notification for it.
|
||||
*/
|
||||
function promiseWindowClosed(win) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
Services.obs.addObserver(function obs(subject, topic) {
|
||||
if (subject == win) {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, "domwindowclosed", false);
|
||||
|
||||
win.close();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* This waits for the browser-delayed-startup-finished notification of a given
|
||||
|
@ -1698,7 +1698,13 @@ Breakpoints.prototype = {
|
||||
// By default, new breakpoints are always enabled. Disabled breakpoints
|
||||
// are, in fact, removed from the server but preserved in the frontend,
|
||||
// so that they may not be forgotten across target navigations.
|
||||
this._disabled.delete(identifier);
|
||||
let disabledPromise = this._disabled.get(identifier);
|
||||
if (disabledPromise) {
|
||||
disabledPromise.then(({ conditionalExpression: previousValue }) => {
|
||||
aBreakpointClient.conditionalExpression = previousValue;
|
||||
});
|
||||
this._disabled.delete(identifier);
|
||||
}
|
||||
|
||||
// Preserve information about the breakpoint's line text, to display it
|
||||
// in the sources pane without requiring fetching the source (for example,
|
||||
|
@ -53,3 +53,7 @@
|
||||
#body[layout=horizontal] #vertical-layout-panes-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#body[layout=vertical] #stackframes {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ support-files =
|
||||
doc_random-javascript.html
|
||||
doc_recursion-stack.html
|
||||
doc_scope-variable.html
|
||||
doc_scope-variable-2.html
|
||||
doc_script-switching-01.html
|
||||
doc_script-switching-02.html
|
||||
doc_step-out.html
|
||||
@ -94,6 +95,7 @@ support-files =
|
||||
[browser_dbg_cmd-dbg.js]
|
||||
[browser_dbg_conditional-breakpoints-01.js]
|
||||
[browser_dbg_conditional-breakpoints-02.js]
|
||||
[browser_dbg_conditional-breakpoints-03.js]
|
||||
[browser_dbg_controller-evaluate-01.js]
|
||||
[browser_dbg_controller-evaluate-02.js]
|
||||
[browser_dbg_debugger-statement.js]
|
||||
@ -210,6 +212,8 @@ support-files =
|
||||
[browser_dbg_variables-view-frozen-sealed-nonext.js]
|
||||
[browser_dbg_variables-view-hide-non-enums.js]
|
||||
[browser_dbg_variables-view-large-array-buffer.js]
|
||||
[browser_dbg_variables-view-override-01.js]
|
||||
[browser_dbg_variables-view-override-02.js]
|
||||
[browser_dbg_variables-view-popup-01.js]
|
||||
[browser_dbg_variables-view-popup-02.js]
|
||||
[browser_dbg_variables-view-popup-03.js]
|
||||
|
@ -0,0 +1,82 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that conditional breakpoint expressions survive disabled breakpoints.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html";
|
||||
|
||||
function test() {
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gSources, gBreakpoints, gLocation;
|
||||
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gBreakpoints = gDebugger.DebuggerController.Breakpoints;
|
||||
|
||||
gLocation = { url: gSources.selectedValue, line: 18 };
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 17)
|
||||
.then(addBreakpoint)
|
||||
.then(setConditional)
|
||||
.then(() => {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_REMOVED);
|
||||
toggleBreakpoint();
|
||||
return finished;
|
||||
})
|
||||
.then(() => {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_ADDED);
|
||||
toggleBreakpoint();
|
||||
return finished;
|
||||
})
|
||||
.then(testConditionalExpressionOnClient)
|
||||
.then(() => {
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWING);
|
||||
openConditionalPopup();
|
||||
return finished;
|
||||
})
|
||||
.then(testConditionalExpressionInPopup)
|
||||
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
|
||||
gDebuggee.ermahgerd();
|
||||
});
|
||||
|
||||
function addBreakpoint() {
|
||||
return gPanel.addBreakpoint(gLocation);
|
||||
}
|
||||
|
||||
function setConditional(aClient) {
|
||||
aClient.conditionalExpression = "hello";
|
||||
}
|
||||
|
||||
function toggleBreakpoint() {
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gDebugger.document.querySelector(".dbg-breakpoint-checkbox"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function openConditionalPopup() {
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gDebugger.document.querySelector(".dbg-breakpoint"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function testConditionalExpressionOnClient() {
|
||||
return gBreakpoints._getAdded(gLocation).then(aClient => {
|
||||
is(aClient.conditionalExpression, "hello", "The expression is correct (1).");
|
||||
});
|
||||
}
|
||||
|
||||
function testConditionalExpressionInPopup() {
|
||||
let textbox = gDebugger.document.getElementById("conditional-breakpoint-panel-textbox");
|
||||
is(textbox.value, "hello", "The expression is correct (2).")
|
||||
}
|
||||
}
|
@ -51,14 +51,14 @@ function initialChecks() {
|
||||
is(gVariables.getScopeAtIndex(1).target, scopeNodes[1],
|
||||
"getScopeAtIndex(1) didn't return the expected scope.");
|
||||
|
||||
is(gVariables.getScopeForNode(scopeNodes[0]).target, scopeNodes[0],
|
||||
"getScopeForNode([0]) didn't return the expected scope.");
|
||||
is(gVariables.getScopeForNode(scopeNodes[1]).target, scopeNodes[1],
|
||||
"getScopeForNode([1]) didn't return the expected scope.");
|
||||
is(gVariables.getItemForNode(scopeNodes[0]).target, scopeNodes[0],
|
||||
"getItemForNode([0]) didn't return the expected scope.");
|
||||
is(gVariables.getItemForNode(scopeNodes[1]).target, scopeNodes[1],
|
||||
"getItemForNode([1]) didn't return the expected scope.");
|
||||
|
||||
is(gVariables.getScopeForNode(scopeNodes[0]).expanded, true,
|
||||
is(gVariables.getItemForNode(scopeNodes[0]).expanded, true,
|
||||
"The local scope should be expanded by default.");
|
||||
is(gVariables.getScopeForNode(scopeNodes[1]).expanded, false,
|
||||
is(gVariables.getItemForNode(scopeNodes[1]).expanded, false,
|
||||
"The global scope should not be collapsed by default.");
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,219 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that VariablesView methods responsible for styling variables
|
||||
* as overridden work properly.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_scope-variable-2.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let variables = win.DebuggerView.Variables;
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
|
||||
|
||||
let firstScope = variables.getScopeAtIndex(0);
|
||||
let secondScope = variables.getScopeAtIndex(1);
|
||||
let thirdScope = variables.getScopeAtIndex(2);
|
||||
let globalScope = variables.getScopeAtIndex(3);
|
||||
|
||||
ok(firstScope, "The first scope is available.");
|
||||
ok(secondScope, "The second scope is available.");
|
||||
ok(thirdScope, "The third scope is available.");
|
||||
ok(globalScope, "The global scope is available.");
|
||||
|
||||
is(firstScope.name, "Function scope [secondNest]",
|
||||
"The first scope's name is correct.");
|
||||
is(secondScope.name, "Function scope [firstNest]",
|
||||
"The second scope's name is correct.");
|
||||
is(thirdScope.name, "Function scope [test]",
|
||||
"The third scope's name is correct.");
|
||||
is(globalScope.name, "Global scope [Window]",
|
||||
"The global scope's name is correct.");
|
||||
|
||||
is(firstScope.expanded, true,
|
||||
"The first scope's expansion state is correct.");
|
||||
is(secondScope.expanded, false,
|
||||
"The second scope's expansion state is correct.");
|
||||
is(thirdScope.expanded, false,
|
||||
"The third scope's expansion state is correct.");
|
||||
is(globalScope.expanded, false,
|
||||
"The global scope's expansion state is correct.");
|
||||
|
||||
is(firstScope._store.size, 3,
|
||||
"The first scope should have all the variables available.");
|
||||
is(secondScope._store.size, 3,
|
||||
"The second scope shoild have all the variables available.");
|
||||
is(thirdScope._store.size, 3,
|
||||
"The third scope shoild have all the variables available.");
|
||||
is(globalScope._store.size, 0,
|
||||
"The global scope shoild have no variables available.");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with simple variables.
|
||||
|
||||
let thisVar = firstScope.get("this");
|
||||
let thisOwner = variables.getOwnerScopeForVariableOrProperty(thisVar);
|
||||
is(thisOwner, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (1).");
|
||||
|
||||
let someVar1 = firstScope.get("a");
|
||||
let someOwner1 = variables.getOwnerScopeForVariableOrProperty(someVar1);
|
||||
is(someOwner1, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (2).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with first-degree properties.
|
||||
|
||||
let argsVar1 = firstScope.get("arguments");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
argsVar1.expand();
|
||||
yield fetched;
|
||||
|
||||
let calleeProp1 = argsVar1.get("callee");
|
||||
let calleeOwner1 = variables.getOwnerScopeForVariableOrProperty(calleeProp1);
|
||||
is(calleeOwner1, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (3).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with second-degree properties.
|
||||
|
||||
let protoVar1 = argsVar1.get("__proto__");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
protoVar1.expand();
|
||||
yield fetched;
|
||||
|
||||
let constrProp1 = protoVar1.get("constructor");
|
||||
let constrOwner1 = variables.getOwnerScopeForVariableOrProperty(constrProp1);
|
||||
is(constrOwner1, firstScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (4).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with a simple variable
|
||||
// from non-topmost scopes.
|
||||
|
||||
let someVar2 = secondScope.get("a");
|
||||
let someOwner2 = variables.getOwnerScopeForVariableOrProperty(someVar2);
|
||||
is(someOwner2, secondScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (5).");
|
||||
|
||||
let someVar3 = thirdScope.get("a");
|
||||
let someOwner3 = variables.getOwnerScopeForVariableOrProperty(someVar3);
|
||||
is(someOwner3, thirdScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (6).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with first-degree properies
|
||||
// from non-topmost scopes.
|
||||
|
||||
let argsVar2 = secondScope.get("arguments");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
argsVar2.expand();
|
||||
yield fetched;
|
||||
|
||||
let calleeProp2 = argsVar2.get("callee");
|
||||
let calleeOwner2 = variables.getOwnerScopeForVariableOrProperty(calleeProp2);
|
||||
is(calleeOwner2, secondScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (7).");
|
||||
|
||||
let argsVar3 = thirdScope.get("arguments");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
argsVar3.expand();
|
||||
yield fetched;
|
||||
|
||||
let calleeProp3 = argsVar3.get("callee");
|
||||
let calleeOwner3 = variables.getOwnerScopeForVariableOrProperty(calleeProp3);
|
||||
is(calleeOwner3, thirdScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (8).");
|
||||
|
||||
// Test getOwnerScopeForVariableOrProperty with second-degree properties
|
||||
// from non-topmost scopes.
|
||||
|
||||
let protoVar2 = argsVar2.get("__proto__");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
protoVar2.expand();
|
||||
yield fetched;
|
||||
|
||||
let constrProp2 = protoVar2.get("constructor");
|
||||
let constrOwner2 = variables.getOwnerScopeForVariableOrProperty(constrProp2);
|
||||
is(constrOwner2, secondScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (9).");
|
||||
|
||||
let protoVar3 = argsVar3.get("__proto__");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_PROPERTIES);
|
||||
protoVar3.expand();
|
||||
yield fetched;
|
||||
|
||||
let constrProp3 = protoVar3.get("constructor");
|
||||
let constrOwner3 = variables.getOwnerScopeForVariableOrProperty(constrProp3);
|
||||
is(constrOwner3, thirdScope,
|
||||
"The getOwnerScopeForVariableOrProperty method works properly (10).");
|
||||
|
||||
// Test getParentScopesForVariableOrProperty with simple variables.
|
||||
|
||||
let varOwners1 = variables.getParentScopesForVariableOrProperty(someVar1);
|
||||
let varOwners2 = variables.getParentScopesForVariableOrProperty(someVar2);
|
||||
let varOwners3 = variables.getParentScopesForVariableOrProperty(someVar3);
|
||||
|
||||
is(varOwners1.length, 0,
|
||||
"There should be no owner scopes for the first variable.");
|
||||
|
||||
is(varOwners2.length, 1,
|
||||
"There should be one owner scope for the second variable.");
|
||||
is(varOwners2[0], firstScope,
|
||||
"The only owner scope for the second variable is correct.");
|
||||
|
||||
is(varOwners3.length, 2,
|
||||
"There should be two owner scopes for the third variable.");
|
||||
is(varOwners3[0], firstScope,
|
||||
"The first owner scope for the third variable is correct.");
|
||||
is(varOwners3[1], secondScope,
|
||||
"The second owner scope for the third variable is correct.");
|
||||
|
||||
// Test getParentScopesForVariableOrProperty with first-degree properties.
|
||||
|
||||
let propOwners1 = variables.getParentScopesForVariableOrProperty(calleeProp1);
|
||||
let propOwners2 = variables.getParentScopesForVariableOrProperty(calleeProp2);
|
||||
let propOwners3 = variables.getParentScopesForVariableOrProperty(calleeProp3);
|
||||
|
||||
is(propOwners1.length, 0,
|
||||
"There should be no owner scopes for the first property.");
|
||||
|
||||
is(propOwners2.length, 1,
|
||||
"There should be one owner scope for the second property.");
|
||||
is(propOwners2[0], firstScope,
|
||||
"The only owner scope for the second property is correct.");
|
||||
|
||||
is(propOwners3.length, 2,
|
||||
"There should be two owner scopes for the third property.");
|
||||
is(propOwners3[0], firstScope,
|
||||
"The first owner scope for the third property is correct.");
|
||||
is(propOwners3[1], secondScope,
|
||||
"The second owner scope for the third property is correct.");
|
||||
|
||||
// Test getParentScopesForVariableOrProperty with second-degree properties.
|
||||
|
||||
let secPropOwners1 = variables.getParentScopesForVariableOrProperty(constrProp1);
|
||||
let secPropOwners2 = variables.getParentScopesForVariableOrProperty(constrProp2);
|
||||
let secPropOwners3 = variables.getParentScopesForVariableOrProperty(constrProp3);
|
||||
|
||||
is(secPropOwners1.length, 0,
|
||||
"There should be no owner scopes for the first inner property.");
|
||||
|
||||
is(secPropOwners2.length, 1,
|
||||
"There should be one owner scope for the second inner property.");
|
||||
is(secPropOwners2[0], firstScope,
|
||||
"The only owner scope for the second inner property is correct.");
|
||||
|
||||
is(secPropOwners3.length, 2,
|
||||
"There should be two owner scopes for the third inner property.");
|
||||
is(secPropOwners3[0], firstScope,
|
||||
"The first owner scope for the third inner property is correct.");
|
||||
is(secPropOwners3[1], secondScope,
|
||||
"The second owner scope for the third inner property is correct.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that overridden variables in the VariablesView are styled properly.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_scope-variable-2.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let variables = win.DebuggerView.Variables;
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 23);
|
||||
|
||||
// Wait for the hierarchy to be committed by the VariablesViewController.
|
||||
let committed = promise.defer();
|
||||
variables.oncommit = committed.resolve;
|
||||
yield committed.promise;
|
||||
|
||||
let firstScope = variables.getScopeAtIndex(0);
|
||||
let secondScope = variables.getScopeAtIndex(1);
|
||||
let thirdScope = variables.getScopeAtIndex(2);
|
||||
|
||||
let someVar1 = firstScope.get("a");
|
||||
let someVar2 = secondScope.get("a");
|
||||
let someVar3 = thirdScope.get("a");
|
||||
|
||||
let argsVar1 = firstScope.get("arguments");
|
||||
let argsVar2 = secondScope.get("arguments");
|
||||
let argsVar3 = thirdScope.get("arguments");
|
||||
|
||||
is(someVar1.target.hasAttribute("overridden"), false,
|
||||
"The first 'a' variable should not be marked as being overridden.");
|
||||
is(someVar2.target.hasAttribute("overridden"), true,
|
||||
"The second 'a' variable should be marked as being overridden.");
|
||||
is(someVar3.target.hasAttribute("overridden"), true,
|
||||
"The third 'a' variable should be marked as being overridden.");
|
||||
|
||||
is(argsVar1.target.hasAttribute("overridden"), false,
|
||||
"The first 'arguments' variable should not be marked as being overridden.");
|
||||
is(argsVar2.target.hasAttribute("overridden"), true,
|
||||
"The second 'arguments' variable should be marked as being overridden.");
|
||||
is(argsVar3.target.hasAttribute("overridden"), true,
|
||||
"The third 'arguments' variable should be marked as being overridden.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -23,6 +23,9 @@ function test() {
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gVariables = gDebugger.DebuggerView.Variables;
|
||||
|
||||
// Always expand all items between pauses except 'window' variables.
|
||||
gVariables.commitHierarchyIgnoredItems = Object.create(null, { window: { value: true } });
|
||||
|
||||
waitForSourceShown(gPanel, ".html")
|
||||
.then(addBreakpoint)
|
||||
.then(() => ensureThreadClientState(gPanel, "resumed"))
|
||||
|
@ -24,7 +24,7 @@ function test() {
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gVariables = gDebugger.DebuggerView.Variables;
|
||||
|
||||
// Always expand all scopes between pauses.
|
||||
// Always expand all items between pauses.
|
||||
gVariables.commitHierarchyIgnoredItems = Object.create(null);
|
||||
|
||||
waitForSourceShown(gPanel, ".html")
|
||||
|
30
browser/devtools/debugger/test/doc_scope-variable-2.html
Normal file
@ -0,0 +1,30 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function test() {
|
||||
var a = "first scope";
|
||||
firstNest();
|
||||
|
||||
function firstNest() {
|
||||
var a = "second scope";
|
||||
secondNest();
|
||||
|
||||
function secondNest() {
|
||||
var a = "third scope";
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1173,10 +1173,11 @@ MarkupContainer.prototype = {
|
||||
this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => {
|
||||
if (data) {
|
||||
data.data.string().then(str => {
|
||||
let res = {data: str, size: data.size};
|
||||
// Resolving the data promise and, to always keep tooltipData.data
|
||||
// as a promise, create a new one that resolves immediately
|
||||
def.resolve(str, data.size);
|
||||
this.tooltipData.data = promise.resolve(str, data.size);
|
||||
def.resolve(res);
|
||||
this.tooltipData.data = promise.resolve(res);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -1186,7 +1187,7 @@ MarkupContainer.prototype = {
|
||||
|
||||
_buildTooltipContent: function(target, tooltip) {
|
||||
if (this.tooltipData && target === this.tooltipData.target) {
|
||||
this.tooltipData.data.then((data, size) => {
|
||||
this.tooltipData.data.then(({data, size}) => {
|
||||
tooltip.setImageContent(data, size);
|
||||
});
|
||||
return true;
|
||||
|
@ -16,10 +16,10 @@ const PAGE_CONTENT = [
|
||||
].join("\n");
|
||||
|
||||
const TEST_NODES = [
|
||||
"img.local",
|
||||
"img.data",
|
||||
"img.remote",
|
||||
".canvas"
|
||||
{selector: "img.local", size: "192 x 192"},
|
||||
{selector: "img.data", size: "64 x 64"},
|
||||
{selector: "img.remote", size: "22 x 23"},
|
||||
{selector: ".canvas", size: "600 x 600"}
|
||||
];
|
||||
|
||||
function test() {
|
||||
@ -77,8 +77,8 @@ function testImageTooltip(index) {
|
||||
return endTests();
|
||||
}
|
||||
|
||||
let node = contentDoc.querySelector(TEST_NODES[index]);
|
||||
ok(node, "We have the [" + TEST_NODES[index] + "] image node to test for tooltip");
|
||||
let node = contentDoc.querySelector(TEST_NODES[index].selector);
|
||||
ok(node, "We have the [" + TEST_NODES[index].selector + "] image node to test for tooltip");
|
||||
let isImg = node.tagName.toLowerCase() === "img";
|
||||
|
||||
let container = getContainerForRawNode(markup, node);
|
||||
@ -90,10 +90,14 @@ function testImageTooltip(index) {
|
||||
|
||||
assertTooltipShownOn(target, () => {
|
||||
let images = markup.tooltip.panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image");
|
||||
is(images.length, 1,
|
||||
"Tooltip for [" + TEST_NODES[index].selector + "] contains an image");
|
||||
|
||||
let label = markup.tooltip.panel.querySelector(".devtools-tooltip-caption");
|
||||
is(label.textContent, TEST_NODES[index].size,
|
||||
"Tooltip label for [" + TEST_NODES[index].selector + "] displays the right image size")
|
||||
|
||||
markup.tooltip.hide();
|
||||
|
||||
testImageTooltip(index + 1);
|
||||
});
|
||||
}
|
||||
|
@ -518,30 +518,33 @@ Tooltip.prototype = {
|
||||
}
|
||||
vbox.appendChild(image);
|
||||
|
||||
// Temporary label during image load
|
||||
// Dimension label
|
||||
let label = this.doc.createElement("label");
|
||||
label.classList.add("devtools-tooltip-caption");
|
||||
label.classList.add("theme-comment");
|
||||
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
|
||||
if (options.naturalWidth && options.naturalHeight) {
|
||||
label.textContent = this._getImageDimensionLabel(options.naturalWidth,
|
||||
options.naturalHeight);
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
} else {
|
||||
// If no dimensions were provided, load the image to get them
|
||||
label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage");
|
||||
let imgObj = new this.doc.defaultView.Image();
|
||||
imgObj.src = imageUrl;
|
||||
imgObj.onload = () => {
|
||||
imgObj.onload = null;
|
||||
label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth,
|
||||
imgObj.naturalHeight);
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
}
|
||||
}
|
||||
vbox.appendChild(label);
|
||||
|
||||
this.content = vbox;
|
||||
|
||||
// Load the image to get dimensions and display it when done
|
||||
let imgObj = new this.doc.defaultView.Image();
|
||||
imgObj.src = imageUrl;
|
||||
imgObj.onload = () => {
|
||||
imgObj.onload = null;
|
||||
|
||||
// Display dimensions
|
||||
let w = options.naturalWidth || imgObj.naturalWidth;
|
||||
let h = options.naturalHeight || imgObj.naturalHeight;
|
||||
label.textContent = w + " x " + h;
|
||||
|
||||
this.setSize(vbox.width, vbox.height);
|
||||
}
|
||||
},
|
||||
|
||||
_getImageDimensionLabel: (w, h) => w + " x " + h,
|
||||
|
||||
/**
|
||||
* Exactly the same as the `image` function but takes a css background image
|
||||
* value instead : url(....)
|
||||
|
@ -16,6 +16,7 @@ const LAZY_APPEND_BATCH = 100; // nodes
|
||||
const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
|
||||
const PAGE_SIZE_MAX_JUMPS = 30;
|
||||
const SEARCH_ACTION_MAX_DELAY = 300; // ms
|
||||
const ITEM_FLASH_DURATION = 300 // ms
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
@ -117,6 +118,9 @@ VariablesView.prototype = {
|
||||
/**
|
||||
* Adds a scope to contain any inspected variables.
|
||||
*
|
||||
* This new scope will be considered the parent of any other scope
|
||||
* added afterwards.
|
||||
*
|
||||
* @param string aName
|
||||
* The scope's name (e.g. "Local", "Global" etc.).
|
||||
* @return Scope
|
||||
@ -131,6 +135,7 @@ VariablesView.prototype = {
|
||||
this._itemsByElement.set(scope._target, scope);
|
||||
this._currHierarchy.set(aName, scope);
|
||||
scope.header = !!aName;
|
||||
|
||||
return scope;
|
||||
},
|
||||
|
||||
@ -604,23 +609,6 @@ VariablesView.prototype = {
|
||||
return this._store[aIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for the scope in this container displayed by the specified node.
|
||||
*
|
||||
* @param nsIDOMNode aNode
|
||||
* The node to search for.
|
||||
* @return Scope
|
||||
* The matched scope, or null if nothing is found.
|
||||
*/
|
||||
getScopeForNode: function(aNode) {
|
||||
let item = this._itemsByElement.get(aNode);
|
||||
// Match only Scopes, not Variables or Properties.
|
||||
if (item && !(item instanceof Variable)) {
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively searches this container for the scope, variable or property
|
||||
* displayed by the specified node.
|
||||
@ -634,6 +622,43 @@ VariablesView.prototype = {
|
||||
return this._itemsByElement.get(aNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the scope owning a Variable or Property.
|
||||
*
|
||||
* @param Variable | Property
|
||||
* The variable or property to retrieven the owner scope for.
|
||||
* @return Scope
|
||||
* The owner scope.
|
||||
*/
|
||||
getOwnerScopeForVariableOrProperty: function(aItem) {
|
||||
if (!aItem) {
|
||||
return null;
|
||||
}
|
||||
// If this is a Scope, return it.
|
||||
if (!(aItem instanceof Variable)) {
|
||||
return aItem;
|
||||
}
|
||||
// If this is a Variable or Property, find its owner scope.
|
||||
if (aItem instanceof Variable && aItem.ownerView) {
|
||||
return this.getOwnerScopeForVariableOrProperty(aItem.ownerView);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the parent scopes for a specified Variable or Property.
|
||||
* The returned list will not include the owner scope.
|
||||
*
|
||||
* @param Variable | Property
|
||||
* The variable or property for which to find the parent scopes.
|
||||
* @return array
|
||||
* A list of parent Scopes.
|
||||
*/
|
||||
getParentScopesForVariableOrProperty: function(aItem) {
|
||||
let scope = this.getOwnerScopeForVariableOrProperty(aItem);
|
||||
return this._store.slice(0, Math.max(this._store.indexOf(scope), 0));
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the currently focused scope, variable or property in this view.
|
||||
*
|
||||
@ -888,6 +913,7 @@ VariablesView.prototype = {
|
||||
label.className = "variables-view-empty-notice";
|
||||
label.setAttribute("value", this._emptyTextValue);
|
||||
|
||||
this._parent.setAttribute("empty", "");
|
||||
this._parent.appendChild(label);
|
||||
this._emptyTextNode = label;
|
||||
},
|
||||
@ -900,6 +926,7 @@ VariablesView.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this._parent.removeAttribute("empty");
|
||||
this._parent.removeChild(this._emptyTextNode);
|
||||
this._emptyTextNode = null;
|
||||
},
|
||||
@ -976,12 +1003,15 @@ VariablesView.prototype = {
|
||||
_window: null,
|
||||
|
||||
_store: null,
|
||||
_itemsByElement: null,
|
||||
_prevHierarchy: null,
|
||||
_currHierarchy: null,
|
||||
|
||||
_enumVisible: true,
|
||||
_nonEnumVisible: true,
|
||||
_alignedValues: false,
|
||||
_actionsFirst: false,
|
||||
|
||||
_parent: null,
|
||||
_list: null,
|
||||
_searchboxNode: null,
|
||||
@ -1244,6 +1274,7 @@ Scope.prototype = {
|
||||
this._variablesView._itemsByElement.set(child._target, child);
|
||||
this._variablesView._currHierarchy.set(child._absoluteName, child);
|
||||
child.header = !!aName;
|
||||
|
||||
return child;
|
||||
},
|
||||
|
||||
@ -1294,7 +1325,9 @@ Scope.prototype = {
|
||||
view._store.splice(view._store.indexOf(this), 1);
|
||||
view._itemsByElement.delete(this._target);
|
||||
view._currHierarchy.delete(this._nameString);
|
||||
|
||||
this._target.remove();
|
||||
|
||||
for (let variable of this._store.values()) {
|
||||
variable.remove();
|
||||
}
|
||||
@ -1725,7 +1758,8 @@ Scope.prototype = {
|
||||
_onClick: function(e) {
|
||||
if (e.button != 0 ||
|
||||
e.target == this._editNode ||
|
||||
e.target == this._deleteNode) {
|
||||
e.target == this._deleteNode ||
|
||||
e.target == this._addPropertyNode) {
|
||||
return;
|
||||
}
|
||||
this.toggle();
|
||||
@ -1935,25 +1969,6 @@ Scope.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the first search results match in this scope.
|
||||
* @return Variable | Property
|
||||
*/
|
||||
get _firstMatch() {
|
||||
for (let [, variable] of this._store) {
|
||||
let match;
|
||||
if (variable._isMatch) {
|
||||
match = variable;
|
||||
} else {
|
||||
match = variable._firstMatch;
|
||||
}
|
||||
if (match) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the first item in the tree of visible items in this item that matches
|
||||
* the predicate. Searches in visual order (the order seen by the user).
|
||||
@ -2078,11 +2093,12 @@ Scope.prototype = {
|
||||
switch: null,
|
||||
delete: null,
|
||||
new: null,
|
||||
editableValueTooltip: "",
|
||||
preventDisableOnChange: false,
|
||||
preventDescriptorModifiers: false,
|
||||
editableNameTooltip: "",
|
||||
editableValueTooltip: "",
|
||||
editButtonTooltip: "",
|
||||
deleteButtonTooltip: "",
|
||||
preventDescriptorModifiers: false,
|
||||
contextMenuId: "",
|
||||
separatorStr: "",
|
||||
|
||||
@ -2178,7 +2194,9 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this.ownerView._store.delete(this._nameString);
|
||||
this._variablesView._itemsByElement.delete(this._target);
|
||||
this._variablesView._currHierarchy.delete(this._absoluteName);
|
||||
|
||||
this._target.remove();
|
||||
|
||||
for (let property of this._store.values()) {
|
||||
property.remove();
|
||||
}
|
||||
@ -2355,6 +2373,37 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this._valueLabel.setAttribute("value", this._valueString);
|
||||
},
|
||||
|
||||
/**
|
||||
* Marks this variable as overridden.
|
||||
*
|
||||
* @param boolean aFlag
|
||||
* Whether this variable is overridden or not.
|
||||
*/
|
||||
setOverridden: function(aFlag) {
|
||||
if (aFlag) {
|
||||
this._target.setAttribute("overridden", "");
|
||||
} else {
|
||||
this._target.removeAttribute("overridden");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Briefly flashes this variable.
|
||||
*
|
||||
* @param number aDuration [optional]
|
||||
* An optional flash animation duration.
|
||||
*/
|
||||
flash: function(aDuration = ITEM_FLASH_DURATION) {
|
||||
let fadeInDelay = this._variablesView.lazyEmptyDelay + 1;
|
||||
let fadeOutDelay = fadeInDelay + aDuration;
|
||||
|
||||
setNamedTimeout("vview-flash-in" + this._absoluteName,
|
||||
fadeInDelay, () => this._target.setAttribute("changed", ""));
|
||||
|
||||
setNamedTimeout("vview-flash-out" + this._absoluteName,
|
||||
fadeOutDelay, () => this._target.removeAttribute("changed"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes this variable's id, view and binds event listeners.
|
||||
*
|
||||
@ -2405,7 +2454,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
let separatorLabel = this._separatorLabel = document.createElement("label");
|
||||
separatorLabel.className = "plain separator";
|
||||
separatorLabel.setAttribute("value", this.ownerView.separatorStr);
|
||||
separatorLabel.setAttribute("value", this.ownerView.separatorStr + " ");
|
||||
|
||||
let valueLabel = this._valueLabel = document.createElement("label");
|
||||
valueLabel.className = "plain value";
|
||||
@ -2428,6 +2477,8 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
separatorLabel.hidden = true;
|
||||
}
|
||||
|
||||
// If this is a getter/setter property, create two child pseudo-properties
|
||||
// called "get" and "set" that display the corresponding functions.
|
||||
if (descriptor.get || descriptor.set) {
|
||||
separatorLabel.hidden = true;
|
||||
valueLabel.hidden = true;
|
||||
@ -2480,15 +2531,16 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
this._title.appendChild(deleteNode);
|
||||
}
|
||||
|
||||
let { actionsFirst } = this._variablesView;
|
||||
if (ownerView.new || actionsFirst) {
|
||||
if (ownerView.new) {
|
||||
let addPropertyNode = this._addPropertyNode = this.document.createElement("toolbarbutton");
|
||||
addPropertyNode.className = "plain variables-view-add-property";
|
||||
addPropertyNode.addEventListener("mousedown", this._onAddProperty.bind(this), false);
|
||||
if (actionsFirst && VariablesView.isPrimitive(descriptor)) {
|
||||
this._title.appendChild(addPropertyNode);
|
||||
|
||||
// Can't add properties to primitive values, hide the node in those cases.
|
||||
if (VariablesView.isPrimitive(descriptor)) {
|
||||
addPropertyNode.setAttribute("invisible", "");
|
||||
}
|
||||
this._title.appendChild(addPropertyNode);
|
||||
}
|
||||
|
||||
if (ownerView.contextMenuId) {
|
||||
@ -2554,11 +2606,12 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
let labels = [
|
||||
"configurable", "enumerable", "writable",
|
||||
"frozen", "sealed", "extensible", "WebIDL"];
|
||||
"frozen", "sealed", "extensible", "overridden", "WebIDL"];
|
||||
|
||||
for (let label of labels) {
|
||||
for (let type of labels) {
|
||||
let labelElement = this.document.createElement("label");
|
||||
labelElement.setAttribute("value", label);
|
||||
labelElement.className = type;
|
||||
labelElement.setAttribute("value", STR.GetStringFromName(type + "Tooltip"));
|
||||
tooltip.appendChild(labelElement);
|
||||
}
|
||||
|
||||
@ -2623,17 +2676,25 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
if (descriptor && "getterValue" in descriptor) {
|
||||
target.setAttribute("safe-getter", "");
|
||||
}
|
||||
|
||||
if (name == "this") {
|
||||
target.setAttribute("self", "");
|
||||
}
|
||||
else if (name == "<exception>") {
|
||||
target.setAttribute("exception", "");
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
else if (name == "<return>") {
|
||||
target.setAttribute("return", "");
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
else if (name == "__proto__") {
|
||||
target.setAttribute("proto", "");
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
|
||||
if (Object.keys(descriptor).length == 0) {
|
||||
target.setAttribute("pseudo-item", "");
|
||||
}
|
||||
},
|
||||
|
||||
@ -2774,8 +2835,8 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
_absoluteName: "",
|
||||
_initialDescriptor: null,
|
||||
_separatorLabel: null,
|
||||
_spacer: null,
|
||||
_valueLabel: null,
|
||||
_spacer: null,
|
||||
_editNode: null,
|
||||
_deleteNode: null,
|
||||
_addPropertyNode: null,
|
||||
@ -2877,69 +2938,101 @@ VariablesView.prototype.createHierarchy = function() {
|
||||
* scope/variable/property hierarchies and reopen previously expanded nodes.
|
||||
*/
|
||||
VariablesView.prototype.commitHierarchy = function() {
|
||||
let prevHierarchy = this._prevHierarchy;
|
||||
let currHierarchy = this._currHierarchy;
|
||||
|
||||
for (let [absoluteName, currVariable] of currHierarchy) {
|
||||
// Ignore variables which were already commmitted.
|
||||
if (currVariable._committed) {
|
||||
continue;
|
||||
}
|
||||
for (let [, currItem] of this._currHierarchy) {
|
||||
// Avoid performing expensive operations.
|
||||
if (this.commitHierarchyIgnoredItems[currVariable._nameString]) {
|
||||
if (this.commitHierarchyIgnoredItems[currItem._nameString]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to get the previous instance of the inspected variable to
|
||||
// determine the difference in state.
|
||||
let prevVariable = prevHierarchy.get(absoluteName);
|
||||
let expanded = false;
|
||||
let changed = false;
|
||||
|
||||
// If the inspected variable existed in a previous hierarchy, check if
|
||||
// the displayed value (a representation of the grip) has changed and if
|
||||
// it was previously expanded.
|
||||
if (prevVariable) {
|
||||
expanded = prevVariable._isExpanded;
|
||||
|
||||
// Only analyze Variables and Properties for displayed value changes.
|
||||
if (currVariable instanceof Variable) {
|
||||
changed = prevVariable._valueString != currVariable._valueString;
|
||||
}
|
||||
let overridden = this.isOverridden(currItem);
|
||||
if (overridden) {
|
||||
currItem.setOverridden(true);
|
||||
}
|
||||
|
||||
// Make sure this variable is not handled in ulteror commits for the
|
||||
// same hierarchy.
|
||||
currVariable._committed = true;
|
||||
|
||||
// Re-expand the variable if not previously collapsed.
|
||||
let expanded = !currItem._committed && this.wasExpanded(currItem);
|
||||
if (expanded) {
|
||||
currVariable.expand();
|
||||
currItem.expand();
|
||||
}
|
||||
// This variable was either not changed or removed, no need to continue.
|
||||
if (!changed) {
|
||||
continue;
|
||||
let changed = !currItem._committed && this.hasChanged(currItem);
|
||||
if (changed) {
|
||||
currItem.flash();
|
||||
}
|
||||
|
||||
// Apply an attribute determining the flash type and duration.
|
||||
// Dispatch this action after all the nodes have been drawn, so that
|
||||
// the transition efects can take place.
|
||||
this.window.setTimeout(function(aTarget) {
|
||||
aTarget.addEventListener("transitionend", function onEvent() {
|
||||
aTarget.removeEventListener("transitionend", onEvent, false);
|
||||
aTarget.removeAttribute("changed");
|
||||
}, false);
|
||||
aTarget.setAttribute("changed", "");
|
||||
}.bind(this, currVariable.target), this.lazyEmptyDelay + 1);
|
||||
currItem._committed = true;
|
||||
}
|
||||
if (this.oncommit) {
|
||||
this.oncommit(this);
|
||||
}
|
||||
};
|
||||
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It would be a bad idea to re-expand them or perform expensive operations.
|
||||
VariablesView.prototype.commitHierarchyIgnoredItems = Object.create(null, {
|
||||
"window": { value: true }
|
||||
VariablesView.prototype.commitHierarchyIgnoredItems = Heritage.extend(null, {
|
||||
"window": true,
|
||||
"this": true
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks if the an item was previously expanded, if it existed in a
|
||||
* previous hierarchy.
|
||||
*
|
||||
* @param Scope | Variable | Property aItem
|
||||
* The item to verify.
|
||||
* @return boolean
|
||||
* Whether the item was expanded.
|
||||
*/
|
||||
VariablesView.prototype.wasExpanded = function(aItem) {
|
||||
if (!(aItem instanceof Scope)) {
|
||||
return false;
|
||||
}
|
||||
let prevItem = this._prevHierarchy.get(aItem._absoluteName || aItem._nameString);
|
||||
return prevItem ? prevItem._isExpanded : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the an item's displayed value (a representation of the grip)
|
||||
* has changed, if it existed in a previous hierarchy.
|
||||
*
|
||||
* @param Variable | Property aItem
|
||||
* The item to verify.
|
||||
* @return boolean
|
||||
* Whether the item has changed.
|
||||
*/
|
||||
VariablesView.prototype.hasChanged = function(aItem) {
|
||||
// Only analyze Variables and Properties for displayed value changes.
|
||||
// Scopes are just collections of Variables and Properties and
|
||||
// don't have a "value", so they can't change.
|
||||
if (!(aItem instanceof Variable)) {
|
||||
return false;
|
||||
}
|
||||
let prevItem = this._prevHierarchy.get(aItem._absoluteName);
|
||||
return prevItem ? prevItem._valueString != aItem._valueString : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the an item was previously expanded, if it existed in a
|
||||
* previous hierarchy.
|
||||
*
|
||||
* @param Scope | Variable | Property aItem
|
||||
* The item to verify.
|
||||
* @return boolean
|
||||
* Whether the item was expanded.
|
||||
*/
|
||||
VariablesView.prototype.isOverridden = function(aItem) {
|
||||
// Only analyze Variables for being overridden in different Scopes.
|
||||
if (!(aItem instanceof Variable) || aItem instanceof Property) {
|
||||
return false;
|
||||
}
|
||||
let currVariableName = aItem._nameString;
|
||||
let parentScopes = this.getParentScopesForVariableOrProperty(aItem);
|
||||
|
||||
for (let otherScope of parentScopes) {
|
||||
for (let [otherVariableName] of otherScope) {
|
||||
if (otherVariableName == currVariableName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the descriptor represents an undefined, null or
|
||||
* primitive value.
|
||||
@ -3247,9 +3340,6 @@ Editable.prototype = {
|
||||
let input = this._input = this._variable.document.createElement("textbox");
|
||||
input.className = "plain " + this.className;
|
||||
input.setAttribute("value", initialString);
|
||||
if (!this._variable._variablesView.alignedValues) {
|
||||
input.setAttribute("flex", "1");
|
||||
}
|
||||
|
||||
// Replace the specified label with a textbox input element.
|
||||
label.parentNode.replaceChild(input, label);
|
||||
|
@ -352,13 +352,19 @@ VariablesViewController.prototype = {
|
||||
aTarget.showArrow();
|
||||
}
|
||||
|
||||
// Make sure that properties are always available on expansion.
|
||||
aTarget.onexpand = () => this.expand(aTarget, aSource);
|
||||
if (aSource.type == "block" || aSource.type == "function") {
|
||||
// Block and function environments already contain scope arguments and
|
||||
// corresponding variables as bindings.
|
||||
this.populate(aTarget, aSource);
|
||||
} else {
|
||||
// Make sure that properties are always available on expansion.
|
||||
aTarget.onexpand = () => this.populate(aTarget, aSource);
|
||||
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It's a good idea to be prepared in case of an expansion.
|
||||
if (aTarget.shouldPrefetch) {
|
||||
aTarget.addEventListener("mouseover", aTarget.onexpand, false);
|
||||
// Some variables are likely to contain a very large number of properties.
|
||||
// It's a good idea to be prepared in case of an expansion.
|
||||
if (aTarget.shouldPrefetch) {
|
||||
aTarget.addEventListener("mouseover", aTarget.onexpand, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Register all the actors that this controller now depends on.
|
||||
@ -373,6 +379,8 @@ VariablesViewController.prototype = {
|
||||
* Adds properties to a Scope, Variable, or Property in the view. Triggered
|
||||
* when a scope is expanded or certain variables are hovered.
|
||||
*
|
||||
* This does not expand the target, it only populates it.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope to be expanded.
|
||||
* @param object aSource
|
||||
@ -380,7 +388,7 @@ VariablesViewController.prototype = {
|
||||
* @return Promise
|
||||
* The promise that is resolved once the target has been expanded.
|
||||
*/
|
||||
expand: function(aTarget, aSource) {
|
||||
populate: function(aTarget, aSource) {
|
||||
// Fetch the variables only once.
|
||||
if (aTarget._fetched) {
|
||||
return aTarget._fetched;
|
||||
@ -510,16 +518,17 @@ VariablesViewController.prototype = {
|
||||
scope.locked = true; // Prevent collpasing the scope.
|
||||
|
||||
let variable = scope.addItem("", { enumerable: true });
|
||||
let expanded;
|
||||
let populated;
|
||||
|
||||
if (aOptions.objectActor) {
|
||||
expanded = this.expand(variable, aOptions.objectActor);
|
||||
populated = this.populate(variable, aOptions.objectActor);
|
||||
variable.expand();
|
||||
} else if (aOptions.rawObject) {
|
||||
variable.populate(aOptions.rawObject, { expanded: true });
|
||||
expanded = promise.resolve();
|
||||
populated = promise.resolve();
|
||||
}
|
||||
|
||||
return { variable: variable, expanded: expanded };
|
||||
return { variable: variable, expanded: populated };
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -56,10 +56,15 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.variable-or-property:not([safe-getter]) > tooltip > label[value=WebIDL],
|
||||
.variable-or-property:not([non-extensible]) > tooltip > label[value=extensible],
|
||||
.variable-or-property:not([frozen]) > tooltip > label[value=frozen],
|
||||
.variable-or-property:not([sealed]) > tooltip > label[value=sealed] {
|
||||
.variable-or-property:not([safe-getter]) > tooltip > label.WebIDL,
|
||||
.variable-or-property:not([overridden]) > tooltip > label.overridden,
|
||||
.variable-or-property:not([non-extensible]) > tooltip > label.extensible,
|
||||
.variable-or-property:not([frozen]) > tooltip > label.frozen,
|
||||
.variable-or-property:not([sealed]) > tooltip > label.sealed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.variable-or-property[pseudo-item] > tooltip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -73,6 +78,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.variables-view-container[aligned-values] .title > [optional-visibility] {
|
||||
.variables-view-container[aligned-values] [optional-visibility] {
|
||||
display: none;
|
||||
}
|
||||
|
@ -72,15 +72,26 @@ StyleEditorPanel.prototype = {
|
||||
*
|
||||
* @param {string} event
|
||||
* Type of event
|
||||
* @param {string} errorCode
|
||||
* @param {string} code
|
||||
* Error code of error to report
|
||||
* @param {string} message
|
||||
* Extra message to append to error message
|
||||
*/
|
||||
_showError: function(event, errorCode) {
|
||||
let message = _(errorCode);
|
||||
_showError: function(event, code, message) {
|
||||
if (!this._toolbox) {
|
||||
// could get an async error after we've been destroyed
|
||||
return;
|
||||
}
|
||||
|
||||
let errorMessage = _(code);
|
||||
if (message) {
|
||||
errorMessage += " " + message;
|
||||
}
|
||||
|
||||
let notificationBox = this._toolbox.getNotificationBox();
|
||||
let notification = notificationBox.getNotificationWithValue("styleeditor-error");
|
||||
if (!notification) {
|
||||
notificationBox.appendNotification(message,
|
||||
notificationBox.appendNotification(errorMessage,
|
||||
"styleeditor-error", "", notificationBox.PRIORITY_CRITICAL_LOW);
|
||||
}
|
||||
},
|
||||
|
@ -42,7 +42,7 @@ function highlightNode(aInspector, aComputedView)
|
||||
is(inspector.selection.node, div, "selection matches the div element");
|
||||
|
||||
expandProperty(0, testComputedViewLink);
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function testComputedViewLink() {
|
||||
|
@ -66,7 +66,7 @@ function highlightNode()
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div, "selection matches the div element");
|
||||
testInlineStyle();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function testInlineStyle()
|
||||
|
@ -48,7 +48,7 @@ function highlightNode()
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div, "selection matches the div element");
|
||||
testRuleViewLink();
|
||||
}).then(null, console.error);
|
||||
});
|
||||
}
|
||||
|
||||
function testRuleViewLink() {
|
||||
|
@ -233,6 +233,23 @@ variablesCloseButtonTooltip=Click to remove
|
||||
# in the variables list on a getter or setter which can be edited.
|
||||
variablesEditButtonTooltip=Click to set value
|
||||
|
||||
# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed
|
||||
# in the variables list on certain variables or properties as tooltips.
|
||||
# Expanations of what these represent can be found at the following links:
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
|
||||
# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed
|
||||
# It's probably best to keep these in English.
|
||||
configurableTooltip=configurable
|
||||
enumerableTooltip=enumerable
|
||||
writableTooltip=writable
|
||||
frozenTooltip=frozen
|
||||
sealedTooltip=sealed
|
||||
extensibleTooltip=extensible
|
||||
overriddenTooltip=overridden
|
||||
WebIDLTooltip=WebIDL
|
||||
|
||||
# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed
|
||||
# in the variables list as a separator between the name and value.
|
||||
variablesSeparatorLabel=:
|
||||
|
@ -209,6 +209,7 @@ let Util = {
|
||||
aURL == "about:blank" ||
|
||||
aURL == "about:empty" ||
|
||||
aURL == "about:home" ||
|
||||
aURL == "about:newtab" ||
|
||||
aURL == "about:start");
|
||||
},
|
||||
|
||||
|
@ -347,6 +347,12 @@ let WebNavigation = {
|
||||
let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
|
||||
for (let i = 0; i < history.count; i++) {
|
||||
let entry = this._serializeHistoryEntry(history.getEntryAtIndex(i, false));
|
||||
|
||||
// If someone directly navigates to one of these URLs and they switch to Desktop,
|
||||
// we need to make the page load-able.
|
||||
if (entry.url == "about:home" || entry.url == "about:start") {
|
||||
entry.url = "about:newtab";
|
||||
}
|
||||
entries.push(entry);
|
||||
}
|
||||
let index = history.index + 1;
|
||||
|
@ -308,7 +308,7 @@ var BrowserUI = {
|
||||
|
||||
isStartURI: function isStartURI(aURI) {
|
||||
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
|
||||
return aURI == kStartURI || aURI == "about:home";
|
||||
return aURI == kStartURI || aURI == "about:start" || aURI == "about:home";
|
||||
},
|
||||
|
||||
updateStartURIAttributes: function (aURI) {
|
||||
|
@ -11,7 +11,7 @@ let Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/PageThumbs.jsm");
|
||||
|
||||
// Page for which the start UI is shown
|
||||
const kStartURI = "about:start";
|
||||
const kStartURI = "about:newtab";
|
||||
|
||||
// allow panning after this timeout on pages with registered touch listeners
|
||||
const kTouchTimeout = 300;
|
||||
|
@ -9,6 +9,7 @@
|
||||
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/cssthrobber.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://branding/content/metro-about.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
@ -384,7 +385,9 @@ Desktop browser's sync prefs.
|
||||
</appbar>
|
||||
<autoscroller class="autoscroller" id="autoscrollerid"/>
|
||||
<flyoutpanel id="about-flyoutpanel" class="flyout-narrow" headertext="&aboutHeader.title;">
|
||||
<label id="about-product-label" linewrap="true" value="&aboutHeader.product.label;"/>
|
||||
<label align="start">
|
||||
<html:img src="chrome://branding/content/metro-about-wordmark.png" alt="&aboutHeader.product.label;"/>
|
||||
</label>
|
||||
<label value="&aboutHeader.company.label;" linewrap="true"/>
|
||||
#expand <label id="about-version-label">__MOZ_APP_VERSION__</label>
|
||||
<vbox id="updateBox">
|
||||
|
@ -8,7 +8,7 @@ const Ci = Components.interfaces;
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let modules = {
|
||||
start: {
|
||||
newtab: {
|
||||
uri: "chrome://browser/content/Start.xul",
|
||||
privileged: true
|
||||
},
|
||||
@ -37,8 +37,12 @@ let modules = {
|
||||
uri: "chrome://browser/content/aboutCertError.xhtml",
|
||||
privileged: true
|
||||
},
|
||||
start: {
|
||||
uri: "about:newtab",
|
||||
privileged: true
|
||||
},
|
||||
home: {
|
||||
uri: "about:start",
|
||||
uri: "about:newtab",
|
||||
privileged: true
|
||||
},
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
@ -73,7 +73,7 @@ function needHomepageOverride() {
|
||||
}
|
||||
|
||||
function getHomePage() {
|
||||
let url = "about:start";
|
||||
let url = "about:newtab";
|
||||
try {
|
||||
url = Services.prefs.getComplexValue("browser.startup.homepage", Ci.nsIPrefLocalizedString).data;
|
||||
} catch (e) { }
|
||||
|
@ -232,7 +232,7 @@ SessionStore.prototype = {
|
||||
|
||||
// Clear all data about closed tabs
|
||||
for (let [ssid, win] in Iterator(this._windows))
|
||||
win.closedTabs = [];
|
||||
win._closedTabs = [];
|
||||
|
||||
if (this._loadState == STATE_RUNNING) {
|
||||
// Save the purged state immediately
|
||||
@ -286,7 +286,7 @@ SessionStore.prototype = {
|
||||
|
||||
// Assign it a unique identifier (timestamp) and create its data object
|
||||
aWindow.__SSID = "window" + Date.now();
|
||||
this._windows[aWindow.__SSID] = { tabs: [], selected: 0, closedTabs: [] };
|
||||
this._windows[aWindow.__SSID] = { tabs: [], selected: 0, _closedTabs: [] };
|
||||
|
||||
// Perform additional initialization when the first window is loading
|
||||
if (this._loadState == STATE_STOPPED) {
|
||||
@ -374,12 +374,15 @@ SessionStore.prototype = {
|
||||
// NB: The access to aBrowser.__SS_extdata throws during automation (in
|
||||
// browser_msgmgr_01). See bug 888736.
|
||||
let data = aBrowser.__SS_data;
|
||||
if (!data) {
|
||||
return; // Cannot restore an empty tab.
|
||||
}
|
||||
try { data.extData = aBrowser.__SS_extdata; } catch (e) { }
|
||||
|
||||
this._windows[aWindow.__SSID].closedTabs.unshift(data);
|
||||
let length = this._windows[aWindow.__SSID].closedTabs.length;
|
||||
this._windows[aWindow.__SSID]._closedTabs.unshift({ state: data });
|
||||
let length = this._windows[aWindow.__SSID]._closedTabs.length;
|
||||
if (length > this._maxTabsUndo)
|
||||
this._windows[aWindow.__SSID].closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo);
|
||||
this._windows[aWindow.__SSID]._closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo);
|
||||
}
|
||||
},
|
||||
|
||||
@ -577,21 +580,21 @@ SessionStore.prototype = {
|
||||
if (!aWindow || !aWindow.__SSID)
|
||||
return 0; // not a browser window, or not otherwise tracked by SS.
|
||||
|
||||
return this._windows[aWindow.__SSID].closedTabs.length;
|
||||
return this._windows[aWindow.__SSID]._closedTabs.length;
|
||||
},
|
||||
|
||||
getClosedTabData: function ss_getClosedTabData(aWindow) {
|
||||
if (!aWindow.__SSID)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
return JSON.stringify(this._windows[aWindow.__SSID].closedTabs);
|
||||
return JSON.stringify(this._windows[aWindow.__SSID]._closedTabs);
|
||||
},
|
||||
|
||||
undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
|
||||
if (!aWindow.__SSID)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let closedTabs = this._windows[aWindow.__SSID].closedTabs;
|
||||
let closedTabs = this._windows[aWindow.__SSID]._closedTabs;
|
||||
if (!closedTabs)
|
||||
return null;
|
||||
|
||||
@ -604,13 +607,13 @@ SessionStore.prototype = {
|
||||
let closedTab = closedTabs.splice(aIndex, 1).shift();
|
||||
|
||||
// create a new tab and bring to front
|
||||
let tab = aWindow.Browser.addTab(closedTab.entries[closedTab.index - 1].url, true);
|
||||
let tab = aWindow.Browser.addTab(closedTab.state.entries[closedTab.state.index - 1].url, true);
|
||||
|
||||
tab.browser.messageManager.sendAsyncMessage("WebNavigation:LoadURI", {
|
||||
uri: closedTab.entries[closedTab.index - 1].url,
|
||||
uri: closedTab.state.entries[closedTab.state.index - 1].url,
|
||||
flags: null,
|
||||
entries: closedTab.entries,
|
||||
index: closedTab.index
|
||||
entries: closedTab.state.entries,
|
||||
index: closedTab.state.index
|
||||
});
|
||||
|
||||
// Put back the extra data
|
||||
@ -623,7 +626,7 @@ SessionStore.prototype = {
|
||||
if (!aWindow.__SSID)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let closedTabs = this._windows[aWindow.__SSID].closedTabs;
|
||||
let closedTabs = this._windows[aWindow.__SSID]._closedTabs;
|
||||
|
||||
// default to the most-recently closed tab
|
||||
aIndex = aIndex || 0;
|
||||
@ -723,6 +726,10 @@ SessionStore.prototype = {
|
||||
|
||||
let tabs = data.windows[0].tabs;
|
||||
let selected = data.windows[0].selected;
|
||||
|
||||
if (data.windows[0]._closedTabs)
|
||||
this._windows[window.__SSID]._closedTabs = data.windows[0]._closedTabs;
|
||||
|
||||
if (selected > tabs.length) // Clamp the selected index if it's bogus
|
||||
selected = 1;
|
||||
|
||||
@ -757,7 +764,7 @@ SessionStore.prototype = {
|
||||
}
|
||||
|
||||
notifyObservers();
|
||||
});
|
||||
}.bind(this));
|
||||
} catch (ex) {
|
||||
Cu.reportError("SessionStore: Could not read from sessionstore.bak file: " + ex);
|
||||
notifyObservers("fail");
|
||||
|
@ -5,6 +5,7 @@ contract @mozilla.org/network/protocol/about;1?what=empty {433d2d75-5923-49b0-85
|
||||
contract @mozilla.org/network/protocol/about;1?what=firefox {433d2d75-5923-49b0-854d-f37267b03dc7}
|
||||
contract @mozilla.org/network/protocol/about;1?what=rights {433d2d75-5923-49b0-854d-f37267b03dc7}
|
||||
contract @mozilla.org/network/protocol/about;1?what=certerror {433d2d75-5923-49b0-854d-f37267b03dc7}
|
||||
contract @mozilla.org/network/protocol/about;1?what=newtab {433d2d75-5923-49b0-854d-f37267b03dc7}
|
||||
contract @mozilla.org/network/protocol/about;1?what=home {433d2d75-5923-49b0-854d-f37267b03dc7}
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
contract @mozilla.org/network/protocol/about;1?what=crashprompt {433d2d75-5923-49b0-854d-f37267b03dc7}
|
||||
|
@ -203,6 +203,8 @@ pref("layout.spellcheckDefault", 1);
|
||||
/* extension manager and xpinstall */
|
||||
// Completely disable extensions
|
||||
pref("extensions.defaultProviders.enabled", false);
|
||||
// Disable version checks making addons compatible by default
|
||||
pref("extensions.strictCompatibility", false);
|
||||
// Disable all add-on locations other than the profile
|
||||
pref("extensions.enabledScopes", 1);
|
||||
// Auto-disable any add-ons that are "dropped in" to the profile
|
||||
|
@ -29,12 +29,12 @@
|
||||
#tabs-container {
|
||||
background: @panel_dark_color@ @panel_dark_background@;
|
||||
padding: 0;
|
||||
-moz-padding-start: @metro_spacing_xnormal@;
|
||||
-moz-padding-start: @metro_spacing_normal@;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
-moz-padding-start: @metro_spacing_large@;
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
#tabs .tabs-list {
|
||||
@ -46,17 +46,22 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#tabs > .tabs-scrollbox > .arrowscrollbox-scrollbox {
|
||||
overflow: hidden;
|
||||
#tabs[input="precise"] > .tabs-scrollbox > .scrollbutton-up {
|
||||
visibility: visible !important;
|
||||
}
|
||||
#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-up {
|
||||
visibility: hidden !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-up,
|
||||
#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-down {
|
||||
visibility: collapse !important;
|
||||
}
|
||||
|
||||
#tabs > .tabs-scrollbox > .scrollbutton-up {
|
||||
list-style-image: url("images/tab-arrows.png") !important;
|
||||
-moz-image-region: rect(15px 58px 63px 14px) !important;
|
||||
padding-right: 13px;
|
||||
}
|
||||
#tabs > .tabs-scrollbox > .scrollbutton-up:hover {
|
||||
-moz-image-region: rect(14px 102px 62px 58px) !important;
|
||||
@ -71,6 +76,7 @@
|
||||
#tabs > .tabs-scrollbox > .scrollbutton-down {
|
||||
list-style-image: url("images/tab-arrows.png") !important;
|
||||
-moz-image-region: rect(73px 58px 121px 14px) !important;
|
||||
padding-left: 16px;
|
||||
}
|
||||
#tabs > .tabs-scrollbox > .scrollbutton-down:hover {
|
||||
-moz-image-region: rect(72px 102px 120px 58px) !important;
|
||||
@ -182,7 +188,7 @@ documenttab[selected] .documenttab-selection {
|
||||
list-style-image: url(chrome://browser/skin/images/newtab-default.png);
|
||||
|
||||
/* Add some extra padding for a larger target */
|
||||
padding: 18px 25px 30px;
|
||||
padding: 18px 20px 30px 20px;
|
||||
}
|
||||
|
||||
/* Start UI ----------------------------------------------------------------- */
|
||||
@ -1167,25 +1173,34 @@ flyoutpanel > settings:first-child {
|
||||
|
||||
/* About flyout pane ------------------------------------------------------- */
|
||||
|
||||
/* Colors are defined in /browser/branding/<dir>/content/metro-about.css */
|
||||
|
||||
#about-flyoutpanel {
|
||||
background-image:url('chrome://browser/skin/images/about-footer.png');
|
||||
background-image: url('chrome://branding/content/metro-about-footer.png');
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-position: right bottom;
|
||||
}
|
||||
|
||||
#about-product-label {
|
||||
font-weight: bold;
|
||||
#about-flyoutpanel .text-link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#about-flyoutpanel > .flyoutpanel-wrapper > .flyoutpanel-header,
|
||||
#about-flyoutpanel > .flyoutpanel-wrapper > .flyoutpanel-contents {
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#about-policy-label {
|
||||
padding: 1.5em @metro_spacing_large@;
|
||||
margin: 1em -@metro_spacing_large@;
|
||||
}
|
||||
|
||||
#about-version-label {
|
||||
margin-top: 11pt;
|
||||
}
|
||||
|
||||
#about-policy-label {
|
||||
margin-top: 24pt;
|
||||
}
|
||||
|
||||
#currentChannel {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
@ -63,7 +63,7 @@ flyoutpanel[visible] {
|
||||
border-width: 1px;
|
||||
-moz-border-start-style: solid;
|
||||
border-color: #c2c2c2;
|
||||
padding: 40px;
|
||||
padding: @metro_spacing_large@;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 127 KiB |
@ -95,7 +95,6 @@ chrome.jar:
|
||||
skin/images/aboutAddonsBackground.png (images/aboutAddonsBackground.png)
|
||||
skin/images/aboutAddonsBackgroundFillSlice.png (images/aboutAddonsBackgroundFillSlice.png)
|
||||
skin/images/flyout-back-button.png (images/flyout-back-button.png)
|
||||
skin/images/about-footer.png (images/about-footer.png)
|
||||
skin/images/arrowleft-16.png (images/arrowleft-16.png)
|
||||
skin/images/arrowright-16.png (images/arrowright-16.png)
|
||||
skin/images/arrowup-16.png (images/arrowup-16.png)
|
||||
|
@ -11,9 +11,11 @@ const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
#ifndef XP_WIN
|
||||
#ifdef XP_UNIX
|
||||
#ifndef XP_MACOSX
|
||||
#define BROKEN_WM_Z_ORDER
|
||||
#endif
|
||||
#endif
|
||||
|
||||
this.RecentWindow = {
|
||||
/*
|
||||
|
@ -68,12 +68,10 @@
|
||||
|
||||
/* Toolbars */
|
||||
|
||||
.devtools-toolbar {
|
||||
height: 26px;
|
||||
.splitview-main > .devtools-toolbar {
|
||||
background-origin: border-box;
|
||||
background-clip: border-box;
|
||||
border-bottom: 1px solid hsla(210,8%,5%,.65);
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.splitview-main > toolbar:-moz-locale-dir(ltr) {
|
||||
@ -84,7 +82,7 @@
|
||||
border-left: 1px solid hsla(210,8%,5%,.5);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton {
|
||||
.splitview-main > .devtools-toolbarbutton {
|
||||
font-size: 11px;
|
||||
padding: 0 8px;
|
||||
width: auto;
|
||||
|
@ -269,6 +269,11 @@
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
.side-menu-widget-container[theme="dark"] {
|
||||
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
|
||||
color: #fff;
|
||||
@ -432,6 +437,11 @@
|
||||
|
||||
/* VariablesView */
|
||||
|
||||
.variables-view-container:not([empty]) {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.variables-view-empty-notice {
|
||||
color: GrayText;
|
||||
padding: 2px;
|
||||
@ -446,32 +456,7 @@
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
/* Generic traits applied to both variables and properties */
|
||||
|
||||
.variable-or-property {
|
||||
transition: background 1s ease-in-out, color 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property[changed] {
|
||||
color: black;
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
/* Custom variables and properties traits */
|
||||
/* Generic variables traits */
|
||||
|
||||
.variables-view-variable {
|
||||
-moz-margin-start: 1px;
|
||||
@ -486,26 +471,70 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Generic variables *and* properties traits */
|
||||
|
||||
.variable-or-property:focus > .title > label {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
/* Traits applied when variables or properties are changed or overridden */
|
||||
|
||||
.variable-or-property:not([overridden]) {
|
||||
transition: background 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property:not([overridden])[changed] {
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] {
|
||||
background: rgba(128,128,128,0.05);
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] .title > label {
|
||||
/* Cross out the title for this variable and all child properties. */
|
||||
font-style: italic;
|
||||
text-decoration: line-through;
|
||||
border-bottom: none !important;
|
||||
color: rgba(128,128,128,0.9);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Traits applied when variables or properties are editable */
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] .title > .value {
|
||||
/* Disallow editing this variable and all child properties. */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
|
||||
* variables and properties */
|
||||
|
||||
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]):not([scope]) > .title > .name {
|
||||
.variable-or-property[non-enumerable]:not([self]):not([pseudo-item]) > .title > .name {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.variable-or-property[non-configurable] > .title > .name {
|
||||
.variable-or-property[non-configurable]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #99f;
|
||||
}
|
||||
|
||||
.variable-or-property[non-writable] > .title > .name {
|
||||
.variable-or-property[non-writable]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #f99;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
.variable-or-property[safe-getter]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
@ -567,15 +596,20 @@
|
||||
margin: 0 2px 0 2px;
|
||||
}
|
||||
|
||||
.variable-or-property[non-enumerable] > tooltip > label[value=enumerable],
|
||||
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
|
||||
.variable-or-property[non-writable] > tooltip > label[value=writable],
|
||||
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
|
||||
.variable-or-property[non-enumerable] > tooltip > label.enumerable,
|
||||
.variable-or-property[non-configurable] > tooltip > label.configurable,
|
||||
.variable-or-property[non-writable] > tooltip > label.writable,
|
||||
.variable-or-property[non-extensible] > tooltip > label.extensible {
|
||||
color: #800;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > tooltip > label[value=WebIDL] {
|
||||
.variable-or-property[overridden] > tooltip > label.overridden {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-border-start: 1px dotted #000;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > tooltip > label.WebIDL {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-border-start: 1px dotted #000;
|
||||
color: #080;
|
||||
@ -610,7 +644,7 @@
|
||||
}
|
||||
|
||||
.element-value-input {
|
||||
-moz-margin-start: 4px !important;
|
||||
-moz-margin-start: -2px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
}
|
||||
|
||||
@ -623,6 +657,7 @@
|
||||
.element-value-input,
|
||||
.element-name-input {
|
||||
border: 1px solid rgba(128, 128, 128, .5) !important;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
@ -68,12 +68,10 @@
|
||||
|
||||
/* Toolbars */
|
||||
|
||||
.devtools-toolbar {
|
||||
height: 26px;
|
||||
.splitview-main > .devtools-toolbar {
|
||||
background-origin: border-box;
|
||||
background-clip: border-box;
|
||||
border-bottom: 1px solid hsla(210,8%,5%,.65);
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.splitview-main > toolbar:-moz-locale-dir(ltr) {
|
||||
@ -84,7 +82,7 @@
|
||||
border-left: 1px solid hsla(210,8%,5%,.5);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton {
|
||||
.splitview-main > .devtools-toolbarbutton {
|
||||
font-size: 11px;
|
||||
padding: 0 8px;
|
||||
width: auto;
|
||||
|
@ -269,6 +269,11 @@
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
.side-menu-widget-container[theme="dark"] {
|
||||
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
|
||||
color: #fff;
|
||||
@ -426,6 +431,11 @@
|
||||
|
||||
/* VariablesView */
|
||||
|
||||
.variables-view-container:not([empty]) {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.variables-view-empty-notice {
|
||||
color: GrayText;
|
||||
padding: 2px;
|
||||
@ -440,32 +450,7 @@
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
/* Generic traits applied to both variables and properties */
|
||||
|
||||
.variable-or-property {
|
||||
transition: background 1s ease-in-out, color 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property[changed] {
|
||||
color: black;
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
/* Custom variables and properties traits */
|
||||
/* Generic variables traits */
|
||||
|
||||
.variables-view-variable {
|
||||
-moz-margin-start: 1px;
|
||||
@ -480,26 +465,70 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Generic variables *and* properties traits */
|
||||
|
||||
.variable-or-property:focus > .title > label {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
/* Traits applied when variables or properties are changed or overridden */
|
||||
|
||||
.variable-or-property:not([overridden]) {
|
||||
transition: background 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property:not([overridden])[changed] {
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] {
|
||||
background: rgba(128,128,128,0.05);
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] .title > label {
|
||||
/* Cross out the title for this variable and all child properties. */
|
||||
font-style: italic;
|
||||
text-decoration: line-through;
|
||||
border-bottom: none !important;
|
||||
color: rgba(128,128,128,0.9);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Traits applied when variables or properties are editable */
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] .title > .value {
|
||||
/* Disallow editing this variable and all child properties. */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
|
||||
* variables and properties */
|
||||
|
||||
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]):not([scope]) > .title > .name {
|
||||
.variable-or-property[non-enumerable]:not([self]):not([pseudo-item]) > .title > .name {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.variable-or-property[non-configurable] > .title > .name {
|
||||
.variable-or-property[non-configurable]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #99f;
|
||||
}
|
||||
|
||||
.variable-or-property[non-writable] > .title > .name {
|
||||
.variable-or-property[non-writable]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #f99;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
.variable-or-property[safe-getter]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
@ -561,15 +590,20 @@
|
||||
margin: 0 2px 0 2px;
|
||||
}
|
||||
|
||||
.variable-or-property[non-enumerable] > tooltip > label[value=enumerable],
|
||||
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
|
||||
.variable-or-property[non-writable] > tooltip > label[value=writable],
|
||||
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
|
||||
.variable-or-property[non-enumerable] > tooltip > label.enumerable,
|
||||
.variable-or-property[non-configurable] > tooltip > label.configurable,
|
||||
.variable-or-property[non-writable] > tooltip > label.writable,
|
||||
.variable-or-property[non-extensible] > tooltip > label.extensible {
|
||||
color: #800;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > tooltip > label[value=WebIDL] {
|
||||
.variable-or-property[overridden] > tooltip > label.overridden {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-border-start: 1px dotted #000;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > tooltip > label.WebIDL {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-border-start: 1px dotted #000;
|
||||
color: #080;
|
||||
@ -604,7 +638,7 @@
|
||||
}
|
||||
|
||||
.element-value-input {
|
||||
-moz-margin-start: 4px !important;
|
||||
-moz-margin-start: -2px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
}
|
||||
|
||||
@ -617,6 +651,7 @@
|
||||
.element-value-input,
|
||||
.element-name-input {
|
||||
border: 1px solid rgba(128, 128, 128, .5) !important;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,9 @@
|
||||
.devtools-horizontal-splitter {
|
||||
-moz-appearance: none;
|
||||
background-image: none;
|
||||
border-top: 1px solid black;
|
||||
border-bottom-width: 0;
|
||||
background-color: transparent;
|
||||
border: 1px solid black;
|
||||
border-width: 1px 0 0 0;
|
||||
min-height: 3px;
|
||||
height: 3px;
|
||||
margin-bottom: -3px;
|
||||
|
@ -52,7 +52,7 @@
|
||||
}
|
||||
|
||||
.theme-bg-contrast,
|
||||
.variable-or-property[changed] { /* contrast bg color to attract attention on a container */
|
||||
.variable-or-property:not([overridden])[changed] { /* contrast bg color to attract attention on a container */
|
||||
background: #a18650;
|
||||
}
|
||||
|
||||
@ -93,7 +93,8 @@
|
||||
.theme-fg-color1,
|
||||
.cm-s-mozilla .cm-number,
|
||||
.variable-or-property .token-number,
|
||||
.variable-or-property[return] > .title > .name { /* green */
|
||||
.variable-or-property[return] > .title > .name,
|
||||
.variable-or-property[scope] > .title > .name { /* green */
|
||||
color: #5c9966;
|
||||
}
|
||||
|
||||
@ -103,8 +104,7 @@
|
||||
.cm-s-mozilla .cm-def,
|
||||
.cm-s-mozilla .cm-property,
|
||||
.cm-s-mozilla .cm-qualifier,
|
||||
.variables-view-variable > .title > .name,
|
||||
.variable-or-property[scope] > .title > .name { /* blue */
|
||||
.variables-view-variable > .title > .name { /* blue */
|
||||
color: #3689b2;
|
||||
}
|
||||
|
||||
@ -112,8 +112,7 @@
|
||||
.cm-s-mozilla .cm-builtin,
|
||||
.cm-s-mozilla .cm-tag,
|
||||
.cm-s-mozilla .cm-header,
|
||||
.variables-view-property > .title > .name,
|
||||
.variable-or-property[safe-getter] > .title > .name { /* pink/lavender */
|
||||
.variables-view-property > .title > .name { /* pink/lavender */
|
||||
color: #a673bf;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
||||
}
|
||||
|
||||
.theme-bg-contrast,
|
||||
.variable-or-property[changed] { /* contrast bg color to attract attention on a container */
|
||||
.variable-or-property:not([overridden])[changed] { /* contrast bg color to attract attention on a container */
|
||||
background: #a18650;
|
||||
}
|
||||
|
||||
@ -92,7 +92,8 @@
|
||||
.theme-fg-color1,
|
||||
.cm-s-mozilla .cm-number,
|
||||
.variable-or-property .token-number,
|
||||
.variable-or-property[return] > .title > .name { /* green */
|
||||
.variable-or-property[return] > .title > .name,
|
||||
.variable-or-property[scope] > .title > .name { /* green */
|
||||
color: hsl(72,100%,27%);
|
||||
}
|
||||
|
||||
@ -102,8 +103,7 @@
|
||||
.cm-s-mozilla .cm-def,
|
||||
.cm-s-mozilla .cm-property,
|
||||
.cm-s-mozilla .cm-qualifier,
|
||||
.variables-view-variable > .title > .name,
|
||||
.variable-or-property[scope] > .title > .name { /* blue */
|
||||
.variables-view-variable > .title > .name { /* blue */
|
||||
color: hsl(208,56%,40%);
|
||||
}
|
||||
|
||||
@ -111,8 +111,7 @@
|
||||
.cm-s-mozilla .cm-variable,
|
||||
.cm-s-mozilla .cm-tag,
|
||||
.cm-s-mozilla .cm-header,
|
||||
.variables-view-property > .title > .name,
|
||||
.variable-or-property[safe-getter] > .title > .name { /* dark blue */
|
||||
.variables-view-property > .title > .name { /* dark blue */
|
||||
color: hsl(208,81%,21%)
|
||||
}
|
||||
|
||||
@ -297,4 +296,4 @@ div.CodeMirror span.eval-text {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
%include toolbars.inc.css
|
||||
%include toolbars.inc.css
|
||||
|
@ -39,6 +39,10 @@
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton > .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton:not([label]) {
|
||||
min-width: 32px;
|
||||
}
|
||||
@ -130,7 +134,11 @@
|
||||
margin: 0 3px;
|
||||
min-height: 22px;
|
||||
border: 1px solid hsla(210,8%,5%,.6);
|
||||
%ifdef XP_MACOSX
|
||||
border-radius: 20px;
|
||||
%else
|
||||
border-radius: 2px;
|
||||
%endif
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
|
||||
padding: 3px;
|
||||
@ -322,6 +330,7 @@
|
||||
#toolbox-controls > toolbarbutton,
|
||||
#toolbox-dock-buttons > toolbarbutton {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
margin: 0 4px;
|
||||
min-width: 16px;
|
||||
width: 16px;
|
||||
@ -372,6 +381,7 @@
|
||||
|
||||
.command-button {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
padding: 0 8px;
|
||||
margin: 0;
|
||||
width: 16px;
|
||||
|
@ -841,6 +841,16 @@ menuitem.bookmark-item {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme:hover:not([focused]):not([readonly]),
|
||||
.searchbar-textbox:-moz-lwtheme:hover:not([focused]) {
|
||||
background-color: rgba(255,255,255,.9);
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme[focused],
|
||||
.searchbar-textbox:-moz-lwtheme[focused] {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
@conditionalForwardWithUrlbar@ > #urlbar-wrapper {
|
||||
padding-left: @conditionalForwardWithUrlbarWidth@px;
|
||||
-moz-margin-start: -@conditionalForwardWithUrlbarWidth@px;
|
||||
@ -898,11 +908,6 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder,
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#urlbar:-moz-lwtheme[focused="true"],
|
||||
.searchbar-textbox:-moz-lwtheme[focused="true"] {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#urlbar-container {
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
@ -68,13 +68,10 @@
|
||||
|
||||
/* Toolbars */
|
||||
|
||||
.devtools-toolbar {
|
||||
height: 26px;
|
||||
.splitview-main > .devtools-toolbar {
|
||||
background-origin: border-box;
|
||||
background-clip: border-box;
|
||||
border-top-width: 0;
|
||||
border-bottom: 1px solid hsla(210,8%,5%,.65);
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.splitview-main > toolbar:-moz-locale-dir(ltr) {
|
||||
@ -85,7 +82,7 @@
|
||||
border-left: 1px solid hsla(210,8%,5%,.5);
|
||||
}
|
||||
|
||||
.devtools-toolbarbutton {
|
||||
.splitview-main > .devtools-toolbarbutton {
|
||||
font-size: 11px;
|
||||
padding: 0 8px;
|
||||
width: auto;
|
||||
|
@ -273,6 +273,11 @@
|
||||
|
||||
/* SideMenuWidget */
|
||||
|
||||
.side-menu-widget-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
.side-menu-widget-container[theme="dark"] {
|
||||
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
|
||||
color: #fff;
|
||||
@ -429,6 +434,11 @@
|
||||
|
||||
/* VariablesView */
|
||||
|
||||
.variables-view-container:not([empty]) {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateX(1px);
|
||||
}
|
||||
|
||||
.variables-view-empty-notice {
|
||||
color: GrayText;
|
||||
padding: 2px;
|
||||
@ -443,32 +453,7 @@
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
/* Generic traits applied to both variables and properties */
|
||||
|
||||
.variable-or-property {
|
||||
transition: background 1s ease-in-out, color 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property[changed] {
|
||||
color: black;
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
}
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
/* Custom variables and properties traits */
|
||||
/* Generic variables traits */
|
||||
|
||||
.variables-view-variable {
|
||||
-moz-margin-start: 1px;
|
||||
@ -483,26 +468,70 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Generic variables *and* properties traits */
|
||||
|
||||
.variable-or-property:focus > .title > label {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
.variable-or-property:not([untitled]) > .variables-view-element-details {
|
||||
-moz-margin-start: 10px;
|
||||
}
|
||||
|
||||
/* Traits applied when variables or properties are changed or overridden */
|
||||
|
||||
.variable-or-property:not([overridden]) {
|
||||
transition: background 1s ease-in-out;
|
||||
}
|
||||
|
||||
.variable-or-property:not([overridden])[changed] {
|
||||
transition-duration: .4s;
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] {
|
||||
background: rgba(128,128,128,0.05);
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] .title > label {
|
||||
/* Cross out the title for this variable and all child properties. */
|
||||
font-style: italic;
|
||||
text-decoration: line-through;
|
||||
border-bottom: none !important;
|
||||
color: rgba(128,128,128,0.9);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Traits applied when variables or properties are editable */
|
||||
|
||||
.variable-or-property[editable] > .title > .value {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.variable-or-property[overridden] .title > .value {
|
||||
/* Disallow editing this variable and all child properties. */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Custom configurable/enumerable/writable or frozen/sealed/extensible
|
||||
* variables and properties */
|
||||
|
||||
.variable-or-property[non-enumerable]:not([self]):not([exception]):not([return]):not([scope]) > .title > .name {
|
||||
.variable-or-property[non-enumerable]:not([self]):not([pseudo-item]) > .title > .name {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.variable-or-property[non-configurable] > .title > .name {
|
||||
.variable-or-property[non-configurable]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #99f;
|
||||
}
|
||||
|
||||
.variable-or-property[non-writable] > .title > .name {
|
||||
.variable-or-property[non-writable]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #f99;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > .title > .name {
|
||||
.variable-or-property[safe-getter]:not([pseudo-item]) > .title > .name {
|
||||
border-bottom: 1px dashed #8b0;
|
||||
}
|
||||
|
||||
@ -564,15 +593,20 @@
|
||||
margin: 0 2px 0 2px;
|
||||
}
|
||||
|
||||
.variable-or-property[non-enumerable] > tooltip > label[value=enumerable],
|
||||
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
|
||||
.variable-or-property[non-writable] > tooltip > label[value=writable],
|
||||
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
|
||||
.variable-or-property[non-enumerable] > tooltip > label.enumerable,
|
||||
.variable-or-property[non-configurable] > tooltip > label.configurable,
|
||||
.variable-or-property[non-writable] > tooltip > label.writable,
|
||||
.variable-or-property[non-extensible] > tooltip > label.extensible {
|
||||
color: #800;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > tooltip > label[value=WebIDL] {
|
||||
.variable-or-property[overridden] > tooltip > label.overridden {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-border-start: 1px dotted #000;
|
||||
}
|
||||
|
||||
.variable-or-property[safe-getter] > tooltip > label.WebIDL {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-border-start: 1px dotted #000;
|
||||
color: #080;
|
||||
@ -607,7 +641,7 @@
|
||||
}
|
||||
|
||||
.element-value-input {
|
||||
-moz-margin-start: 4px !important;
|
||||
-moz-margin-start: -2px !important;
|
||||
-moz-margin-end: 2px !important;
|
||||
}
|
||||
|
||||
@ -620,6 +654,7 @@
|
||||
.element-value-input,
|
||||
.element-name-input {
|
||||
border: 1px solid rgba(128, 128, 128, .5) !important;
|
||||
border-radius: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=938023
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var index = -1;
|
||||
var todayDate = new Date();
|
||||
var baseServeURL = "http://mochi.test:8888/tests/dom/downloads/tests/";
|
||||
var baseDownloadPath = "/mnt/sdcard/downloads/";
|
||||
var lastKnownCurrentBytes = 0;
|
||||
|
||||
function next() {
|
||||
index += 1;
|
||||
@ -38,16 +42,58 @@ function next() {
|
||||
}
|
||||
}
|
||||
|
||||
function checkConsistentDownloadAttributes(download) {
|
||||
var href = document.getElementById("download1").getAttribute("href");
|
||||
var expectedServeURL = baseServeURL + href;
|
||||
var expectedDownloadPath = baseDownloadPath + "test.bin";
|
||||
|
||||
// bug 945323: Download path isn't honoring download attribute
|
||||
todo(download.path === expectedDownloadPath,
|
||||
"Download path = " + expectedDownloadPath);
|
||||
|
||||
// bug 948287: Accessing startTime attribute at download start fires
|
||||
// NS_ERROR_UNEXPECTED in emulator
|
||||
//ok(download.startTime >= todayDate,
|
||||
// "Download start time should be greater than or equal to today");
|
||||
|
||||
// bug 945366: Accessing error attribute at download start fires
|
||||
// NS_ERROR_UNEXPECTED in emulator
|
||||
//is(download.error, null, "Download does not have an error");
|
||||
|
||||
is(download.url, expectedServeURL,
|
||||
"Download URL = " + expectedServeURL);
|
||||
ok(download.id !== null, "Download id is defined");
|
||||
is(download.contentType, "application/octet-stream",
|
||||
"Download content type is application/octet-stream");
|
||||
}
|
||||
|
||||
function downloadChange(evt) {
|
||||
var download = evt.download;
|
||||
if (download.state == "succeeded") {
|
||||
ok(download.totalBytes == 1024, "Download size is 1024 bytes.");
|
||||
ok(download.contentType == "application/octet-stream",
|
||||
"contentType is application/octet-stream.");
|
||||
checkConsistentDownloadAttributes(download);
|
||||
is(download.totalBytes, 1024, "Download total size is 1024 bytes");
|
||||
|
||||
if (download.state === "succeeded") {
|
||||
is(download.currentBytes, 1024, "Download current size is 1024 bytes");
|
||||
SimpleTest.finish();
|
||||
} else if (download.state === "downloading") {
|
||||
ok(download.currentBytes > lastKnownCurrentBytes,
|
||||
"Download current size is larger than last download change event");
|
||||
lastKnownCurrentBytes = download.currentBytes;
|
||||
} else {
|
||||
ok(false, "Unexpected download state = " + download.state);
|
||||
}
|
||||
}
|
||||
|
||||
function downloadStart(evt) {
|
||||
var download = evt.download;
|
||||
checkConsistentDownloadAttributes(download);
|
||||
|
||||
is(download.currentBytes, 0, "Download current size is zero");
|
||||
is(download.state, "downloading", "Download state is downloading");
|
||||
|
||||
download.onstatechange = downloadChange;
|
||||
}
|
||||
|
||||
var steps = [
|
||||
// Start by setting the pref to true.
|
||||
function() {
|
||||
@ -61,11 +107,7 @@ var steps = [
|
||||
SpecialPowers.pushPermissions([
|
||||
{type: "downloads", allow: true, context: document}
|
||||
], function() {
|
||||
navigator.mozDownloadManager.ondownloadstart =
|
||||
function(evt) {
|
||||
ok(true, "Download started");
|
||||
evt.download.addEventListener("statechange", downloadChange);
|
||||
}
|
||||
navigator.mozDownloadManager.ondownloadstart = downloadStart;
|
||||
next();
|
||||
});
|
||||
},
|
||||
@ -82,3 +124,4 @@ next();
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -572,6 +572,11 @@ pref("media.preload.auto", 2); // preload metadata if preload=auto
|
||||
pref("image.mem.decodeondraw", true);
|
||||
pref("image.mem.min_discard_timeout_ms", 10000);
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// Shumway component (SWF player) is disabled by default. Also see bug 904346.
|
||||
pref("shumway.disabled", true);
|
||||
#endif
|
||||
|
||||
// enable touch events interfaces
|
||||
pref("dom.w3c_touch_events.enabled", 1);
|
||||
|
||||
|
@ -1467,7 +1467,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
animator.setUseHardwareLayer(false);
|
||||
|
||||
mBrowserToolbar.startEditing(url, animator);
|
||||
showHomePagerWithAnimator(HomePager.Page.TOP_SITES, animator);
|
||||
showHomePagerWithAnimator(animator);
|
||||
|
||||
animator.start();
|
||||
}
|
||||
@ -1618,16 +1618,12 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
private void showHomePager(HomePager.Page page) {
|
||||
showHomePagerWithAnimator(page, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocaleReady(final String locale) {
|
||||
super.onLocaleReady(locale);
|
||||
if (mHomePager != null) {
|
||||
if (isHomePagerVisible()) {
|
||||
// Blow it away and rebuild it with the right strings.
|
||||
mHomePager.redisplay(getSupportFragmentManager());
|
||||
mHomePager.redisplay(getSupportLoaderManager(), getSupportFragmentManager());
|
||||
}
|
||||
|
||||
if (mMenu != null) {
|
||||
@ -1636,6 +1632,16 @@ abstract public class BrowserApp extends GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
private void showHomePager(HomePager.Page page) {
|
||||
showHomePagerWithAnimator(page, null);
|
||||
}
|
||||
|
||||
private void showHomePagerWithAnimator(PropertyAnimator animator) {
|
||||
// Passing null here means the default page will be defined
|
||||
// by the HomePager's configuration.
|
||||
showHomePagerWithAnimator(null, animator);
|
||||
}
|
||||
|
||||
private void showHomePagerWithAnimator(HomePager.Page page, PropertyAnimator animator) {
|
||||
if (isHomePagerVisible()) {
|
||||
return;
|
||||
@ -1655,7 +1661,9 @@ abstract public class BrowserApp extends GeckoApp
|
||||
mHomePager = (HomePager) homePagerStub.inflate();
|
||||
}
|
||||
|
||||
mHomePager.show(getSupportFragmentManager(), page, animator);
|
||||
mHomePager.show(getSupportLoaderManager(),
|
||||
getSupportFragmentManager(),
|
||||
page, animator);
|
||||
|
||||
// Hide the web content so it cannot be focused by screen readers.
|
||||
hideWebContentOnPropertyAnimationEnd(animator);
|
||||
|
@ -95,7 +95,7 @@ public class Tab {
|
||||
mUserSearch = "";
|
||||
mExternal = external;
|
||||
mParentId = parentId;
|
||||
mAboutHomePage = HomePager.Page.TOP_SITES;
|
||||
mAboutHomePage = null;
|
||||
mTitle = title == null ? "" : title;
|
||||
mFavicon = null;
|
||||
mFaviconUrl = null;
|
||||
@ -155,7 +155,6 @@ public class Tab {
|
||||
mAboutHomePage = page;
|
||||
}
|
||||
|
||||
|
||||
// may be null if user-entered query hasn't yet been resolved to a URI
|
||||
public synchronized String getURL() {
|
||||
return mUrl;
|
||||
@ -660,6 +659,8 @@ public class Tab {
|
||||
final String homePage = message.getString("aboutHomePage");
|
||||
if (!TextUtils.isEmpty(homePage)) {
|
||||
setAboutHomePage(HomePager.Page.valueOf(homePage));
|
||||
} else {
|
||||
setAboutHomePage(null);
|
||||
}
|
||||
|
||||
Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, oldUrl);
|
||||
|
@ -475,7 +475,7 @@ class JavaPanZoomController
|
||||
|
||||
case TOUCHING:
|
||||
// Don't allow panning if there is an element in full-screen mode. See bug 775511.
|
||||
if (mTarget.isFullScreen() || panDistance(event) < PAN_THRESHOLD) {
|
||||
if ((mTarget.isFullScreen() && !mSubscroller.scrolling()) || panDistance(event) < PAN_THRESHOLD) {
|
||||
return false;
|
||||
}
|
||||
cancelTouch();
|
||||
|
191
mobile/android/base/home/HomeAdapter.java
Normal file
@ -0,0 +1,191 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageType;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
import org.mozilla.gecko.home.HomePager.Page;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
class HomeAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayList<PageInfo> mPageInfos;
|
||||
private final HashMap<String, Fragment> mPages;
|
||||
|
||||
private boolean mCanLoadHint;
|
||||
|
||||
private OnAddPageListener mAddPageListener;
|
||||
|
||||
interface OnAddPageListener {
|
||||
public void onAddPage(String title);
|
||||
}
|
||||
|
||||
public HomeAdapter(Context context, FragmentManager fm) {
|
||||
super(fm);
|
||||
|
||||
mContext = context;
|
||||
mCanLoadHint = HomeFragment.DEFAULT_CAN_LOAD_HINT;
|
||||
|
||||
mPageInfos = new ArrayList<PageInfo>();
|
||||
mPages = new HashMap<String, Fragment>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mPageInfos.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
PageInfo info = mPageInfos.get(position);
|
||||
return Fragment.instantiate(mContext, info.getClassName(), info.getArgs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
if (mPageInfos.size() > 0) {
|
||||
PageInfo info = mPageInfos.get(position);
|
||||
return info.getTitle().toUpperCase();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
Fragment fragment = (Fragment) super.instantiateItem(container, position);
|
||||
mPages.put(mPageInfos.get(position).getId(), fragment);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
super.destroyItem(container, position, object);
|
||||
mPages.remove(mPageInfos.get(position).getId());
|
||||
}
|
||||
|
||||
public void setOnAddPageListener(OnAddPageListener listener) {
|
||||
mAddPageListener = listener;
|
||||
}
|
||||
|
||||
public int getItemPosition(Page page) {
|
||||
for (int i = 0; i < mPageInfos.size(); i++) {
|
||||
final Page infoPage = mPageInfos.get(i).toPage();
|
||||
if (infoPage == page) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Page getPageAtPosition(int position) {
|
||||
// getPageAtPosition() might be called before HomeAdapter
|
||||
// has got its initial list of PageEntries. Just bail.
|
||||
if (mPageInfos.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PageInfo info = mPageInfos.get(position);
|
||||
return info.toPage();
|
||||
}
|
||||
|
||||
private void addPage(PageInfo info) {
|
||||
mPageInfos.add(info);
|
||||
|
||||
if (mAddPageListener != null) {
|
||||
mAddPageListener.onAddPage(info.getTitle());
|
||||
}
|
||||
}
|
||||
|
||||
public void update(List<PageEntry> pageEntries) {
|
||||
mPages.clear();
|
||||
mPageInfos.clear();
|
||||
|
||||
if (pageEntries != null) {
|
||||
for (PageEntry pageEntry : pageEntries) {
|
||||
final PageInfo info = new PageInfo(pageEntry);
|
||||
addPage(info);
|
||||
}
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public boolean getCanLoadHint() {
|
||||
return mCanLoadHint;
|
||||
}
|
||||
|
||||
public void setCanLoadHint(boolean canLoadHint) {
|
||||
// We cache the last hint value so that we can use it when
|
||||
// creating new pages. See PageInfo.getArgs().
|
||||
mCanLoadHint = canLoadHint;
|
||||
|
||||
// Enable/disable loading on all existing pages
|
||||
for (Fragment page : mPages.values()) {
|
||||
final HomeFragment homePage = (HomeFragment) page;
|
||||
homePage.setCanLoadHint(canLoadHint);
|
||||
}
|
||||
}
|
||||
|
||||
private final class PageInfo {
|
||||
private final String mId;
|
||||
private final PageEntry mPageEntry;
|
||||
|
||||
PageInfo(PageEntry pageEntry) {
|
||||
mId = pageEntry.getType() + "-" + pageEntry.getId();
|
||||
mPageEntry = pageEntry;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mPageEntry.getTitle();
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
final PageType type = mPageEntry.getType();
|
||||
return type.getPageClass().getName();
|
||||
}
|
||||
|
||||
public Bundle getArgs() {
|
||||
final Bundle args = new Bundle();
|
||||
|
||||
args.putBoolean(HomePager.CAN_LOAD_ARG, mCanLoadHint);
|
||||
|
||||
// Only list pages need the page entry
|
||||
if (mPageEntry.getType() == PageType.LIST) {
|
||||
args.putParcelable(HomePager.PAGE_ENTRY_ARG, mPageEntry);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
public Page toPage() {
|
||||
final PageType type = mPageEntry.getType();
|
||||
if (type == PageType.LIST) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Page.valueOf(type);
|
||||
}
|
||||
}
|
||||
}
|
224
mobile/android/base/home/HomeConfig.java
Normal file
@ -0,0 +1,224 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.home.HomePager.Page;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
final class HomeConfig {
|
||||
public static enum PageType implements Parcelable {
|
||||
TOP_SITES("top_sites", TopSitesPage.class),
|
||||
BOOKMARKS("bookmarks", BookmarksPage.class),
|
||||
HISTORY("history", HistoryPage.class),
|
||||
READING_LIST("reading_list", ReadingListPage.class),
|
||||
LIST("list", ListPage.class);
|
||||
|
||||
private final String mId;
|
||||
private final Class<?> mPageClass;
|
||||
|
||||
PageType(String id, Class<?> pageClass) {
|
||||
mId = id;
|
||||
mPageClass = pageClass;
|
||||
}
|
||||
|
||||
public static PageType valueOf(Page page) {
|
||||
switch(page) {
|
||||
case TOP_SITES:
|
||||
return PageType.TOP_SITES;
|
||||
|
||||
case BOOKMARKS:
|
||||
return PageType.BOOKMARKS;
|
||||
|
||||
case HISTORY:
|
||||
return PageType.HISTORY;
|
||||
|
||||
case READING_LIST:
|
||||
return PageType.READING_LIST;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not convert unrecognized Page");
|
||||
}
|
||||
}
|
||||
|
||||
public static PageType fromId(String id) {
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Could not convert null String to PageType");
|
||||
}
|
||||
|
||||
for (PageType page : PageType.values()) {
|
||||
if (TextUtils.equals(page.mId, id.toLowerCase())) {
|
||||
return page;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Could not convert String id to PageType");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public Class<?> getPageClass() {
|
||||
return mPageClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
|
||||
public static final Creator<PageType> CREATOR = new Creator<PageType>() {
|
||||
@Override
|
||||
public PageType createFromParcel(final Parcel source) {
|
||||
return PageType.values()[source.readInt()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageType[] newArray(final int size) {
|
||||
return new PageType[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class PageEntry implements Parcelable {
|
||||
private final PageType mType;
|
||||
private final String mTitle;
|
||||
private final String mId;
|
||||
private final EnumSet<Flags> mFlags;
|
||||
|
||||
public enum Flags {
|
||||
DEFAULT_PAGE
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PageEntry(Parcel in) {
|
||||
mType = (PageType) in.readParcelable(getClass().getClassLoader());
|
||||
mTitle = in.readString();
|
||||
mId = in.readString();
|
||||
mFlags = (EnumSet<Flags>) in.readSerializable();
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title) {
|
||||
this(type, title, EnumSet.noneOf(Flags.class));
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title, EnumSet<Flags> flags) {
|
||||
this(type, title, type.toString(), flags);
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title, String id) {
|
||||
this(type, title, id, EnumSet.noneOf(Flags.class));
|
||||
}
|
||||
|
||||
public PageEntry(PageType type, String title, String id, EnumSet<Flags> flags) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null type");
|
||||
}
|
||||
mType = type;
|
||||
|
||||
if (title == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null title");
|
||||
}
|
||||
mTitle = title;
|
||||
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null id");
|
||||
}
|
||||
mId = id;
|
||||
|
||||
if (flags == null) {
|
||||
throw new IllegalArgumentException("Can't create PageEntry with null flags");
|
||||
}
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
public PageType getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public boolean isDefault() {
|
||||
return mFlags.contains(Flags.DEFAULT_PAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelable(mType, 0);
|
||||
dest.writeString(mTitle);
|
||||
dest.writeString(mId);
|
||||
dest.writeSerializable(mFlags);
|
||||
}
|
||||
|
||||
public static final Creator<PageEntry> CREATOR = new Creator<PageEntry>() {
|
||||
@Override
|
||||
public PageEntry createFromParcel(final Parcel in) {
|
||||
return new PageEntry(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageEntry[] newArray(final int size) {
|
||||
return new PageEntry[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface OnChangeListener {
|
||||
public void onChange();
|
||||
}
|
||||
|
||||
public interface HomeConfigBackend {
|
||||
public List<PageEntry> load();
|
||||
public void save(List<PageEntry> entries);
|
||||
public void setOnChangeListener(OnChangeListener listener);
|
||||
}
|
||||
|
||||
private final HomeConfigBackend mBackend;
|
||||
|
||||
public HomeConfig(HomeConfigBackend backend) {
|
||||
mBackend = backend;
|
||||
}
|
||||
|
||||
public List<PageEntry> load() {
|
||||
return mBackend.load();
|
||||
}
|
||||
|
||||
public void save(List<PageEntry> entries) {
|
||||
mBackend.save(entries);
|
||||
}
|
||||
|
||||
public void setOnChangeListener(OnChangeListener listener) {
|
||||
mBackend.setOnChangeListener(listener);
|
||||
}
|
||||
|
||||
public static HomeConfig getDefault(Context context) {
|
||||
return new HomeConfig(new HomeConfigMemBackend(context));
|
||||
}
|
||||
}
|
83
mobile/android/base/home/HomeConfigLoader.java
Normal file
@ -0,0 +1,83 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.OnChangeListener;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HomeConfigLoader extends AsyncTaskLoader<List<PageEntry>> {
|
||||
private final HomeConfig mConfig;
|
||||
private List<PageEntry> mPageEntries;
|
||||
|
||||
public HomeConfigLoader(Context context, HomeConfig homeConfig) {
|
||||
super(context);
|
||||
mConfig = homeConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PageEntry> loadInBackground() {
|
||||
return mConfig.load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deliverResult(List<PageEntry> pageEntries) {
|
||||
if (isReset()) {
|
||||
mPageEntries = null;
|
||||
return;
|
||||
}
|
||||
|
||||
mPageEntries = pageEntries;
|
||||
mConfig.setOnChangeListener(new ForceLoadChangeListener());
|
||||
|
||||
if (isStarted()) {
|
||||
super.deliverResult(pageEntries);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartLoading() {
|
||||
if (mPageEntries != null) {
|
||||
deliverResult(mPageEntries);
|
||||
}
|
||||
|
||||
if (takeContentChanged() || mPageEntries == null) {
|
||||
forceLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopLoading() {
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(List<PageEntry> pageEntries) {
|
||||
mPageEntries = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReset() {
|
||||
super.onReset();
|
||||
|
||||
// Ensure the loader is stopped.
|
||||
onStopLoading();
|
||||
|
||||
mPageEntries = null;
|
||||
mConfig.setOnChangeListener(null);
|
||||
}
|
||||
|
||||
private class ForceLoadChangeListener implements OnChangeListener {
|
||||
@Override
|
||||
public void onChange() {
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
}
|
67
mobile/android/base/home/HomeConfigMemBackend.java
Normal file
@ -0,0 +1,67 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.home.HomeConfig.HomeConfigBackend;
|
||||
import org.mozilla.gecko.home.HomeConfig.OnChangeListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageType;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
class HomeConfigMemBackend implements HomeConfigBackend {
|
||||
private final Context mContext;
|
||||
|
||||
public HomeConfigMemBackend(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public List<PageEntry> load() {
|
||||
final ArrayList<PageEntry> pageEntries = new ArrayList<PageEntry>();
|
||||
|
||||
pageEntries.add(new PageEntry(PageType.TOP_SITES,
|
||||
mContext.getString(R.string.home_top_sites_title),
|
||||
EnumSet.of(PageEntry.Flags.DEFAULT_PAGE)));
|
||||
|
||||
pageEntries.add(new PageEntry(PageType.BOOKMARKS,
|
||||
mContext.getString(R.string.bookmarks_title)));
|
||||
|
||||
// We disable reader mode support on low memory devices. Hence the
|
||||
// reading list page should not show up on such devices.
|
||||
if (!HardwareUtils.isLowMemoryPlatform()) {
|
||||
pageEntries.add(new PageEntry(PageType.READING_LIST,
|
||||
mContext.getString(R.string.reading_list_title)));
|
||||
}
|
||||
|
||||
final PageEntry historyEntry = new PageEntry(PageType.HISTORY,
|
||||
mContext.getString(R.string.home_history_title));
|
||||
|
||||
// On tablets, the history page is the last.
|
||||
// On phones, the history page is the first one.
|
||||
if (HardwareUtils.isTablet()) {
|
||||
pageEntries.add(historyEntry);
|
||||
} else {
|
||||
pageEntries.add(0, historyEntry);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(pageEntries);
|
||||
}
|
||||
|
||||
public void save(List<PageEntry> entries) {
|
||||
// This is a static backend, do nothing.
|
||||
}
|
||||
|
||||
public void setOnChangeListener(OnChangeListener listener) {
|
||||
// This is a static backend, do nothing.
|
||||
}
|
||||
}
|
@ -44,6 +44,9 @@ abstract class HomeFragment extends Fragment {
|
||||
// Share MIME type.
|
||||
private static final String SHARE_MIME_TYPE = "text/plain";
|
||||
|
||||
// Default value for "can load" hint
|
||||
static final boolean DEFAULT_CAN_LOAD_HINT = false;
|
||||
|
||||
// Whether the fragment can load its content or not
|
||||
// This is used to defer data loading until the editing
|
||||
// mode animation ends.
|
||||
@ -58,9 +61,9 @@ abstract class HomeFragment extends Fragment {
|
||||
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, false);
|
||||
mCanLoadHint = args.getBoolean(HomePager.CAN_LOAD_ARG, DEFAULT_CAN_LOAD_HINT);
|
||||
} else {
|
||||
mCanLoadHint = false;
|
||||
mCanLoadHint = DEFAULT_CAN_LOAD_HINT;
|
||||
}
|
||||
|
||||
mIsLoaded = false;
|
||||
|
@ -8,6 +8,9 @@ package org.mozilla.gecko.home;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.home.HomeAdapter.OnAddPageListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageType;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
|
||||
@ -16,8 +19,9 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.util.AttributeSet;
|
||||
@ -25,17 +29,24 @@ import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class HomePager extends ViewPager {
|
||||
// Subpage fragment tag
|
||||
public static final String SUBPAGE_TAG = "home_pager_subpage";
|
||||
|
||||
private static final int LOADER_ID_CONFIG = 0;
|
||||
|
||||
private final Context mContext;
|
||||
private volatile boolean mLoaded;
|
||||
private Decor mDecor;
|
||||
private View mTabStrip;
|
||||
|
||||
private final OnAddPageListener mAddPageListener;
|
||||
|
||||
private final HomeConfig mConfig;
|
||||
private ConfigLoaderCallbacks mConfigLoaderCallbacks;
|
||||
|
||||
private Page mInitialPage;
|
||||
|
||||
// List of pages in order.
|
||||
@RobocopTarget
|
||||
@ -43,7 +54,26 @@ public class HomePager extends ViewPager {
|
||||
HISTORY,
|
||||
TOP_SITES,
|
||||
BOOKMARKS,
|
||||
READING_LIST
|
||||
READING_LIST;
|
||||
|
||||
static Page valueOf(PageType page) {
|
||||
switch(page) {
|
||||
case TOP_SITES:
|
||||
return Page.TOP_SITES;
|
||||
|
||||
case BOOKMARKS:
|
||||
return Page.BOOKMARKS;
|
||||
|
||||
case HISTORY:
|
||||
return Page.HISTORY;
|
||||
|
||||
case READING_LIST:
|
||||
return Page.READING_LIST;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Could not convert unrecognized PageType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is mostly used by UI tests to easily fetch
|
||||
@ -56,8 +86,6 @@ public class HomePager extends ViewPager {
|
||||
static final String LIST_TAG_LAST_TABS = "last_tabs";
|
||||
static final String LIST_TAG_BROWSER_SEARCH = "browser_search";
|
||||
|
||||
private EnumMap<Page, Fragment> mPages = new EnumMap<Page, Fragment>(Page.class);
|
||||
|
||||
public interface OnUrlOpenListener {
|
||||
public enum Flags {
|
||||
ALLOW_SWITCH_TO_TAB
|
||||
@ -86,6 +114,7 @@ public class HomePager extends ViewPager {
|
||||
}
|
||||
|
||||
static final String CAN_LOAD_ARG = "canLoad";
|
||||
static final String PAGE_ENTRY_ARG = "pageEntry";
|
||||
|
||||
public HomePager(Context context) {
|
||||
this(context, null);
|
||||
@ -95,6 +124,18 @@ public class HomePager extends ViewPager {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
|
||||
mConfig = HomeConfig.getDefault(mContext);
|
||||
mConfigLoaderCallbacks = new ConfigLoaderCallbacks();
|
||||
|
||||
mAddPageListener = new OnAddPageListener() {
|
||||
@Override
|
||||
public void onAddPage(String title) {
|
||||
if (mDecor != null) {
|
||||
mDecor.onAddPagerView(title);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This is to keep all 4 pages in memory after they are
|
||||
// selected in the pager.
|
||||
setOffscreenPageLimit(3);
|
||||
@ -112,6 +153,15 @@ public class HomePager extends ViewPager {
|
||||
if (child instanceof Decor) {
|
||||
((ViewPager.LayoutParams) params).isDecor = true;
|
||||
mDecor = (Decor) child;
|
||||
mTabStrip = child;
|
||||
|
||||
mDecor.setOnTitleClickListener(new OnTitleClickListener() {
|
||||
@Override
|
||||
public void onTitleClicked(int index) {
|
||||
setCurrentItem(index, true);
|
||||
}
|
||||
});
|
||||
|
||||
setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
@ -126,14 +176,27 @@ public class HomePager extends ViewPager {
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) { }
|
||||
});
|
||||
} else if (child instanceof HomePagerTabStrip) {
|
||||
mTabStrip = child;
|
||||
}
|
||||
|
||||
super.addView(child, index, params);
|
||||
}
|
||||
|
||||
public void redisplay(FragmentManager fm) {
|
||||
final TabsAdapter adapter = (TabsAdapter) getAdapter();
|
||||
show(fm, adapter.getCurrentPage(), null);
|
||||
public void redisplay(LoaderManager lm, FragmentManager fm) {
|
||||
final HomeAdapter adapter = (HomeAdapter) getAdapter();
|
||||
|
||||
// If mInitialPage is non-null, this means the HomePager hasn't
|
||||
// finished loading its config yet. Simply re-show() with the
|
||||
// current target page.
|
||||
final Page currentPage;
|
||||
if (mInitialPage != null) {
|
||||
currentPage = mInitialPage;
|
||||
} else {
|
||||
currentPage = adapter.getPageAtPosition(getCurrentItem());
|
||||
}
|
||||
|
||||
show(lm, fm, currentPage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,38 +204,27 @@ public class HomePager extends ViewPager {
|
||||
*
|
||||
* @param fm FragmentManager for the adapter
|
||||
*/
|
||||
public void show(FragmentManager fm, Page page, PropertyAnimator animator) {
|
||||
public void show(LoaderManager lm, FragmentManager fm, Page page, PropertyAnimator animator) {
|
||||
mLoaded = true;
|
||||
final TabsAdapter adapter = new TabsAdapter(fm);
|
||||
mInitialPage = page;
|
||||
|
||||
// Only animate on post-HC devices, when a non-null animator is given
|
||||
final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11);
|
||||
|
||||
adapter.addTab(Page.TOP_SITES, TopSitesPage.class, new Bundle(),
|
||||
getContext().getString(R.string.home_top_sites_title));
|
||||
adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, new Bundle(),
|
||||
getContext().getString(R.string.bookmarks_title));
|
||||
|
||||
// We disable reader mode support on low memory devices. Hence the
|
||||
// reading list page should not show up on such devices.
|
||||
if (!HardwareUtils.isLowMemoryPlatform()) {
|
||||
adapter.addTab(Page.READING_LIST, ReadingListPage.class, new Bundle(),
|
||||
getContext().getString(R.string.reading_list_title));
|
||||
}
|
||||
|
||||
// On phones, the history tab is the first tab. On tablets, the
|
||||
// history tab is the last tab.
|
||||
adapter.addTab(HardwareUtils.isTablet() ? -1 : 0,
|
||||
Page.HISTORY, HistoryPage.class, new Bundle(),
|
||||
getContext().getString(R.string.home_history_title));
|
||||
|
||||
final HomeAdapter adapter = new HomeAdapter(mContext, fm);
|
||||
adapter.setOnAddPageListener(mAddPageListener);
|
||||
adapter.setCanLoadHint(!shouldAnimate);
|
||||
|
||||
setAdapter(adapter);
|
||||
|
||||
setCurrentItem(adapter.getItemPosition(page), false);
|
||||
setVisibility(VISIBLE);
|
||||
|
||||
// Don't show the tabs strip until we have the
|
||||
// list of pages in place.
|
||||
mTabStrip.setVisibility(View.INVISIBLE);
|
||||
|
||||
// Load list of pages from configuration
|
||||
lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
|
||||
|
||||
if (shouldAnimate) {
|
||||
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
||||
@Override
|
||||
@ -225,122 +277,6 @@ public class HomePager extends ViewPager {
|
||||
}
|
||||
}
|
||||
|
||||
class TabsAdapter extends FragmentStatePagerAdapter
|
||||
implements OnTitleClickListener {
|
||||
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
|
||||
|
||||
final class TabInfo {
|
||||
private final Page page;
|
||||
private final Class<?> clss;
|
||||
private final Bundle args;
|
||||
private final String title;
|
||||
|
||||
TabInfo(Page page, Class<?> clss, Bundle args, String title) {
|
||||
this.page = page;
|
||||
this.clss = clss;
|
||||
this.args = args;
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
public TabsAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
|
||||
if (mDecor != null) {
|
||||
mDecor.removeAllPagerViews();
|
||||
mDecor.setOnTitleClickListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void addTab(Page page, Class<?> clss, Bundle args, String title) {
|
||||
addTab(-1, page, clss, args, title);
|
||||
}
|
||||
|
||||
public void addTab(int index, Page page, Class<?> clss, Bundle args, String title) {
|
||||
TabInfo info = new TabInfo(page, clss, args, title);
|
||||
|
||||
if (index >= 0) {
|
||||
mTabs.add(index, info);
|
||||
} else {
|
||||
mTabs.add(info);
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
|
||||
if (mDecor != null) {
|
||||
mDecor.onAddPagerView(title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTitleClicked(int index) {
|
||||
setCurrentItem(index, true);
|
||||
}
|
||||
|
||||
public int getItemPosition(Page page) {
|
||||
for (int i = 0; i < mTabs.size(); i++) {
|
||||
TabInfo info = mTabs.get(i);
|
||||
if (info.page == page) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Page getCurrentPage() {
|
||||
int currentItem = getCurrentItem();
|
||||
TabInfo info = mTabs.get(currentItem);
|
||||
return info.page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mTabs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
TabInfo info = mTabs.get(position);
|
||||
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
TabInfo info = mTabs.get(position);
|
||||
return info.title.toUpperCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
Fragment fragment = (Fragment) super.instantiateItem(container, position);
|
||||
|
||||
mPages.put(mTabs.get(position).page, fragment);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
super.destroyItem(container, position, object);
|
||||
|
||||
mPages.remove(mTabs.get(position).page);
|
||||
}
|
||||
|
||||
public void setCanLoadHint(boolean canLoadHint) {
|
||||
// Update fragment arguments for future instances
|
||||
for (TabInfo info : mTabs) {
|
||||
info.args.putBoolean(CAN_LOAD_ARG, canLoadHint);
|
||||
}
|
||||
|
||||
// Enable/disable loading on all existing pages
|
||||
for (Fragment page : mPages.values()) {
|
||||
final HomeFragment homePage = (HomeFragment) page;
|
||||
homePage.setCanLoadHint(canLoadHint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
@ -350,4 +286,66 @@ public class HomePager extends ViewPager {
|
||||
|
||||
return super.onInterceptTouchEvent(event);
|
||||
}
|
||||
|
||||
private void updateUiFromPageEntries(List<PageEntry> pageEntries) {
|
||||
// We only care about the adapter if HomePager is currently
|
||||
// loaded, which means it's visible in the activity.
|
||||
if (!mLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDecor != null) {
|
||||
mDecor.removeAllPagerViews();
|
||||
}
|
||||
|
||||
final HomeAdapter adapter = (HomeAdapter) getAdapter();
|
||||
|
||||
// Disable loading until the final current item is defined
|
||||
// after loading the page entries. This is to stop any temporary
|
||||
// active item from loading.
|
||||
boolean originalCanLoadHint = adapter.getCanLoadHint();
|
||||
adapter.setCanLoadHint(false);
|
||||
|
||||
// Update the adapter with the new page entries
|
||||
adapter.update(pageEntries);
|
||||
|
||||
final int count = (pageEntries != null ? pageEntries.size() : 0);
|
||||
mTabStrip.setVisibility(count > 0 ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
// Use the default page as defined in the HomePager's configuration
|
||||
// if the initial page wasn't explicitly set by the show() caller.
|
||||
if (mInitialPage != null) {
|
||||
setCurrentItem(adapter.getItemPosition(mInitialPage), false);
|
||||
mInitialPage = null;
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final PageEntry pageEntry = pageEntries.get(i);
|
||||
if (pageEntry.isDefault()) {
|
||||
setCurrentItem(i, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore canLoadHint now that we have the final
|
||||
// state in HomePager.
|
||||
adapter.setCanLoadHint(originalCanLoadHint);
|
||||
}
|
||||
|
||||
private class ConfigLoaderCallbacks implements LoaderCallbacks<List<PageEntry>> {
|
||||
@Override
|
||||
public Loader<List<PageEntry>> onCreateLoader(int id, Bundle args) {
|
||||
return new HomeConfigLoader(mContext, mConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<List<PageEntry>> loader, List<PageEntry> pageEntries) {
|
||||
updateUiFromPageEntries(pageEntries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<List<PageEntry>> loader) {
|
||||
updateUiFromPageEntries(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
185
mobile/android/base/home/ListPage.java
Normal file
@ -0,0 +1,185 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
||||
import org.mozilla.gecko.home.HomeConfig.PageEntry;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Fragment that displays custom lists.
|
||||
*/
|
||||
public class ListPage extends HomeFragment {
|
||||
// Cursor loader ID for the lists
|
||||
private static final int LOADER_ID_LIST = 0;
|
||||
|
||||
// The page entry associated with this page
|
||||
private PageEntry mPageEntry;
|
||||
|
||||
// Adapter for the list
|
||||
private HomeListAdapter mAdapter;
|
||||
|
||||
// The view shown by the fragment
|
||||
private ListView mList;
|
||||
|
||||
// Callbacks used for the list loader
|
||||
private CursorLoaderCallbacks mCursorLoaderCallbacks;
|
||||
|
||||
// On URL open listener
|
||||
private OnUrlOpenListener mUrlOpenListener;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
||||
try {
|
||||
mUrlOpenListener = (OnUrlOpenListener) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString()
|
||||
+ " must implement HomePager.OnUrlOpenListener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
|
||||
mUrlOpenListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final Bundle args = getArguments();
|
||||
if (args != null) {
|
||||
mPageEntry = (PageEntry) args.getParcelable(HomePager.PAGE_ENTRY_ARG);
|
||||
}
|
||||
|
||||
if (mPageEntry == null) {
|
||||
throw new IllegalStateException("Can't create a ListPage without a PageEntry");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mList = new HomeListView(getActivity());
|
||||
return mList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
registerForContextMenu(mList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mList = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
// Detach and reattach the fragment as the layout changes.
|
||||
if (isVisible()) {
|
||||
getFragmentManager().beginTransaction()
|
||||
.detach(this)
|
||||
.attach(this)
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
mAdapter = new HomeListAdapter(getActivity(), null);
|
||||
mList.setAdapter(mAdapter);
|
||||
|
||||
// Create callbacks before the initial loader is started.
|
||||
mCursorLoaderCallbacks = new CursorLoaderCallbacks();
|
||||
loadIfVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() {
|
||||
getLoaderManager().initLoader(LOADER_ID_LIST, null, mCursorLoaderCallbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor loader for the lists.
|
||||
*/
|
||||
private static class HomeListLoader extends SimpleCursorLoader {
|
||||
public HomeListLoader(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadCursor() {
|
||||
// Do nothing
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursor adapter for the list.
|
||||
*/
|
||||
private class HomeListAdapter extends CursorAdapter {
|
||||
public HomeListAdapter(Context context, Cursor cursor) {
|
||||
super(context, cursor, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
final TwoLinePageRow row = (TwoLinePageRow) view;
|
||||
row.updateFromCursor(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item_row, parent, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LoaderCallbacks implementation that interacts with the LoaderManager.
|
||||
*/
|
||||
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new HomeListLoader(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
|
||||
mAdapter.swapCursor(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
mAdapter.swapCursor(null);
|
||||
}
|
||||
}
|
||||
}
|
@ -208,12 +208,17 @@ gbjar.sources += [
|
||||
'home/BrowserSearch.java',
|
||||
'home/FadedTextView.java',
|
||||
'home/HistoryPage.java',
|
||||
'home/HomeAdapter.java',
|
||||
'home/HomeBanner.java',
|
||||
'home/HomeConfig.java',
|
||||
'home/HomeConfigLoader.java',
|
||||
'home/HomeConfigMemBackend.java',
|
||||
'home/HomeFragment.java',
|
||||
'home/HomeListView.java',
|
||||
'home/HomePager.java',
|
||||
'home/HomePagerTabStrip.java',
|
||||
'home/LastTabsPage.java',
|
||||
'home/ListPage.java',
|
||||
'home/MostRecentPage.java',
|
||||
'home/MultiTypeCursorAdapter.java',
|
||||
'home/PinSiteDialog.java',
|
||||
|
@ -52,6 +52,13 @@ public class StringHelper {
|
||||
"Add to Home Screen"
|
||||
};
|
||||
|
||||
public static final String[] CONTEXT_MENU_ITEMS_IN_URL_BAR = new String[] {
|
||||
"Share",
|
||||
"Copy Address",
|
||||
"Edit Site Settings",
|
||||
"Add to Home Screen"
|
||||
};
|
||||
|
||||
public static final String TITLE_PLACE_HOLDER = "Enter Search or Address";
|
||||
|
||||
// Robocop page urls
|
||||
|
@ -1,8 +1,17 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import android.view.View;
|
||||
import org.mozilla.gecko.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This patch tests the clear private data options:
|
||||
* - clear history option by: adding and checking that clear private
|
||||
* data option removes the history items but not the users bookmarks
|
||||
* - clear site settings and clear saved password by: checking
|
||||
* each option present in the doorhanger and clearing the settings from
|
||||
* the URL bar context menu and settings menu
|
||||
*/
|
||||
|
||||
public class testClearPrivateData extends PixelTest {
|
||||
private final int TEST_WAIT_MS = 10000;
|
||||
|
||||
@ -14,21 +23,25 @@ public class testClearPrivateData extends PixelTest {
|
||||
public void testClearPrivateData() {
|
||||
blockForGeckoReady();
|
||||
clearHistory();
|
||||
clearSiteSettings();
|
||||
clearPassword();
|
||||
}
|
||||
|
||||
private void clearHistory() {
|
||||
|
||||
// Loading a page and adding a second one as bookmark to have user made bookmarks and history
|
||||
String blank1 = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
String blank2 = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_02_URL);
|
||||
|
||||
loadAndPaint(blank1);
|
||||
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
|
||||
|
||||
String title = StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE;
|
||||
inputAndLoadUrl(blank1);
|
||||
verifyPageTitle(title);
|
||||
mDatabaseHelper.addOrUpdateMobileBookmark(StringHelper.ROBOCOP_BLANK_PAGE_02_TITLE, blank2);
|
||||
|
||||
// Checking that the history list is not empty
|
||||
verifyHistoryCount(1);
|
||||
clearPrivateData();
|
||||
|
||||
//clear and check for device
|
||||
checkDevice(title);
|
||||
|
||||
// Checking that history list is empty
|
||||
verifyHistoryCount(0);
|
||||
@ -45,4 +58,60 @@ public class testClearPrivateData extends PixelTest {
|
||||
}, TEST_WAIT_MS);
|
||||
mAsserter.ok(match, "Checking that the number of history items is correct", String.valueOf(expectedCount) + " history items present in the database");
|
||||
}
|
||||
|
||||
public void clearSiteSettings() {
|
||||
String shareStrings[] = {"Share your location with", "Share", "Don't share", "There are no settings to clear"};
|
||||
String titleGeolocation = StringHelper.ROBOCOP_GEOLOCATION_TITLE;
|
||||
String url = getAbsoluteUrl(StringHelper.ROBOCOP_GEOLOCATION_URL);
|
||||
loadCheckDismiss(shareStrings[1], url, shareStrings[0]);
|
||||
checkOption(shareStrings[1], "Clear");
|
||||
checkOption(shareStrings[3], "Cancel");
|
||||
loadCheckDismiss(shareStrings[2], url, shareStrings[0]);
|
||||
checkOption(shareStrings[2], "Cancel");
|
||||
checkDevice(titleGeolocation);
|
||||
}
|
||||
|
||||
public void clearPassword(){
|
||||
String passwordStrings[] = {"Save password", "Save", "Don't save"};
|
||||
String title = StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE;
|
||||
String loginUrl = getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_URL);
|
||||
loadCheckDismiss(passwordStrings[1], loginUrl, passwordStrings[0]);
|
||||
checkOption(passwordStrings[1], "Clear");
|
||||
loadCheckDismiss(passwordStrings[2], loginUrl, passwordStrings[0]);
|
||||
checkDevice(title);
|
||||
}
|
||||
|
||||
// clear private data and verify the device type because for phone there is an extra back action to exit the settings menu
|
||||
public void checkDevice(String title) {
|
||||
clearPrivateData();
|
||||
if (mDevice.type.equals("phone")) {
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
mAsserter.ok(waitForText(StringHelper.PRIVACY_SECTION_LABEL), "waiting to perform one back", "one back");
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
verifyPageTitle(title);
|
||||
}
|
||||
else {
|
||||
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
|
||||
verifyPageTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
// Load a URL, verify that the doorhanger appears and dismiss it
|
||||
public void loadCheckDismiss(String option, String url, String message) {
|
||||
inputAndLoadUrl(url);
|
||||
waitForText(message);
|
||||
mAsserter.is(mSolo.searchText(message), true, "Doorhanger:" + message + " has been displayed");
|
||||
mSolo.clickOnButton(option);
|
||||
mAsserter.is(mSolo.searchText(message), false, "Doorhanger:" + message + " has been hidden");
|
||||
}
|
||||
|
||||
//Verify if there are settings to be clear if so clear them from the URL bar context menu
|
||||
public void checkOption(String option, String button) {
|
||||
final View toolbarView = mSolo.getView("browser_toolbar");
|
||||
mSolo.clickLongOnView(toolbarView);
|
||||
mAsserter.ok(waitForText(StringHelper.CONTEXT_MENU_ITEMS_IN_URL_BAR[2]), "Waiting for the pop-up to open", "Pop up was openend");
|
||||
mSolo.clickOnText(StringHelper.CONTEXT_MENU_ITEMS_IN_URL_BAR[2]);
|
||||
mAsserter.ok(waitForText(option), "Verify that the option: " + option + " is in the list", "The option is in the list. There are settings to clear");
|
||||
mSolo.clickOnButton(button);
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ShumwayUtils",
|
||||
"resource://shumway/ShumwayUtils.jsm");
|
||||
#endif
|
||||
|
||||
// Lazily-loaded browser scripts:
|
||||
[
|
||||
["SelectHelper", "chrome://browser/content/SelectHelper.js"],
|
||||
@ -341,6 +346,9 @@ var BrowserApp = {
|
||||
#ifdef ACCESSIBILITY
|
||||
AccessFu.attach(window);
|
||||
#endif
|
||||
#ifdef NIGHTLY_BUILD
|
||||
ShumwayUtils.init();
|
||||
#endif
|
||||
|
||||
// Init LoginManager
|
||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
|
27
mobile/android/extensions/Makefile.in
Normal file
@ -0,0 +1,27 @@
|
||||
# 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/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
SHUMWAY_BROWSER_EXTENSION = $(topsrcdir)/browser/extensions/shumway
|
||||
|
||||
exclude_files = \
|
||||
test \
|
||||
$(NULL)
|
||||
|
||||
ifdef NIGHTLY_BUILD
|
||||
$(FINAL_TARGET)/chrome/shumway.manifest: $(GLOBAL_DEPS)
|
||||
$(call py_action,buildlist,$@ "manifest shumway/chrome.manifest")
|
||||
|
||||
libs:: $(SHUMWAY_BROWSER_EXTENSION) $(GLOBAL_DEPS)
|
||||
$(PYTHON) $(topsrcdir)/config/nsinstall.py \
|
||||
$(SHUMWAY_BROWSER_EXTENSION) \
|
||||
$(foreach exclude,$(exclude_files), -X $(SHUMWAY_BROWSER_EXTENSION)/$(exclude)) \
|
||||
$(FINAL_TARGET)/chrome
|
||||
|
||||
libs:: $(FINAL_TARGET)/chrome/shumway.manifest
|
||||
$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest "manifest chrome/shumway.manifest")
|
||||
endif
|
||||
|
||||
|
6
mobile/android/extensions/moz.build
Normal file
@ -0,0 +1,6 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
@ -442,6 +442,10 @@
|
||||
; [Browser Chrome Files]
|
||||
@BINPATH@/chrome/browser@JAREXT@
|
||||
@BINPATH@/chrome/browser.manifest
|
||||
#ifdef NIGHTLY_BUILD
|
||||
@BINPATH@/chrome/shumway.manifest
|
||||
@BINPATH@/chrome/shumway/*
|
||||
#endif
|
||||
@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
|
||||
@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
|
||||
@BINPATH@/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/preview.png
|
||||
|
@ -17,6 +17,7 @@ DIRS += [
|
||||
'app',
|
||||
'fonts',
|
||||
'geckoview_library',
|
||||
'extensions',
|
||||
]
|
||||
|
||||
if not CONFIG['LIBXUL_SDK']:
|
||||
|
@ -130,6 +130,14 @@ InternalMethods.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
signOut: function signOut() {
|
||||
this.abortExistingFlow();
|
||||
this.signedInUser = null; // clear in-memory cache
|
||||
return this.signedInUserStorage.set(null).then(() => {
|
||||
this.notifyObservers("fxaccounts:onlogout");
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch encryption keys for the signed-in-user from the FxA API server.
|
||||
*
|
||||
@ -582,11 +590,7 @@ this.FxAccounts.prototype = Object.freeze({
|
||||
* The promise is rejected if a storage error occurs.
|
||||
*/
|
||||
signOut: function signOut() {
|
||||
internal.abortExistingFlow();
|
||||
internal.signedInUser = null; // clear in-memory cache
|
||||
return internal.signedInUserStorage.set(null).then(() => {
|
||||
internal.notifyObservers("fxaccounts:onlogout");
|
||||
});
|
||||
return internal.signOut();
|
||||
},
|
||||
|
||||
// Return the URI of the remote UI flows.
|
||||
|
@ -291,6 +291,29 @@ add_task(function test_getKeys() {
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* getKeys with no keyFetchToken should trigger signOut
|
||||
*/
|
||||
add_test(function test_getKeys_no_token() {
|
||||
do_test_pending();
|
||||
|
||||
let fxa = new MockFxAccounts();
|
||||
let user = getTestUser("lettuce.protheroe");
|
||||
delete user.keyFetchToken
|
||||
|
||||
makeObserver("fxaccounts:onlogout", function() {
|
||||
log.debug("test_getKeys_no_token observed logout");
|
||||
fxa.internal.getUserAccountData().then(user => {
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
fxa.setSignedInUser(user).then((user) => {
|
||||
fxa.internal.getKeys();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Alice (User A) signs up but never verifies her email. Then Bob (User B)
|
||||
* signs in with a verified email. Ensure that no sign-in events are triggered
|
||||
|
@ -129,14 +129,6 @@
|
||||
"dom/imptests/editing/conformancetest/test_runtest.html": "",
|
||||
"dom/imptests/editing/selecttest/test_addRange.html": "bug 775227",
|
||||
"dom/imptests/html/webgl": "WebGL",
|
||||
"dom/imptests/html/dom/ranges/test_Range-cloneContents.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-compareBoundaryPoints.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-deleteContents.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-extractContents.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-insertNode.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-mutations.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-set.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-surroundContents.html": "bug 775227",
|
||||
"dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html": "bug 775227",
|
||||
"dom/indexedDB/ipc/test_ipc.html": "bug 783513",
|
||||
"dom/indexedDB/test/test_third_party.html": "TIMED_OUT",
|
||||
|
@ -206,14 +206,6 @@
|
||||
"dom/imptests/editing/conformancetest/test_runtest.html": "",
|
||||
"dom/imptests/editing/selecttest/test_addRange.html": "bug 775227",
|
||||
"dom/imptests/html/webgl": "WebGL",
|
||||
"dom/imptests/html/dom/ranges/test_Range-cloneContents.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-compareBoundaryPoints.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-deleteContents.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-extractContents.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-insertNode.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-mutations.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-set.html": "bug 775227",
|
||||
"dom/imptests/html/dom/ranges/test_Range-surroundContents.html": "bug 775227",
|
||||
"dom/imptests/webapps/WebStorage/tests/submissions/Infraware/test_storage_local_key.html": "bug 775227",
|
||||
"dom/indexedDB/ipc/test_ipc.html": "bug 783513",
|
||||
"dom/indexedDB/test/test_third_party.html": "TIMED_OUT",
|
||||
|
@ -35,7 +35,7 @@ this.SafeBrowsing = {
|
||||
return;
|
||||
}
|
||||
|
||||
Services.prefs.addObserver("browser.safebrowsing", this.readPrefs, false);
|
||||
Services.prefs.addObserver("browser.safebrowsing", this.readPrefs.bind(this), false);
|
||||
this.readPrefs();
|
||||
|
||||
// Register our two types of tables, and add custom Mozilla entries
|
||||
|