Merge m-c to inbound on a CLOSED TREE.

This commit is contained in:
Ryan VanderMeulen 2014-02-03 15:45:49 -05:00
commit f40fe712d0
43 changed files with 689 additions and 400 deletions

View File

@ -36,10 +36,7 @@ let CustomizationHandler = {
let cmd = document.getElementById("cmd_CustomizeToolbars");
cmd.setAttribute("disabled", "true");
let splitter = document.getElementById("urlbar-search-splitter");
if (splitter) {
splitter.parentNode.removeChild(splitter);
}
UpdateUrlbarSearchSplitterState();
CombinedStopReload.uninit();
CombinedBackForward.uninit();

View File

@ -980,8 +980,15 @@ let BookmarkingUI = {
if (widget.overflowed)
return widget.anchor;
return document.getAnonymousElementByAttribute(this.star, "class",
"toolbarbutton-icon");
let star = this.star;
return star ? document.getAnonymousElementByAttribute(star, "class",
"toolbarbutton-icon")
: null;
},
get notifier() {
delete this.notifier;
return this.notifier = document.getElementById("bookmarked-notification-anchor");
},
get broadcaster() {
@ -1087,7 +1094,7 @@ let BookmarkingUI = {
* Handles star styling based on page proxy state changes.
*/
onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
if (!this._shouldUpdateStarState()) {
if (!this._shouldUpdateStarState() || !this.star) {
return;
}
@ -1158,6 +1165,7 @@ let BookmarkingUI = {
},
_hasBookmarksObserver: false,
_itemIds: [],
uninit: function BUI_uninit() {
this._updateBookmarkPageMenuItem(true);
CustomizableUI.removeListener(this);
@ -1232,7 +1240,7 @@ let BookmarkingUI = {
return;
}
if (this._itemIds && this._itemIds.length > 0) {
if (this._itemIds.length > 0) {
this.button.setAttribute("starred", "true");
this.button.setAttribute("buttontooltiptext", this._starredTooltip);
}
@ -1247,7 +1255,7 @@ let BookmarkingUI = {
* to the default (Bookmark This Page) for OS X.
*/
_updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) {
let isStarred = !forceReset && this._itemIds && this._itemIds.length > 0;
let isStarred = !forceReset && this._itemIds.length > 0;
let label = isStarred ? "editlabel" : "bookmarklabel";
this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label));
},
@ -1257,6 +1265,42 @@ let BookmarkingUI = {
PlacesCommandHook.updateBookmarkAllTabsCommand();
},
_showBookmarkedNotification: function BUI_showBookmarkedNotification() {
if (this._notificationTimeout) {
clearTimeout(this._notificationTimeout);
}
if (this.notifier.style.transform == '') {
let buttonRect = this.button.getBoundingClientRect();
let notifierRect = this.notifier.getBoundingClientRect();
let topDiff = buttonRect.top - notifierRect.top;
let leftDiff = buttonRect.left - notifierRect.left;
let heightDiff = buttonRect.height - notifierRect.height;
let widthDiff = buttonRect.width - notifierRect.width;
let translateX = (leftDiff + .5 * widthDiff) + "px";
let translateY = (topDiff + .5 * heightDiff) + "px";
this.notifier.style.transform = "translate(" + translateX + ", " + translateY + ")";
}
let isInBookmarksToolbar = this.button.classList.contains("bookmark-item");
if (isInBookmarksToolbar)
this.notifier.setAttribute("in-bookmarks-toolbar", true);
let isInOverflowPanel = this.button.classList.contains("overflowedItem");
if (!isInOverflowPanel) {
this.notifier.setAttribute("notification", "finish");
this.button.setAttribute("notification", "finish");
}
this._notificationTimeout = setTimeout( () => {
this.notifier.removeAttribute("notification");
this.notifier.removeAttribute("in-bookmarks-toolbar");
this.button.removeAttribute("notification");
this.notifier.style.transform = '';
}, 1000);
},
onCommand: function BUI_onCommand(aEvent) {
if (aEvent.target != aEvent.currentTarget) {
return;
@ -1265,6 +1309,8 @@ let BookmarkingUI = {
// Handle special case when the button is in the panel.
let widget = CustomizableUI.getWidget("bookmarks-menu-button")
.forWindow(window);
let isBookmarked = this._itemIds.length > 0;
if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) {
let view = document.getElementById("PanelUI-bookmarks");
view.addEventListener("ViewShowing", this);
@ -1277,7 +1323,7 @@ let BookmarkingUI = {
if (widget.overflowed) {
// Allow to close the panel if the page is already bookmarked, cause
// we are going to open the edit bookmark panel.
if (this._itemIds.length > 0)
if (isBookmarked)
widget.node.removeAttribute("closemenu");
else
widget.node.setAttribute("closemenu", "none");
@ -1285,7 +1331,9 @@ let BookmarkingUI = {
// Ignore clicks on the star if we are updating its state.
if (!this._pendingStmt) {
PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
if (!isBookmarked)
this._showBookmarkedNotification();
PlacesCommandHook.bookmarkCurrentPage(isBookmarked);
}
},

View File

@ -2123,6 +2123,13 @@ function UpdateUrlbarSearchSplitterState()
var urlbar = document.getElementById("urlbar-container");
var searchbar = document.getElementById("search-container");
if (document.documentElement.getAttribute("customizing") == "true") {
if (splitter) {
splitter.remove();
}
return;
}
// If the splitter is already in the right place, we don't need to do anything:
if (splitter &&
((splitter.nextSibling == searchbar && splitter.previousSibling == urlbar) ||

View File

@ -462,6 +462,12 @@
</vbox>
</hbox>
<hbox id="bookmarked-notification-container" mousethrough="always">
<vbox id="bookmarked-notification-anchor">
<vbox id="bookmarked-notification"/>
</vbox>
</hbox>
<tooltip id="dynamic-shortcut-tooltip"
onpopupshowing="UpdateDynamicShortcutTooltipText(this.triggerNode)">
<label id="dynamic-shortcut-tooltip-label"/>

View File

@ -37,6 +37,7 @@
<toolbarbutton id="PanelUI-quit"
#ifdef XP_WIN
label="&quitApplicationCmdWin.label;"
tooltiptext="&quitApplicationCmdWin.tooltip;"
#else
#ifdef XP_MACOSX
label="&quitApplicationCmdMac.label;"

View File

@ -420,12 +420,11 @@ const PanelUI = {
#ifndef XP_WIN
#ifdef XP_MACOSX
let tooltipId = "quit-button.tooltiptext.mac";
#else
let tooltipId = "quit-button.tooltiptext.linux2";
#endif
let brands = Services.strings.createBundle("chrome://branding/locale/brand.properties");
let stringArgs = [brands.GetStringFromName("brandShortName")];
#else
let tooltipId = "quit-button.tooltiptext.linux";
let stringArgs = [];
#endif
let key = document.getElementById("key_quitApplication");
stringArgs.push(ShortcutUtils.prettifyShortcut(key));

View File

@ -703,6 +703,8 @@ let CustomizableUIInternal = {
// We remove location attributes here to make sure they're gone too when a
// widget is removed from a toolbar to the palette. See bug 930950.
this.removeLocationAttributes(widgetNode);
// We also need to remove the panel context menu if it's there:
this.ensureButtonContextMenu(widgetNode);
widgetNode.removeAttribute("wrap");
if (gPalette.has(aWidgetId) || this.isSpecialWidget(aWidgetId)) {
container.removeChild(widgetNode);
@ -1139,8 +1141,6 @@ let CustomizableUIInternal = {
LOG("handleWidgetCommand");
if (aWidget.type == "button") {
this.maybeAutoHidePanel(aEvent);
if (aWidget.onCommand) {
try {
aWidget.onCommand.call(null, aEvent);
@ -1162,10 +1162,6 @@ let CustomizableUIInternal = {
handleWidgetClick: function(aWidget, aNode, aEvent) {
LOG("handleWidgetClick");
if (aWidget.type == "button") {
this.maybeAutoHidePanel(aEvent);
}
if (aWidget.onClick) {
try {
aWidget.onClick.call(null, aEvent);
@ -1322,7 +1318,7 @@ let CustomizableUIInternal = {
let target = aEvent.originalTarget;
let closemenu = "auto";
let widgetType = "button";
while (target.localName != "panel") {
while (target.parentNode && target.localName != "panel") {
closemenu = target.getAttribute("closemenu");
widgetType = target.getAttribute("widget-type");
if (closemenu == "none" || closemenu == "single" ||

View File

@ -19,6 +19,9 @@ const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
const kMaxTransitionDurationMs = 2000;
const kPanelItemContextMenu = "customizationPanelItemContextMenu";
const kPaletteItemContextMenu = "customizationPaletteItemContextMenu";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/CustomizableUI.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -35,6 +38,8 @@ let gModuleName = "[CustomizeMode]";
let gDisableAnimation = null;
let gDraggingInToolbars;
function CustomizeMode(aWindow) {
if (gDisableAnimation === null) {
gDisableAnimation = Services.prefs.getPrefType(kPrefCustomizationAnimation) == Ci.nsIPrefBranch.PREF_BOOL &&
@ -655,9 +660,6 @@ CustomizeMode.prototype = {
wrapper.setAttribute("flex", aNode.getAttribute("flex"));
}
const kPanelItemContextMenu = "customizationPanelItemContextMenu";
const kPaletteItemContextMenu = "customizationPaletteItemContextMenu";
let contextMenuAttrName = aNode.getAttribute("context") ? "context" :
aNode.getAttribute("contextmenu") ? "contextmenu" : "";
let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
@ -704,6 +706,8 @@ CustomizeMode.prototype = {
aWrapper.removeEventListener("mousedown", this);
aWrapper.removeEventListener("mouseup", this);
let place = aWrapper.getAttribute("place");
let toolbarItem = aWrapper.firstChild;
if (!toolbarItem) {
ERROR("no toolbarItem child for " + aWrapper.tagName + "#" + aWrapper.id);
@ -736,6 +740,8 @@ CustomizeMode.prototype = {
toolbarItem.setAttribute(contextAttrName, wrappedContext);
toolbarItem.removeAttribute("wrapped-contextAttrName");
toolbarItem.removeAttribute("wrapped-context");
} else if (place == "panel") {
toolbarItem.setAttribute("context", kPanelItemContextMenu);
}
if (aWrapper.parentNode) {
@ -1048,6 +1054,8 @@ CustomizeMode.prototype = {
this._dragOffset = {x: aEvent.clientX - itemCenter.x,
y: aEvent.clientY - itemCenter.y};
gDraggingInToolbars = new Set();
// Hack needed so that the dragimage will still show the
// item as it appeared before it was hidden.
this._initializeDragAfterMove = function() {
@ -1058,8 +1066,10 @@ CustomizeMode.prototype = {
item.hidden = true;
this._showPanelCustomizationPlaceholders();
DragPositionManager.start(this.window);
if (!isInToolbar && item.nextSibling) {
this._setDragActive(item.nextSibling, "before", draggedItem.id, false);
if (item.nextSibling) {
this._setDragActive(item.nextSibling, "before", draggedItem.id, isInToolbar);
} else if (isInToolbar && item.previousSibling) {
this._setDragActive(item.previousSibling, "after", draggedItem.id, isInToolbar);
}
}
this._initializeDragAfterMove = null;
@ -1408,7 +1418,7 @@ CustomizeMode.prototype = {
return;
}
if (aItem.hasAttribute("dragover") != aValue) {
if (aItem.getAttribute("dragover") != aValue) {
aItem.setAttribute("dragover", aValue);
let window = aItem.ownerDocument.defaultView;
@ -1416,6 +1426,14 @@ CustomizeMode.prototype = {
if (!aInToolbar) {
this._setGridDragActive(aItem, draggedItem, aValue);
} else {
let targetArea = this._getCustomizableParent(aItem);
let makeSpaceImmediately = false;
if (!gDraggingInToolbars.has(targetArea.id)) {
gDraggingInToolbars.add(targetArea.id);
let draggedWrapper = this.document.getElementById("wrapper-" + aDraggedItemId);
let originArea = this._getCustomizableParent(draggedWrapper);
makeSpaceImmediately = originArea == targetArea;
}
// Calculate width of the item when it'd be dropped in this position
let width = this._getDragItemSize(aItem, draggedItem).width;
let direction = window.getComputedStyle(aItem).direction;
@ -1429,8 +1447,16 @@ CustomizeMode.prototype = {
prop = "borderRightWidth";
otherProp = "border-left-width";
}
if (makeSpaceImmediately) {
aItem.setAttribute("notransition", "true");
}
aItem.style[prop] = width + 'px';
aItem.style.removeProperty(otherProp);
if (makeSpaceImmediately) {
// Force a layout flush:
aItem.getBoundingClientRect();
aItem.removeAttribute("notransition");
}
}
}
},
@ -1441,12 +1467,21 @@ CustomizeMode.prototype = {
}
let isToolbar = CustomizableUI.getAreaType(currentArea.id) == "toolbar";
if (isToolbar) {
if (aNoTransition) {
aItem.setAttribute("notransition", "true");
}
aItem.removeAttribute("dragover");
// Remove both property values in the case that the end padding
// had been set.
aItem.style.removeProperty("border-left-width");
aItem.style.removeProperty("border-right-width");
if (aNoTransition) {
// Force a layout flush:
aItem.getBoundingClientRect();
aItem.removeAttribute("notransition");
}
} else {
aItem.removeAttribute("dragover");
if (aNextItem) {
let nextArea = this._getCustomizableParent(aNextItem);
if (nextArea == currentArea) {

View File

@ -274,6 +274,39 @@ add_task(function() {
yield resetCustomization();
});
// Bug 947586 - After customization, panel items show wrong context menu options
add_task(function() {
yield startCustomizing();
yield endCustomizing();
yield PanelUI.show();
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
let shownContextPromise = contextMenuShown(contextMenu);
let newWindowButton = document.getElementById("new-window-button");
ok(newWindowButton, "new-window-button was found");
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
yield shownContextPromise;
is(PanelUI.panel.state, "open", "The PanelUI should still be open.");
let expectedEntries = [
[".customize-context-moveToToolbar", true],
[".customize-context-removeFromPanel", true],
["---"],
[".viewCustomizeToolbar", true]
];
checkContextMenu(contextMenu, expectedEntries);
let hiddenContextPromise = contextMenuHidden(contextMenu);
contextMenu.hidePopup();
yield hiddenContextPromise;
let hiddenPromise = promisePanelHidden(window);
PanelUI.hide();
yield hiddenPromise;
});
function contextMenuShown(aContextMenu) {
let deferred = Promise.defer();
let win = aContextMenu.ownerDocument.defaultView;

View File

@ -28,6 +28,7 @@ this.EXPORTED_SYMBOLS = ["SessionFile"];
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -35,6 +36,8 @@ Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/osfile/_PromiseWorker.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
@ -125,10 +128,57 @@ let SessionFileInternal = {
_isClosed: false,
read: function () {
return SessionWorker.post("read").then(msg => {
this._recordTelemetry(msg.telemetry);
return msg.ok;
// We must initialize the worker during startup so it will be ready to
// perform the final write. If shutdown happens soon after startup and
// the worker has not started yet we may not write.
// See Bug 964531.
SessionWorker.post("init");
return Task.spawn(function () {
let data = null;
for (let filename of [this.path, this.backupPath]) {
try {
data = yield this._readSessionFile(filename);
break;
} catch (ex if ex == Cr.NS_ERROR_FILE_NOT_FOUND) {
// Ignore exceptions about non-existent files.
}
}
throw new Task.Result(data || "");
}.bind(this));
},
/**
* Read the session file asynchronously.
*
* @param filename
* string The name of the session file.
* @returns {promise}
*/
_readSessionFile: function (path) {
let deferred = Promise.defer();
let file = FileUtils.File(path);
let durationMs = Date.now();
NetUtil.asyncFetch(file, function(inputStream, status) {
if (!Components.isSuccessCode(status)) {
deferred.reject(status);
return;
}
let byteLength = inputStream.available();
let data = NetUtil.readInputStreamToString(inputStream, byteLength,
{ charset: "UTF-8" });
durationMs = Date.now() - durationMs;
deferred.resolve(data);
Telemetry.getHistogramById("FX_SESSION_RESTORE_READ_FILE_MS").add(durationMs);
Telemetry.getHistogramById("FX_SESSION_RESTORE_FILE_SIZE_BYTES").add(byteLength);
});
return deferred.promise;
},
gatherTelemetry: function(aStateString) {

View File

@ -67,27 +67,10 @@ let Agent = {
backupPath: OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak"),
/**
* Read the session from disk.
* In case sessionstore.js does not exist, attempt to read sessionstore.bak.
* NO-OP to start the worker.
*/
read: function () {
for (let path of [this.path, this.backupPath]) {
try {
let durationMs = Date.now();
let bytes = File.read(path);
durationMs = Date.now() - durationMs;
return {
result: Decoder.decode(bytes),
telemetry: {FX_SESSION_RESTORE_READ_FILE_MS: durationMs,
FX_SESSION_RESTORE_FILE_SIZE_BYTES: bytes.byteLength}
};
} catch (ex if isNoSuchFileEx(ex)) {
// Ignore exceptions about non-existent files.
}
}
// No sessionstore data files found. Return an empty string.
return {result: ""};
init: function () {
return {result: true};
},
/**

View File

@ -35,6 +35,9 @@
<!ENTITY helpFeedbackPage.label "Submit Feedback…">
<!ENTITY helpFeedbackPage.accesskey "S">
<!ENTITY helpShowTour.label "&brandShortName; Tour">
<!ENTITY helpShowTour.accesskey "o">
<!ENTITY preferencesCmdMac.label "Preferences…">
<!ENTITY preferencesCmdMac.commandkey ",">

View File

@ -570,6 +570,7 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY quitApplicationCmdWin.label "Exit">
<!ENTITY quitApplicationCmdWin.accesskey "x">
<!ENTITY quitApplicationCmdWin.tooltip "Exit &brandShortName;">
<!ENTITY goBackCmd.commandKey "[">
<!ENTITY goForwardCmd.commandKey "]">
<!ENTITY quitApplicationCmd.label "Quit">

View File

@ -91,8 +91,9 @@ email-link-button.label = Email Link
# \u2026, or use "..." if \u2026 doesn't suit traditions in your locale.
email-link-button.tooltiptext2 = Email Link…
# LOCALIZATION NOTE(quit-button.tooltiptext.linux): %S is the keyboard shortcut
quit-button.tooltiptext.linux = Quit (%S)
# LOCALIZATION NOTE(quit-button.tooltiptext.linux2): %1$S is the brand name (e.g. Firefox),
# %2$S is the keyboard shortcut
quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
# LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
# %2$S is the keyboard shortcut
quit-button.tooltiptext.mac = Quit %1$S (%2$S)

View File

@ -1260,7 +1260,7 @@ function Tab(aURI, aParams, aOwner) {
if ("private" in aParams) {
this._private = aParams.private;
} else if (aOwner) {
this._private = aOwner.private;
this._private = aOwner._private;
}
this.owner = aOwner || null;

View File

@ -167,6 +167,7 @@ var ContextMenuUI = {
for (let command of Array.slice(this.commands.childNodes)) {
command.hidden = true;
command.selected = false;
}
let optionsAvailable = false;

View File

@ -874,6 +874,43 @@ gTests.push({
}
});
gTests.push({
desc: "Bug 963067 - 'Cut' in the cut, copy, paste menu is always active " +
"after a browser launch.",
run: function test() {
info(chromeRoot + "browser_context_menu_tests_02.html");
yield addTab(chromeRoot + "browser_context_menu_tests_02.html");
purgeEventQueue();
emptyClipboard();
ContextUI.dismiss();
yield waitForCondition(() => !ContextUI.navbarVisible);
let tabWindow = Browser.selectedTab.browser.contentWindow;
let input = tabWindow.document.getElementById("text3-input");
let cutMenuItem = document.getElementById("context-cut");
input.select();
// Emulate RichListBox's behavior and set first item selected by default.
cutMenuItem.selected = true;
let promise = waitForEvent(document, "popupshown");
sendContextMenuClickToElement(tabWindow, input);
yield promise;
ok(!cutMenuItem.hidden && !cutMenuItem.selected,
"Cut menu item is visible and not selected.");
promise = waitForEvent(document, "popuphidden");
ContextMenuUI.hide();
yield promise;
Browser.closeTab(Browser.selectedTab, { forceClose: true });
}
});
function test() {
setDevPixelEqualToPx();
runTests();

View File

@ -16,12 +16,22 @@ gTests.push({
is(tab.isPrivate, false, "Tabs are not private by default");
is(tab.chromeTab.hasAttribute("private"), false,
"non-private tab has no private attribute");
let child = Browser.addTab("about:mozilla", false, tab);
is(child.isPrivate, false, "Child of a non-private tab is not private");
Browser.closeTab(child, { forceClose: true });
Browser.closeTab(tab, { forceClose: true });
tab = Browser.addTab("about:mozilla", false, null, { private: true });
is(tab.isPrivate, true, "Create a private tab");
is(tab.chromeTab.getAttribute("private"), "true",
"private tab has private attribute");
child = Browser.addTab("about:mozilla", false, tab);
is(child.isPrivate, true, "Child of a private tab is private");
Browser.closeTab(child, { forceClose: true });
Browser.closeTab(tab, { forceClose: true });
}
});

View File

@ -117,6 +117,62 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
list-style-image: url("chrome://browser/skin/places/bookmarksToolbar-menuPanel.png") !important;
}
/* ----- BOOKMARK STAR ANIMATION ----- */
@keyframes animation-bookmarkAdded {
from { transform: rotate(0deg) translateX(-20px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkAddedToBookmarksBar {
from { transform: rotate(0deg) translateX(-12px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkPulse {
from { transform: scale(1); }
50% { transform: scale(1.3); }
to { transform: scale(1); }
}
#bookmarked-notification-container {
min-height: 1px;
min-width: 1px;
height: 1px;
margin-bottom: -1px;
z-index: 5;
position: relative;
}
#bookmarked-notification {
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
width: 16px;
height: 16px;
opacity: 0;
}
#bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
animation: animation-bookmarkAdded 800ms;
animation-timing-function: ease ease ease linear;
}
#bookmarked-notification-anchor[notification="finish"][in-bookmarks-toolbar=true] > #bookmarked-notification {
animation: animation-bookmarkAddedToBookmarksBar 800ms;
}
#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
animation: animation-bookmarkPulse 300ms;
animation-delay: 600ms;
animation-timing-function: ease-out;
}
/* Bookmark menus */
menu.bookmark-item,
menuitem.bookmark-item {

View File

@ -95,6 +95,7 @@ browser.jar:
skin/classic/browser/newtab/controls.png (newtab/controls.png)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarks-notification-finish.png (places/bookmarks-notification-finish.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
* skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -361,6 +361,68 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
}
}
/* ----- BOOKMARK STAR ANIMATION ----- */
@keyframes animation-bookmarkAdded {
from { transform: rotate(0deg) translateX(-20px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-20px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkAddedToBookmarksBar {
from { transform: rotate(0deg) translateX(-12px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-12px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkPulse {
from { transform: scale(1); }
50% { transform: scale(1.3); }
to { transform: scale(1); }
}
#bookmarked-notification-container {
min-height: 1px;
min-width: 1px;
height: 1px;
margin-bottom: -1px;
z-index: 5;
position: relative;
}
#bookmarked-notification {
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
width: 16px;
height: 16px;
opacity: 0;
}
#bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
animation: animation-bookmarkAdded 800ms;
animation-timing-function: ease ease ease linear;
}
#bookmarked-notification-anchor[notification="finish"][in-bookmarks-toolbar=true] > #bookmarked-notification {
animation: animation-bookmarkAddedToBookmarksBar 800ms;
}
@media (min-resolution: 2dppx) {
#bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
background-image: url("chrome://browser/skin/places/bookmarks-notification-finish@2x.png");
}
}
#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
animation: animation-bookmarkPulse 300ms;
animation-delay: 600ms;
animation-timing-function: ease-out;
}
/* ----- BOOKMARK MENUS ----- */
.bookmark-item > .menu-iconic-left > .menu-iconic-icon {

View File

@ -157,6 +157,8 @@ browser.jar:
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar@2x.png (places/bookmarksToolbar@2x.png)
skin/classic/browser/places/bookmarks-notification-finish.png (places/bookmarks-notification-finish.png)
skin/classic/browser/places/bookmarks-notification-finish@2x.png (places/bookmarks-notification-finish@2x.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel@2x.png (places/bookmarksToolbar-menuPanel@2x.png)
skin/classic/browser/places/history.png (places/history.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -51,6 +51,7 @@
}
#UITourTooltipClose {
visibility: hidden; /* XXX Temporarily disabled by bug 966913 */
-moz-appearance: none;
border: none;
background-color: transparent;

View File

@ -117,7 +117,7 @@
background-attachment: scroll, scroll, fixed, fixed, scroll;
}
toolbarpaletteitem {
toolbarpaletteitem[place="toolbar"] {
transition: border-width 250ms ease-in-out;
}
@ -132,6 +132,7 @@ toolbarpaletteitem[place="panel"] {
}
toolbarpaletteitem[notransition].panel-customization-placeholder,
toolbarpaletteitem[notransition][place="toolbar"],
toolbarpaletteitem[notransition][place="palette"],
toolbarpaletteitem[notransition][place="panel"] {
transition: none;

View File

@ -318,6 +318,51 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-p
list-style-image: url("chrome://browser/skin/places/bookmarksToolbar-menuPanel.png") !important;
}
/* ----- BOOKMARK STAR ANIMATION ----- */
@keyframes animation-bookmarkAdded {
from { transform: rotate(0deg) translateX(-16px) rotate(0deg) scale(1); opacity: 0; }
60% { transform: rotate(180deg) translateX(-16px) rotate(-180deg) scale(2.2); opacity: 1; }
80% { opacity: 1; }
to { transform: rotate(180deg) translateX(-16px) rotate(-180deg) scale(1); opacity: 0; }
}
@keyframes animation-bookmarkPulse {
from { transform: scale(1); }
50% { transform: scale(1.3); }
to { transform: scale(1); }
}
#bookmarked-notification-container {
min-height: 1px;
min-width: 1px;
height: 1px;
margin-bottom: -1px;
z-index: 5;
position: relative;
}
#bookmarked-notification {
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
width: 16px;
height: 16px;
opacity: 0;
}
#bookmarked-notification-anchor[notification="finish"] > #bookmarked-notification {
background-image: url("chrome://browser/skin/places/bookmarks-notification-finish.png");
animation: animation-bookmarkAdded 800ms;
animation-timing-function: ease ease ease linear;
}
#bookmarks-menu-button[notification="finish"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
animation: animation-bookmarkPulse 300ms;
animation-delay: 600ms;
animation-timing-function: ease-out;
}
/* ::::: bookmark menus ::::: */
menu.bookmark-item,

View File

@ -119,6 +119,7 @@ browser.jar:
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)
skin/classic/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel.png)
skin/classic/browser/places/bookmarks-notification-finish.png (places/bookmarks-notification-finish.png)
skin/classic/browser/places/calendar.png (places/calendar.png)
skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png)
skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -520,15 +520,6 @@ abstract public class BrowserApp extends GeckoApp
}
});
mBrowserToolbar.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (isHomePagerVisible()) {
mHomePager.onToolbarFocusChange(hasFocus);
}
}
});
// Intercept key events for gamepad shortcuts
mBrowserToolbar.setOnKeyListener(this);
@ -1658,7 +1649,7 @@ abstract public class BrowserApp extends GeckoApp
if (mHomePager == null) {
final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
mHomePager = (HomePager) homePagerStub.inflate().findViewById(R.id.home_pager);
mHomePager = (HomePager) homePagerStub.inflate();
}
mHomePager.show(getSupportLoaderManager(),

View File

@ -5,10 +5,6 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.PropertyAnimator.Property;
import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R;
@ -27,7 +23,6 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
@ -38,22 +33,6 @@ public class HomeBanner extends LinearLayout
implements GeckoEventListener {
private static final String LOGTAG = "GeckoHomeBanner";
final TextView mTextView;
final ImageView mIconView;
final ImageButton mCloseButton;
// Used for tracking scroll length
private float mTouchY = -1;
// Used to detect for upwards scroll to push banner all the way up
private boolean mSnapBannerToTop;
// Used so that we don't move the banner when scrolling between pages
private boolean mScrollingPages = false;
// User has dismissed the banner using the close button
private boolean mDismissed = false;
public HomeBanner(Context context) {
this(context, null);
}
@ -62,18 +41,23 @@ public class HomeBanner extends LinearLayout
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.home_banner, this);
mTextView = (TextView) findViewById(R.id.text);
mIconView = (ImageView) findViewById(R.id.icon);
mCloseButton = (ImageButton) findViewById(R.id.close);
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mCloseButton.getDrawable().setAlpha(127);
// Tapping on the close button will ensure that the banner is never
// showed again on this session.
mCloseButton.setOnClickListener(new View.OnClickListener() {
final ImageButton closeButton = (ImageButton) findViewById(R.id.close);
// The drawable should have 50% opacity.
closeButton.getDrawable().setAlpha(127);
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
animateDown();
mDismissed = true;
HomeBanner.this.setVisibility(View.GONE);
}
});
@ -81,66 +65,54 @@ public class HomeBanner extends LinearLayout
@Override
public void onClick(View v) {
// Send the current message id back to JS.
GeckoAppShell.sendEventToGecko(
GeckoEvent.createBroadcastEvent("HomeBanner:Click",(String) getTag()));
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Click", (String) getTag()));
}
});
}
@Override
public void onAttachedToWindow() {
GeckoAppShell.getEventDispatcher().registerEventListener("HomeBanner:Data", this);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Get", null));
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
GeckoAppShell.getEventDispatcher().unregisterEventListener("HomeBanner:Data", this);
}
}
public void show() {
if (!mDismissed) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Get", null));
}
}
public void hide() {
animateDown();
}
public void setScrollingPages(boolean scrollingPages) {
mScrollingPages = scrollingPages;
public boolean isDismissed() {
return (getVisibility() == View.GONE);
}
@Override
public void handleMessage(final String event, final JSONObject message) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
try {
// Store the current message id to pass back to JS in the view's OnClickListener.
setTag(message.getString("id"));
setText(message.getString("text"));
setIcon(message.optString("iconURI"));
animateUp();
} catch (JSONException e) {
Log.e(LOGTAG, "Exception handling " + event + " message", e);
public void handleMessage(String event, JSONObject message) {
try {
// Store the current message id to pass back to JS in the view's OnClickListener.
setTag(message.getString("id"));
// Display styled text from an HTML string.
final Spanned text = Html.fromHtml(message.getString("text"));
final TextView textView = (TextView) findViewById(R.id.text);
// Update the banner message on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
textView.setText(text);
setVisibility(View.VISIBLE);
}
}
});
}
});
} catch (JSONException e) {
Log.e(LOGTAG, "Exception handling " + event + " message", e);
return;
}
private void setText(String text) {
// Display styled text from an HTML string.
final Spanned html = Html.fromHtml(text);
final String iconURI = message.optString("iconURI");
final ImageView iconView = (ImageView) findViewById(R.id.icon);
// Update the banner message on the UI thread.
mTextView.setText(html);
}
private void setIcon(String iconURI) {
if (TextUtils.isEmpty(iconURI)) {
// Hide the image view if we don't have an icon to show.
mIconView.setVisibility(View.GONE);
iconView.setVisibility(View.GONE);
return;
}
@ -149,102 +121,18 @@ public class HomeBanner extends LinearLayout
public void onBitmapFound(final Drawable d) {
// Bail if getDrawable doesn't find anything.
if (d == null) {
mIconView.setVisibility(View.GONE);
iconView.setVisibility(View.GONE);
return;
}
// Update the banner icon
mIconView.setImageDrawable(d);
}
});
}
private void animateDown() {
// No need to animate if already translated.
if (getVisibility() == GONE && ViewHelper.getTranslationY(this) == getHeight()) {
return;
}
final PropertyAnimator animator = new PropertyAnimator(100);
animator.attach(this, Property.TRANSLATION_Y, getHeight());
animator.start();
animator.addPropertyAnimationListener(new PropertyAnimationListener() {
@Override
public void onPropertyAnimationStart() {}
public void onPropertyAnimationEnd() {
HomeBanner.this.setVisibility(GONE);
}
});
}
private void animateUp() {
// No need to animate if already translated.
if (getVisibility() == VISIBLE && ViewHelper.getTranslationY(this) == 0) {
return;
}
setVisibility(View.VISIBLE);
final PropertyAnimator animator = new PropertyAnimator(100);
animator.attach(this, Property.TRANSLATION_Y, 0);
animator.start();
}
/**
* Touches to the HomePager are forwarded here to handle the hiding / showing of the banner
* on scroll.
*/
public void handleHomeTouch(MotionEvent event) {
if (mDismissed || mScrollingPages) {
return;
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mTouchY = event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
// There is a chance that we won't receive ACTION_DOWN, if the touch event
// actually started on the Grid instead of the List. Treat this as first event.
if (mTouchY == -1) {
mTouchY = event.getRawY();
return;
}
final float curY = event.getRawY();
final float delta = mTouchY - curY;
mSnapBannerToTop = delta <= 0.0f;
final float height = getHeight();
float newTranslationY = ViewHelper.getTranslationY(this) + delta;
// Clamp the values to be between 0 and height.
if (newTranslationY < 0.0f) {
newTranslationY = 0.0f;
} else if (newTranslationY > height) {
newTranslationY = height;
}
ViewHelper.setTranslationY(this, newTranslationY);
mTouchY = curY;
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mTouchY = -1;
final float y = ViewHelper.getTranslationY(this);
final float height = getHeight();
if (y > 0.0f && y < height) {
if (mSnapBannerToTop) {
animateUp();
} else {
animateDown();
// Update the banner icon on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
iconView.setImageDrawable(d);
}
}
break;
});
}
}
});
}
}

View File

@ -24,10 +24,8 @@ import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.view.ViewGroup.LayoutParams;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.View;
import java.util.ArrayList;
@ -35,7 +33,6 @@ import java.util.EnumSet;
import java.util.List;
public class HomePager extends ViewPager {
private static final String LOGTAG = "GeckoHomePager";
private static final int LOADER_ID_CONFIG = 0;
@ -43,7 +40,6 @@ public class HomePager extends ViewPager {
private volatile boolean mLoaded;
private Decor mDecor;
private View mTabStrip;
private HomeBanner mHomeBanner;
private final OnAddPanelListener mAddPanelListener;
@ -51,7 +47,6 @@ public class HomePager extends ViewPager {
private ConfigLoaderCallbacks mConfigLoaderCallbacks;
private String mInitialPanelId;
private int mDefaultPanelIndex;
// Whether or not we need to restart the loader when we show the HomePager.
private boolean mRestartLoader;
@ -244,10 +239,6 @@ public class HomePager extends ViewPager {
PropertyAnimator.Property.ALPHA,
1.0f);
}
// Setup banner and decor listeners
mHomeBanner = (HomeBanner) ((ViewGroup) getParent()).findViewById(R.id.home_banner);
setOnPageChangeListener(new HomePagerOnPageChangeListener());
}
/**
@ -259,15 +250,6 @@ public class HomePager extends ViewPager {
setAdapter(null);
}
@Override
public void setVisibility(int visibility) {
// Ensure that no decorations are overlaying the mainlayout
if (mHomeBanner != null) {
mHomeBanner.setVisibility(visibility);
}
super.setVisibility(visibility);
}
/**
* Determines whether the pager is visible.
*
@ -287,13 +269,6 @@ public class HomePager extends ViewPager {
if (mDecor != null) {
mDecor.onPageSelected(item);
}
if (mHomeBanner != null) {
if (item == mDefaultPanelIndex) {
mHomeBanner.show();
} else {
mHomeBanner.hide();
}
}
}
@Override
@ -306,24 +281,6 @@ public class HomePager extends ViewPager {
return super.onInterceptTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// Get touches to pages, pass to banner, and forward to pages.
if (mHomeBanner != null) {
mHomeBanner.handleHomeTouch(event);
}
return super.dispatchTouchEvent(event);
}
public void onToolbarFocusChange(boolean hasFocus) {
if (hasFocus) {
mHomeBanner.hide();
} else if (mDefaultPanelIndex == getCurrentItem() || getAdapter().getCount() == 0) {
mHomeBanner.show();
}
}
private void updateUiFromPanelConfigs(List<PanelConfig> panelConfigs) {
// We only care about the adapter if HomePager is currently
// loaded, which means it's visible in the activity.
@ -347,9 +304,6 @@ public class HomePager extends ViewPager {
for (PanelConfig panelConfig : panelConfigs) {
if (!panelConfig.isDisabled()) {
enabledPanels.add(panelConfig);
if (panelConfig.isDefault()) {
mDefaultPanelIndex = enabledPanels.size() - 1;
}
}
}
@ -395,35 +349,4 @@ public class HomePager extends ViewPager {
public void onLoaderReset(Loader<List<PanelConfig>> loader) {
}
}
private class HomePagerOnPageChangeListener implements ViewPager.OnPageChangeListener {
@Override
public void onPageSelected(int position) {
if (mDecor != null) {
mDecor.onPageSelected(position);
}
if (mHomeBanner != null) {
if (position == mDefaultPanelIndex) {
mHomeBanner.show();
} else {
mHomeBanner.hide();
}
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (mDecor != null) {
mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
if (mHomeBanner != null) {
mHomeBanner.setScrollingPages(positionOffsetPixels > 0);
}
}
@Override
public void onPageScrollStateChanged(int state) { }
}
}

View File

@ -86,6 +86,15 @@ public class TopSitesPanel extends HomeFragment {
// Grid of top sites
private TopSitesGridView mGrid;
// Banner to show snippets.
private HomeBanner mBanner;
// Raw Y value of the last event that happened on the list view.
private float mListTouchY = -1;
// Scrolling direction of the banner.
private boolean mSnapBannerToTop;
// Callbacks used for the search and favicon cursor loaders
private CursorLoaderCallbacks mCursorLoaderCallbacks;
@ -197,6 +206,15 @@ public class TopSitesPanel extends HomeFragment {
registerForContextMenu(mList);
registerForContextMenu(mGrid);
mBanner = (HomeBanner) view.findViewById(R.id.home_banner);
mList.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
TopSitesPanel.this.handleListTouchEvent(event);
return false;
}
});
}
@Override
@ -435,6 +453,60 @@ public class TopSitesPanel extends HomeFragment {
}
}
private void handleListTouchEvent(MotionEvent event) {
// Ignore the event if the banner is hidden for this session.
if (mBanner.isDismissed()) {
return;
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mListTouchY = event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
// There is a chance that we won't receive ACTION_DOWN, if the touch event
// actually started on the Grid instead of the List. Treat this as first event.
if (mListTouchY == -1) {
mListTouchY = event.getRawY();
return;
}
final float curY = event.getRawY();
final float delta = mListTouchY - curY;
mSnapBannerToTop = (delta > 0.0f) ? false : true;
final float height = mBanner.getHeight();
float newTranslationY = ViewHelper.getTranslationY(mBanner) + delta;
// Clamp the values to be between 0 and height.
if (newTranslationY < 0.0f) {
newTranslationY = 0.0f;
} else if (newTranslationY > height) {
newTranslationY = height;
}
ViewHelper.setTranslationY(mBanner, newTranslationY);
mListTouchY = curY;
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mListTouchY = -1;
final float y = ViewHelper.getTranslationY(mBanner);
final float height = mBanner.getHeight();
if (y > 0.0f && y < height) {
final PropertyAnimator animator = new PropertyAnimator(100);
animator.attach(mBanner, Property.TRANSLATION_Y, mSnapBannerToTop ? 0 : height);
animator.start();
}
break;
}
}
}
private void updateUiFromCursor(Cursor c) {
mList.setHeaderDividersEnabled(c != null && c.getCount() > mMaxGridEntries);
}

View File

@ -14,8 +14,7 @@
android:background="@android:color/white"
android:visibility="gone">
<org.mozilla.gecko.home.TabMenuStrip android:id="@+id/tablet_menu_strip"
android:layout_width="fill_parent"
<org.mozilla.gecko.home.TabMenuStrip android:layout_width="fill_parent"
android:layout_height="32dip"
android:background="@color/background_light"
android:layout_gravity="top"

View File

@ -36,16 +36,6 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<org.mozilla.gecko.home.HomeBanner android:id="@+id/home_banner"
style="@style/Widget.HomeBanner"
android:layout_width="fill_parent"
android:layout_height="@dimen/home_banner_height"
android:background="@drawable/home_banner"
android:layout_gravity="bottom"
android:gravity="center_vertical"
android:visibility="gone"
android:clickable="true"
android:focusable="true"/>
</FrameLayout>

View File

@ -14,8 +14,7 @@
android:background="@android:color/white"
android:visibility="gone">
<org.mozilla.gecko.home.HomePagerTabStrip android:id="@+id/phone_menu_strip"
android:layout_width="fill_parent"
<org.mozilla.gecko.home.HomePagerTabStrip android:layout_width="fill_parent"
android:layout_height="32dip"
android:layout_gravity="top"
android:gravity="bottom"

View File

@ -3,8 +3,26 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<org.mozilla.gecko.home.HomeListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list"
style="@style/Widget.TopSitesListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<org.mozilla.gecko.home.HomeListView
android:id="@+id/list"
style="@style/Widget.TopSitesListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<org.mozilla.gecko.home.HomeBanner android:id="@+id/home_banner"
style="@style/Widget.HomeBanner"
android:layout_width="fill_parent"
android:layout_height="@dimen/home_banner_height"
android:background="@drawable/home_banner"
android:layout_gravity="bottom"
android:gravity="center_vertical"
android:visibility="gone"
android:clickable="true"
android:focusable="true"/>
</FrameLayout>

View File

@ -142,7 +142,6 @@ public class BrowserToolbar extends GeckoRelativeLayout
private OnFilterListener mFilterListener;
private OnStartEditingListener mStartEditingListener;
private OnStopEditingListener mStopEditingListener;
private OnFocusChangeListener mFocusChangeListener;
final private BrowserApp mActivity;
private boolean mHasSoftMenuButton;
@ -316,9 +315,6 @@ public class BrowserToolbar extends GeckoRelativeLayout
@Override
public void onFocusChange(View v, boolean hasFocus) {
setSelected(hasFocus);
if (mFocusChangeListener != null) {
mFocusChangeListener.onFocusChange(v, hasFocus);
}
}
});
@ -797,10 +793,6 @@ public class BrowserToolbar extends GeckoRelativeLayout
mStopEditingListener = listener;
}
public void setOnFocusChangeListener(OnFocusChangeListener listener) {
mFocusChangeListener = listener;
}
private void showUrlEditLayout() {
setUrlEditLayoutVisibility(true, null);
}

View File

@ -34,12 +34,15 @@
this.EXPORTED_SYMBOLS = [ "CrashMonitor" ];
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
const NOTIFICATIONS = [
"final-ui-startup",
@ -93,25 +96,42 @@ let CrashMonitorInternal = {
* @return {Promise} A promise that resolves/rejects once loading is complete
*/
loadPreviousCheckpoints: function () {
let promise = Task.spawn(function () {
let notifications;
try {
let decoder = new TextDecoder();
let data = yield OS.File.read(CrashMonitorInternal.path);
let contents = decoder.decode(data);
notifications = JSON.parse(contents);
} catch (ex if ex instanceof OS.File.Error && ex.becauseNoSuchFile) {
// If checkpoint file cannot be read
throw new Task.Result(null);
} catch (ex) {
Cu.reportError("Error while loading crash monitor data: " + ex);
throw new Task.Result(null);
let deferred = Promise.defer();
CrashMonitorInternal.previousCheckpoints = deferred.promise;
let file = FileUtils.File(CrashMonitorInternal.path);
NetUtil.asyncFetch(file, function(inputStream, status) {
if (!Components.isSuccessCode(status)) {
if (status != Cr.NS_ERROR_FILE_NOT_FOUND) {
Cu.reportError("Error while loading crash monitor data: " + status);
}
deferred.resolve(null);
return;
}
let data = NetUtil.readInputStreamToString(inputStream,
inputStream.available(), { charset: "UTF-8" });
let notifications = null;
try {
notifications = JSON.parse(data);
} catch (ex) {
Cu.reportError("Error while parsing crash monitor data: " + ex);
deferred.resolve(null);
}
try {
deferred.resolve(Object.freeze(notifications));
} catch (ex) {
// The only exception we reject from is if notifications is not
// an object. This happens when the checkpoints file contained
// just a numeric string.
deferred.reject(ex);
}
throw new Task.Result(Object.freeze(notifications));
});
CrashMonitorInternal.previousCheckpoints = promise;
return promise;
return deferred.promise;
}
};

View File

@ -265,9 +265,9 @@ function checkPanelPosition(panel)
panel.hidePopup();
}
function isWithinHalfPixel(a, b)
function isWithinHalfPixel(a, b, desc)
{
return Math.abs(a - b) <= 0.5;
ok(Math.abs(a - b) <= 0.5, desc);
}
function checkBigPanel(panel)

View File

@ -59,35 +59,46 @@
<implementation implements="nsIDOMXULLabelElement">
<constructor>
<![CDATA[
try {
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
this.mUnderlineAccesskey = (prefs.getIntPref("ui.key.menuAccessKey") != 0);
const nsIPrefLocalizedString =
Components.interfaces.nsIPrefLocalizedString;
const prefNameInsertSeparator =
"intl.menuitems.insertseparatorbeforeaccesskeys";
const prefNameAlwaysAppendAccessKey =
"intl.menuitems.alwaysappendaccesskeys";
var val = prefs.getComplexValue(prefNameInsertSeparator,
nsIPrefLocalizedString).data;
this.mInsertSeparator = (val == "true");
val = prefs.getComplexValue(prefNameAlwaysAppendAccessKey,
nsIPrefLocalizedString).data;
this.mAlwaysAppendAccessKey = (val == "true");
}
catch (e) { }
this.formatAccessKey();
this.formatAccessKey(true);
]]>
</constructor>
<method name="formatAccessKey">
<parameter name="firstTime"/>
<body>
<![CDATA[
var accessKey = this.accessKey;
// No need to remove existing formatting the first time.
if (firstTime && !accessKey)
return;
if (this.mInsertSeparator === undefined) {
try {
var prefs = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
this.mUnderlineAccesskey = (prefs.getIntPref("ui.key.menuAccessKey") != 0);
const nsIPrefLocalizedString =
Components.interfaces.nsIPrefLocalizedString;
const prefNameInsertSeparator =
"intl.menuitems.insertseparatorbeforeaccesskeys";
const prefNameAlwaysAppendAccessKey =
"intl.menuitems.alwaysappendaccesskeys";
var val = prefs.getComplexValue(prefNameInsertSeparator,
nsIPrefLocalizedString).data;
this.mInsertSeparator = (val == "true");
val = prefs.getComplexValue(prefNameAlwaysAppendAccessKey,
nsIPrefLocalizedString).data;
this.mAlwaysAppendAccessKey = (val == "true");
}
catch (e) {
this.mInsertSeparator = true;
}
}
if (!this.mUnderlineAccesskey)
return;
@ -116,7 +127,6 @@
this.mergeElement(oldHiddenSpan);
}
var accessKey = this.accessKey;
var labelText = this.textContent;
if (!accessKey || !labelText || !control) {
return;
@ -218,7 +228,7 @@
<field name="mUnderlineAccesskey">
!/Mac/.test(navigator.platform)
</field>
<field name="mInsertSeparator">true</field>
<field name="mInsertSeparator"/>
<field name="mAlwaysAppendAccessKey">false</field>
<property name="accessKey">
@ -245,7 +255,7 @@
if (control) {
control.setAttribute('accesskey', val);
}
this.formatAccessKey();
this.formatAccessKey(false);
return val;
]]>
</setter>
@ -262,7 +272,7 @@
control.labelElement = null; // No longer pointed to be this label
}
this.setAttribute('control', val);
this.formatAccessKey();
this.formatAccessKey(false);
return val;
]]>
</setter>

View File

@ -1,33 +1,43 @@
const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
const {DebuggerServer, ActorPool} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
/* 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/. */
if (!DebuggerServer.initialized) {
DebuggerServer.init();
}
"use strict";
// In case of apps being loaded in parent process, DebuggerServer is already
// initialized, but child specific actors are not registered.
// Otherwise, for apps in child process, we need to load actors the first
// time we load child.js
DebuggerServer.addChildActors();
// Encapsulate in its own scope to allows loading this frame script
// more than once.
(function () {
const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
const {DebuggerServer, ActorPool} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let onConnect = DevToolsUtils.makeInfallible(function (msg) {
removeMessageListener("debug:connect", onConnect);
if (!DebuggerServer.initialized) {
DebuggerServer.init();
}
let mm = msg.target;
// In case of apps being loaded in parent process, DebuggerServer is already
// initialized, but child specific actors are not registered.
// Otherwise, for apps in child process, we need to load actors the first
// time we load child.js
DebuggerServer.addChildActors();
let prefix = msg.data.prefix + docShell.appId;
let onConnect = DevToolsUtils.makeInfallible(function (msg) {
removeMessageListener("debug:connect", onConnect);
let conn = DebuggerServer.connectToParent(prefix, mm);
let mm = msg.target;
let actor = new DebuggerServer.ContentAppActor(conn, content);
let actorPool = new ActorPool(conn);
actorPool.addActor(actor);
conn.addActorPool(actorPool);
let prefix = msg.data.prefix + docShell.appId;
sendAsyncMessage("debug:actor", {actor: actor.grip(),
appId: docShell.appId,
prefix: prefix});
});
let conn = DebuggerServer.connectToParent(prefix, mm);
addMessageListener("debug:connect", onConnect);
let actor = new DebuggerServer.ContentAppActor(conn, content);
let actorPool = new ActorPool(conn);
actorPool.addActor(actor);
conn.addActorPool(actorPool);
sendAsyncMessage("debug:actor", {actor: actor.grip(),
appId: docShell.appId,
prefix: prefix});
});
addMessageListener("debug:connect", onConnect);
})();