Merge fx-team to m-c.

This commit is contained in:
Ryan VanderMeulen 2014-02-19 15:35:01 -05:00
commit 8190806d88
64 changed files with 1080 additions and 477 deletions

View File

@ -19,11 +19,11 @@ Cu.import("resource://gre/modules/Timer.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
"resource:///modules/sessionstore/DocShellCapabilities.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormData",
"resource:///modules/sessionstore/FormData.jsm");
"resource://gre/modules/FormData.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PageStyle",
"resource:///modules/sessionstore/PageStyle.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ScrollPosition",
"resource:///modules/sessionstore/ScrollPosition.jsm");
"resource://gre/modules/ScrollPosition.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
"resource:///modules/sessionstore/SessionHistory.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",

View File

@ -14,11 +14,11 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
"resource:///modules/sessionstore/DocShellCapabilities.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormData",
"resource:///modules/sessionstore/FormData.jsm");
"resource://gre/modules/FormData.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PageStyle",
"resource:///modules/sessionstore/PageStyle.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ScrollPosition",
"resource:///modules/sessionstore/ScrollPosition.jsm");
"resource://gre/modules/ScrollPosition.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
"resource:///modules/sessionstore/SessionHistory.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",

View File

@ -15,14 +15,12 @@ JS_MODULES_PATH = 'modules/sessionstore'
EXTRA_JS_MODULES = [
'ContentRestore.jsm',
'DocShellCapabilities.jsm',
'FormData.jsm',
'FrameTree.jsm',
'GlobalState.jsm',
'PageStyle.jsm',
'PrivacyFilter.jsm',
'PrivacyLevel.jsm',
'RecentlyClosedTabsAndWindowsMenuUtils.jsm',
'ScrollPosition.jsm',
'SessionCookies.jsm',
'SessionFile.jsm',
'SessionHistory.jsm',
@ -33,7 +31,6 @@ EXTRA_JS_MODULES = [
'TabState.jsm',
'TabStateCache.jsm',
'Utils.jsm',
'XPathGenerator.jsm',
]
EXTRA_PP_JS_MODULES += [

View File

@ -65,6 +65,9 @@ const COMPAT = {
// The indent of a console group in pixels.
GROUP_INDENT: 12,
// The default indent in pixels, applied even without any groups.
GROUP_INDENT_DEFAULT: 6,
};
// A map from the console API call levels to the Web Console severities.
@ -776,6 +779,12 @@ Messages.Simple.prototype = Heritage.extend(Messages.BaseMessage.prototype,
let icon = this.document.createElementNS(XHTML_NS, "span");
icon.className = "icon";
// Apply the current group by indenting appropriately.
// TODO: remove this once bug 778766 is fixed.
let iconMarginLeft = this._groupDepthCompat * COMPAT.GROUP_INDENT +
COMPAT.GROUP_INDENT_DEFAULT;
icon.style.marginLeft = iconMarginLeft + "px";
let body = this._renderBody();
this._repeatID.textContent += "|" + body.textContent;
@ -1319,11 +1328,6 @@ Widgets.MessageTimestamp.prototype = Heritage.extend(Widgets.BaseWidget.prototyp
this.element.className = "timestamp devtools-monospace";
this.element.textContent = l10n.timestampString(this.timestamp) + " ";
// Apply the current group by indenting appropriately.
// TODO: remove this once bug 778766 is fixed.
this.element.style.marginRight = this.message._groupDepthCompat *
COMPAT.GROUP_INDENT + "px";
return this;
},
}); // Widgets.MessageTimestamp.prototype

View File

@ -257,6 +257,7 @@ run-if = os == "mac"
[browser_webconsole_expandable_timestamps.js]
[browser_webconsole_autocomplete_in_debugger_stackframe.js]
[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
[browser_webconsole_output_01.js]
[browser_webconsole_output_02.js]
[browser_webconsole_output_03.js]

View File

@ -91,7 +91,7 @@ function test()
},
{
name: "console.dir output",
consoleDir: "XULDocument {",
consoleDir: /XULDocument .+ chrome:\/\/.+\/browser\.xul/,
},
{
name: "console.time output",

View File

@ -18,9 +18,14 @@ function test()
{
let hud = null;
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.chrome.enabled");
});
Task.spawn(runner).then(finishTest);
function* runner() {
Services.prefs.setBoolPref("devtools.chrome.enabled", true);
let {tab} = yield loadTab(TEST_URI);
info("open the browser console");

View File

@ -0,0 +1,104 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/*
* Bug 922161 - hide Browser Console JS input field if devtools.chrome.enabled is false
* when devtools.chrome.enabled then
* -browser console jsterm should be enabled
* -browser console object inspector properties should be set.
* -webconsole jsterm should be enabled
* -webconsole object inspector properties should be set.
*
* when devtools.chrome.enabled == false then
* -browser console jsterm should be disabled
* -browser console object inspector properties should not be set.
* -webconsole jsterm should be enabled
* -webconsole object inspector properties should be set.
*/
function testObjectInspectorPropertiesAreNotSet(variablesView) {
is(variablesView.eval, null, "vview.eval is null");
is(variablesView.switch, null, "vview.switch is null");
is(variablesView.delete, null, "vview.delete is null");
}
function* getVariablesView(hud) {
function openVariablesView(event, vview) {
deferred.resolve(vview._variablesView);
}
let deferred = promise.defer();
hud.jsterm.clearOutput();
hud.jsterm.execute('new Object()');
let [message] = yield waitForMessages({
webconsole: hud,
messages: [{
text: "object"
}],
})
hud.jsterm.once("variablesview-fetched", openVariablesView);
let anchor = [...message.matched][0].querySelector("a");
executeSoon(() =>
EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow)
);
return deferred.promise;
}
function testJSTermIsVisible(hud) {
let inputContainer = hud.ui.window.document.querySelector(".jsterm-input-container");
isnot(inputContainer.style.display, "none", "input is visible");
}
function testObjectInspectorPropertiesAreSet(variablesView) {
isnot(variablesView.eval, null, "vview.eval is set");
isnot(variablesView.switch, null, "vview.switch is set");
isnot(variablesView.delete, null, "vview.delete is set");
}
function testJSTermIsNotVisible(hud) {
let inputContainer = hud.ui.window.document.querySelector(".jsterm-input-container");
is(inputContainer.style.display, "none", "input is not visible");
}
function* testRunner() {
let browserConsole, webConsole, variablesView;
Services.prefs.setBoolPref("devtools.chrome.enabled", true);
browserConsole = yield HUDService.toggleBrowserConsole();
variablesView = yield getVariablesView(browserConsole);
testJSTermIsVisible(browserConsole);
testObjectInspectorPropertiesAreSet(variablesView);
let {tab: browserTab} = yield loadTab("data:text/html;charset=utf8,hello world");
webConsole = yield openConsole(browserTab);
variablesView = yield getVariablesView(webConsole);
testJSTermIsVisible(webConsole)
testObjectInspectorPropertiesAreSet(variablesView)
yield closeConsole(browserTab);
yield HUDService.toggleBrowserConsole();
Services.prefs.setBoolPref("devtools.chrome.enabled", false);
browserConsole = yield HUDService.toggleBrowserConsole();
variablesView = yield getVariablesView(browserConsole);
testJSTermIsNotVisible(browserConsole);
testObjectInspectorPropertiesAreNotSet(variablesView);
webConsole = yield openConsole(browserTab);
variablesView = yield getVariablesView(webConsole);
testJSTermIsVisible(webConsole)
testObjectInspectorPropertiesAreSet(variablesView)
yield closeConsole(browserTab);
}
function test() {
Task.spawn(testRunner).then(finishTest);
}

View File

@ -5,103 +5,75 @@
*/
// Tests that console.group/groupEnd works as intended.
let testDriver, hud;
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 664131: Expand console object with group methods";
function test() {
addTab("data:text/html;charset=utf-8,Web Console test for bug 664131: Expand console " +
"object with group methods");
browser.addEventListener("load", function onLoad(aEvent) {
browser.removeEventListener(aEvent.type, onLoad, true);
openConsole(null, function(aHud) {
hud = aHud;
testDriver = testGen();
testNext();
Task.spawn(runner).then(finishTest);
function* runner() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
let outputNode = hud.outputNode;
hud.jsterm.clearOutput();
content.console.group("bug664131a");
yield waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131a",
consoleGroup: 1,
}],
});
}, true);
content.console.log("bug664131a-inside");
yield waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131a-inside",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
groupDepth: 1,
}],
});
content.console.groupEnd("bug664131a");
content.console.log("bug664131-outside");
yield waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131-outside",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
groupDepth: 0,
}],
});
content.console.groupCollapsed("bug664131b");
yield waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131b",
consoleGroup: 1,
}],
});
// Test that clearing the console removes the indentation.
hud.jsterm.clearOutput();
content.console.log("bug664131-cleared");
yield waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131-cleared",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
groupDepth: 0,
}],
});
}
}
function testNext() {
testDriver.next();
}
function testGen() {
outputNode = hud.outputNode;
hud.jsterm.clearOutput();
content.console.group("bug664131a");
waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131a",
consoleGroup: 1,
}],
}).then(testNext);
yield undefined;
content.console.log("bug664131a-inside");
waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131a-inside",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
groupDepth: 1,
}],
}).then(testNext);
yield undefined;
content.console.groupEnd("bug664131a");
content.console.log("bug664131-outside");
waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131-outside",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
groupDepth: 0,
}],
}).then(testNext);
yield undefined;
content.console.groupCollapsed("bug664131b");
waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131b",
consoleGroup: 1,
}],
}).then(testNext);
yield undefined;
// Test that clearing the console removes the indentation.
hud.jsterm.clearOutput();
content.console.log("bug664131-cleared");
waitForMessages({
webconsole: hud,
messages: [{
text: "bug664131-cleared",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
groupDepth: 0,
}],
}).then(testNext);
yield undefined;
testDriver = hud = null;
finishTest();
yield undefined;
}

View File

@ -34,6 +34,9 @@ const SEVERITY_LOG = 3;
// The indent of a console group in pixels.
const GROUP_INDENT = 12;
// The default indent in pixels, applied even without any groups.
const GROUP_INDENT_DEFAULT = 6;
const WEBCONSOLE_STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
let WCU_l10n = new WebConsoleUtils.l10n(WEBCONSOLE_STRINGS_URI);
@ -1134,10 +1137,10 @@ function waitForMessages(aOptions)
}
if ("groupDepth" in aRule) {
let timestamp = aElement.querySelector(".timestamp");
let indent = (GROUP_INDENT * aRule.groupDepth) + "px";
if (!timestamp || timestamp.style.marginRight != indent) {
is(timestamp.style.marginRight, indent,
let icon = aElement.querySelector(".icon");
let indent = (GROUP_INDENT * aRule.groupDepth + GROUP_INDENT_DEFAULT) + "px";
if (!icon || icon.style.marginLeft != indent) {
is(icon.style.marginLeft, indent,
"group depth check failed for message rule: " + displayRule(aRule));
return false;
}

View File

@ -145,6 +145,9 @@ const HISTORY_FORWARD = 1;
// The indent of a console group in pixels.
const GROUP_INDENT = 12;
// The default indent in pixels, applied even without any groups.
const GROUP_INDENT_DEFAULT = 6;
// The number of messages to display in a single display update. If we display
// too many messages at once we slow the Firefox UI too much.
const MESSAGES_IN_INTERVAL = DEFAULT_LOG_LIMIT;
@ -2438,6 +2441,10 @@ WebConsoleFrame.prototype = {
let iconContainer = this.document.createElementNS(XHTML_NS, "span");
iconContainer.className = "icon";
// Apply the current group by indenting appropriately.
let iconMarginLeft = this.groupDepth * GROUP_INDENT + GROUP_INDENT_DEFAULT;
iconContainer.style.marginLeft = iconMarginLeft + "px";
// Create the message body, which contains the actual text of the message.
let bodyNode = this.document.createElementNS(XHTML_NS, "span");
bodyNode.className = "body devtools-monospace";
@ -2495,8 +2502,6 @@ WebConsoleFrame.prototype = {
// Create the timestamp.
let timestampNode = this.document.createElementNS(XHTML_NS, "span");
timestampNode.className = "timestamp devtools-monospace";
// Apply the current group by indenting appropriately.
timestampNode.style.marginRight = this.groupDepth * GROUP_INDENT + "px";
let timestampString = l10n.timestampString(timestamp);
timestampNode.textContent = timestampString + " ";
@ -3102,14 +3107,22 @@ JSTerm.prototype = {
autocompleteOptions);
let doc = this.hud.document;
let inputContainer = doc.querySelector(".jsterm-input-container");
this.completeNode = doc.querySelector(".jsterm-complete-node");
this.inputNode = doc.querySelector(".jsterm-input-node");
this.inputNode.addEventListener("keypress", this._keyPress, false);
this.inputNode.addEventListener("input", this._inputEventHandler, false);
this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
this.inputNode.addEventListener("focus", this._focusEventHandler, false);
this.hud.window.addEventListener("blur", this._blurEventHandler, false);
if (this.hud.owner._browserConsole &&
!Services.prefs.getBoolPref("devtools.chrome.enabled")) {
inputContainer.style.display = "none";
}
else {
this.inputNode.addEventListener("keypress", this._keyPress, false);
this.inputNode.addEventListener("input", this._inputEventHandler, false);
this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
this.inputNode.addEventListener("focus", this._focusEventHandler, false);
}
this.hud.window.addEventListener("blur", this._blurEventHandler, false);
this.lastInputValue && this.setInputValue(this.lastInputValue);
},
@ -3534,7 +3547,9 @@ JSTerm.prototype = {
return view._consoleLastObjectActor != aActor;
});
if (aOptions.objectActor) {
if (aOptions.objectActor &&
(!this.hud.owner._browserConsole ||
Services.prefs.getBoolPref("devtools.chrome.enabled"))) {
// Make sure eval works in the correct context.
view.eval = this._variablesViewEvaluate.bind(this, aOptions);
view.switch = this._variablesViewSwitch.bind(this, aOptions);

View File

@ -378,7 +378,9 @@ Section "-Application" APP_IDX
WriteRegDWORD HKCU "$0" "IconsVisible" 0
${EndIf}
!ifdef MOZ_METRO
${CleanupMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID}
${CleanupMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID} \
"FirefoxURL" \
"FirefoxHTML"
${AddMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID} \
"$INSTDIR\CommandExecuteHandler.exe" \
$AppUserModelID \

View File

@ -10,7 +10,9 @@
Function RegisterCEH
!ifdef MOZ_METRO
${If} ${AtLeastWin8}
${CleanupMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID}
${CleanupMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID} \
"FirefoxURL" \
"FirefoxHTML"
${AddMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID} \
"$INSTDIR\CommandExecuteHandler.exe" \
$AppUserModelID \

View File

@ -294,7 +294,9 @@ Section "Uninstall"
!ifdef MOZ_METRO
${If} ${AtLeastWin8}
${un.CleanupMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID}
${un.CleanupMetroBrowserHandlerValues} ${DELEGATE_EXECUTE_HANDLER_ID} \
"FirefoxURL" \
"FirefoxHTML"
${EndIf}
${ResetWin8PromptKeys}
${ResetWin8MetroSplash}

View File

@ -295,7 +295,7 @@
if (event.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
if (typeof SelectionHelperUI != 'undefined') {
SelectionHelperUI.attachEditSession(ChromeSelectionHandler,
event.clientX, event.clientY, this);
event.clientX, event.clientY, event.target);
} else {
// If we don't have access to SelectionHelperUI then we are using this
// binding for browser content (e.g. about:config)

View File

@ -8,6 +8,9 @@ let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FormData.jsm");
Cu.import("resource://gre/modules/ScrollPosition.jsm");
Cu.import("resource://gre/modules/Timer.jsm", this);
let WebProgressListener = {
_lastLocation: null,
@ -441,6 +444,11 @@ WebNavigation.init();
let DOMEvents = {
_timeout: null,
_sessionEvents: new Set(),
_sessionEventMap: {"SessionStore:collectFormdata" : FormData.collect,
"SessionStore:collectScrollPosition" : ScrollPosition.collect},
init: function() {
addEventListener("DOMContentLoaded", this, false);
addEventListener("DOMTitleChanged", this, false);
@ -451,6 +459,22 @@ let DOMEvents = {
addEventListener("DOMPopupBlocked", this, false);
addEventListener("pageshow", this, false);
addEventListener("pagehide", this, false);
addEventListener("input", this, true);
addEventListener("change", this, true);
addEventListener("scroll", this, true);
addMessageListener("SessionStore:restoreSessionTabData", this);
},
receiveMessage: function(message) {
switch (message.name) {
case "SessionStore:restoreSessionTabData":
if (message.json.formdata)
FormData.restore(content, message.json.formdata);
if (message.json.scroll)
ScrollPosition.restore(content, message.json.scroll.scroll);
break;
}
},
handleEvent: function(aEvent) {
@ -547,6 +571,31 @@ let DOMEvents = {
}
}
break;
case "input":
case "change":
this._sessionEvents.add("SessionStore:collectFormdata");
this._sendUpdates();
break;
case "scroll":
this._sessionEvents.add("SessionStore:collectScrollPosition");
this._sendUpdates();
break;
}
},
_sendUpdates: function() {
if (!this._timeout) {
// Wait a little before sending the message to batch multiple changes.
this._timeout = setTimeout(function() {
for (let eventType of this._sessionEvents) {
sendAsyncMessage(eventType, {
data: this._sessionEventMap[eventType](content)
});
}
this._sessionEvents.clear();
clearTimeout(this._timeout);
this._timeout = null;
}.bind(this), 1000);
}
}
};

View File

@ -293,7 +293,7 @@
if (aShouldDismiss)
ContextUI.dismissTabs();
if (!InputSourceHelper.isPrecise && this.textLength) {
if (!InputSourceHelper.isPrecise) {
let inputRectangle = this.inputField.getBoundingClientRect();
SelectionHelperUI.attachEditSession(ChromeSelectionHandler,
inputRectangle.left, inputRectangle.top, this);

View File

@ -1458,7 +1458,7 @@ Tab.prototype = {
browser.id = "browser-" + this._id;
this._chromeTab.linkedBrowser = browser;
browser.setAttribute("type", "content");
browser.setAttribute("type", "content-targetable");
let useRemote = Services.appinfo.browserTabsRemote;
let useLocal = Util.isLocalScheme(aURI);
@ -1532,7 +1532,7 @@ Tab.prototype = {
} else {
notification.classList.remove("active-tab-notificationbox");
browser.messageManager.sendAsyncMessage("Browser:Blur", { });
browser.setAttribute("type", "content");
browser.setAttribute("type", "content-targetable");
browser.active = false;
}
},

View File

@ -6,6 +6,8 @@
* Selection handler for chrome text inputs
*/
let Ci = Components.interfaces;
const kCaretMode = 1;
const kSelectionMode = 2;
@ -34,7 +36,8 @@ var ChromeSelectionHandler = {
this._domWinUtils = Util.getWindowUtils(window);
this._contentWindow = window;
this._targetElement = aJson.target;
this._targetIsEditable = this._targetElement instanceof Components.interfaces.nsIDOMXULTextBoxElement;
this._targetIsEditable = Util.isTextInput(this._targetElement) ||
this._targetElement instanceof Ci.nsIDOMXULTextBoxElement;
if (!this._targetIsEditable) {
this._onFail("not an editable?", this._targetElement);
return;
@ -46,6 +49,11 @@ var ChromeSelectionHandler = {
return;
}
if (!this._getTargetElementValue()) {
this._onFail("Target element does not contain any content to select.");
return;
}
if (!selection.isCollapsed) {
this._mode = kSelectionMode;
this._updateSelectionUI("start", true, true);
@ -380,19 +388,36 @@ var ChromeSelectionHandler = {
*/
_getSelection: function _getSelection() {
let targetElementEditor = this._getTargetElementEditor();
return targetElementEditor ? targetElementEditor.selection : null;
},
_getTargetElementValue: function _getTargetElementValue() {
if (this._targetElement instanceof Ci.nsIDOMXULTextBoxElement) {
return this._targetElement
.QueryInterface(Components.interfaces.nsIDOMXULTextBoxElement)
.editor.selection;
return this._targetElement.inputField.value;
} else if (Util.isTextInput(this._targetElement)) {
return this._targetElement.value;
}
return null;
},
_getSelectController: function _getSelectController() {
return this._targetElement
.QueryInterface(Components.interfaces.nsIDOMXULTextBoxElement)
.editor.selectionController;
let targetElementEditor = this._getTargetElementEditor();
return targetElementEditor ? targetElementEditor.selectionController : null;
},
_getTargetElementEditor: function() {
if (this._targetElement instanceof Ci.nsIDOMXULTextBoxElement) {
return this._targetElement.QueryInterface(Ci.nsIDOMXULTextBoxElement)
.editor;
} else if (Util.isTextInput(this._targetElement)) {
return this._targetElement.QueryInterface(Ci.nsIDOMNSEditableElement)
.editor;
}
return null;
}
};
ChromeSelectionHandler.__proto__ = new SelectionPrototype();

View File

@ -250,6 +250,39 @@ gTests.push({
}
});
gTests.push({
desc: "Bug 972428 - grippers not appearing under the URL field when adding " +
"text.",
run: function() {
let inputField = document.getElementById("urlbar-edit").inputField;
let inputFieldRectangle = inputField.getBoundingClientRect();
let chromeHandlerSpy = spyOnMethod(ChromeSelectionHandler, "msgHandler");
// Reset URL to empty string
inputField.value = "";
inputField.blur();
// Activate URL input
sendTap(window, inputFieldRectangle.left + 50, inputFieldRectangle.top + 5);
// Wait until ChromeSelectionHandler tries to attach selection
yield waitForCondition(() => chromeHandlerSpy.argsForCall.some(
(args) => args[0] == "Browser:SelectionAttach"));
ok(!SelectHelperUI.isSelectionUIVisible && !SelectHelperUI.isCaretUIVisible,
"Neither CaretUI nor SelectionUI is visible on empty input.");
inputField.value = "Test text";
sendTap(window, inputFieldRectangle.left + 10, inputFieldRectangle.top + 5);
yield waitForCondition(() => SelectionHelperUI.isCaretUIVisible);
chromeHandlerSpy.restore();
}
});
function test() {
if (!isLandscapeMode()) {
todo(false, "browser_selection_tests need landscape mode to run.");

View File

@ -1033,11 +1033,14 @@ function runTests() {
function spyOnMethod(aObj, aMethod) {
let origFunc = aObj[aMethod];
let spy = function() {
spy.calledWith = Array.slice(arguments);
let callArguments = Array.slice(arguments);
spy.callCount++;
spy.calledWith = callArguments;
spy.argsForCall.push(callArguments);
return (spy.returnValue = origFunc.apply(aObj, arguments));
};
spy.callCount = 0;
spy.argsForCall = [];
spy.restore = function() {
return (aObj[aMethod] = origFunc);
};

View File

@ -355,8 +355,19 @@ SessionStore.prototype = {
handleEvent: function ss_handleEvent(aEvent) {
let window = aEvent.currentTarget.ownerDocument.defaultView;
switch (aEvent.type) {
case "load":
browser = aEvent.currentTarget;
if (aEvent.target == browser.contentDocument && browser.__SS_tabFormData) {
browser.messageManager.sendAsyncMessage("SessionStore:restoreSessionTabData", {
formdata: browser.__SS_tabFormData.formdata,
scroll: browser.__SS_tabFormData.scroll
});
}
break;
case "TabOpen":
this.updateTabTelemetryVars(window);
let browser = aEvent.originalTarget.linkedBrowser;
browser.addEventListener("load", this, true);
case "TabClose": {
let browser = aEvent.originalTarget.linkedBrowser;
if (aEvent.type == "TabOpen") {
@ -380,8 +391,19 @@ SessionStore.prototype = {
},
receiveMessage: function ss_receiveMessage(aMessage) {
let window = aMessage.target.ownerDocument.defaultView;
this.onTabLoad(window, aMessage.target, aMessage);
let browser = aMessage.target;
switch (aMessage.name) {
case "SessionStore:collectFormdata":
browser.__SS_data.formdata = aMessage.json.data;
break;
case "SessionStore:collectScrollPosition":
browser.__SS_data.scroll = aMessage.json.data;
break;
default:
let window = aMessage.target.ownerDocument.defaultView;
this.onTabLoad(window, aMessage.target, aMessage);
break;
}
},
onWindowOpen: function ss_onWindowOpen(aWindow) {
@ -455,6 +477,8 @@ SessionStore.prototype = {
onTabAdd: function ss_onTabAdd(aWindow, aBrowser, aNoNotification) {
aBrowser.messageManager.addMessageListener("pageshow", this);
aBrowser.messageManager.addMessageListener("Content:SessionHistory", this);
aBrowser.messageManager.addMessageListener("SessionStore:collectFormdata", this);
aBrowser.messageManager.addMessageListener("SessionStore:collectScrollPosition", this);
if (!aNoNotification)
this.saveStateDelayed();
@ -464,6 +488,8 @@ SessionStore.prototype = {
onTabRemove: function ss_onTabRemove(aWindow, aBrowser, aNoNotification) {
aBrowser.messageManager.removeMessageListener("pageshow", this);
aBrowser.messageManager.removeMessageListener("Content:SessionHistory", this);
aBrowser.messageManager.removeMessageListener("SessionStore:collectFormdata", this);
aBrowser.messageManager.removeMessageListener("SessionStore:collectScrollPosition", this);
// If this browser is being restored, skip any session save activity
if (aBrowser.__SS_restore)
@ -912,6 +938,7 @@ SessionStore.prototype = {
tab.chromeTab.updateTitle(tabData.entries[tabData.index - 1].title);
}
tab.browser.__SS_tabFormData = tabData
tab.browser.__SS_extdata = tabData.extData;
}

View File

@ -26,7 +26,7 @@
#endif
// Heartbeat timer duration used while waiting for an incoming request.
#define HEARTBEAT_MSEC 1000
#define HEARTBEAT_MSEC 250
// Total number of heartbeats we wait before giving up and shutting down.
#define REQUEST_WAIT_TIMEOUT 30
// Pulled from desktop browser's shell

View File

@ -194,8 +194,8 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-it
}
/* Help SDK buttons fit in. */
toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-placeholder,
#personal-bookmarks[cui-areatype="menu-panel"] > #bookmarks-toolbar-placeholder {
toolbarpaletteitem[place="palette"] > toolbarbutton[sdk-button="true"] > .toolbarbutton-icon,
toolbarbutton[sdk-button="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon {
height: 32px;
width: 32px;
}

View File

@ -81,9 +81,10 @@ system.
Tested Versions
===============
============ ==================================== =================
OS Version Working as of
============ ==================================== =================
Mac OS X Luna (Build id: 20130919-0819) February 2014
Mac OS X Kepler (Build id: 20131219-0014) February 2014
============ ==================================== =================
=============== ==================================== =================
OS Version Working as of
=============== ==================================== =================
Mac OS X Luna (Build id: 20130919-0819) February 2014
Mac OS X Kepler (Build id: 20131219-0014) February 2014
Mac OS X 10.8.5 Kepler (Build id: 20130919-0819) February 2014
=============== ==================================== =================

View File

@ -13,6 +13,8 @@ import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.favicons.LoadFaviconTask;
import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.GeckoLayerClient;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
@ -28,6 +30,7 @@ import org.mozilla.gecko.home.SearchEngine;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
import org.mozilla.gecko.util.Clipboard;
@ -545,6 +548,8 @@ abstract public class BrowserApp extends GeckoApp
registerEventListener("Menu:Add");
registerEventListener("Menu:Remove");
registerEventListener("Menu:Update");
registerEventListener("Accounts:Create");
registerEventListener("Accounts:Exist");
Distribution.init(this);
JavaAddonManager.getInstance().init(getApplicationContext());
@ -862,6 +867,8 @@ abstract public class BrowserApp extends GeckoApp
unregisterEventListener("Menu:Add");
unregisterEventListener("Menu:Remove");
unregisterEventListener("Menu:Update");
unregisterEventListener("Accounts:Create");
unregisterEventListener("Accounts:Exist");
if (AppConstants.MOZ_ANDROID_BEAM && Build.VERSION.SDK_INT >= 14) {
NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
@ -1246,6 +1253,30 @@ abstract public class BrowserApp extends GeckoApp
bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS);
bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(bringToFrontIntent);
} else if (event.equals("Accounts:Create")) {
// Do exactly the same thing as if you tapped 'Sync'
// in Settings.
final Intent intent = new Intent(getContext(), FxAccountGetStartedActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
} else if (event.equals("Accounts:Exist")) {
final String kind = message.getString("kind");
final JSONObject response = new JSONObject();
if ("any".equals(kind)) {
response.put("exists", SyncAccounts.syncAccountsExist(getContext()) ||
FirefoxAccounts.firefoxAccountsExist(getContext()));
EventDispatcher.sendResponse(message, response);
} else if ("fxa".equals(kind)) {
response.put("exists", FirefoxAccounts.firefoxAccountsExist(getContext()));
EventDispatcher.sendResponse(message, response);
} else if ("sync11".equals(kind)) {
response.put("exists", SyncAccounts.syncAccountsExist(getContext()));
EventDispatcher.sendResponse(message, response);
} else {
response.put("error", "Unknown kind");
EventDispatcher.sendError(message, response);
}
} else {
super.handleMessage(event, message);
}
@ -1402,7 +1433,6 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mToast.onSaveInstanceState(outState);
outState.putBoolean(STATE_DYNAMIC_TOOLBAR_ENABLED, mDynamicToolbarEnabled);
outState.putInt(STATE_ABOUT_HOME_TOP_PADDING, mHomePagerContainer.getPaddingTop());
}
@ -1897,7 +1927,7 @@ abstract public class BrowserApp extends GeckoApp
*/
private void addAddonMenuItemToMenu(final Menu menu, final MenuItemInfo info) {
info.added = true;
final Menu destination;
if (info.parent == 0) {
destination = menu;
@ -2061,11 +2091,8 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void openOptionsMenu() {
// Disable menu access in edge cases only accessible to hardware menu buttons.
if ((!hasTabsSideBar() && areTabsShown()) ||
mBrowserToolbar.isEditing()) {
if (!hasTabsSideBar() && areTabsShown())
return;
}
// Scroll custom menu to the top
if (mMenuPanel != null)
@ -2264,7 +2291,7 @@ abstract public class BrowserApp extends GeckoApp
item.setIcon(R.drawable.ic_menu_bookmark_add);
} else {
tab.addBookmark();
mToast.show(false,
getButtonToast().show(false,
getResources().getString(R.string.bookmark_added),
getResources().getString(R.string.bookmark_options),
null,
@ -2550,7 +2577,7 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void onNewTabs(String[] urls) {
final EnumSet<OnUrlOpenListener.Flags> flags = EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB);
for (String url : urls) {
if (!maybeSwitchToTab(url, flags)) {
openUrlAndStopEditing(url, true);

View File

@ -91,6 +91,7 @@ import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AbsoluteLayout;
@ -399,7 +400,7 @@ public abstract class GeckoApp
return mMenuPanel;
}
return super.onCreatePanelView(featureId);
}
@ -491,6 +492,10 @@ public abstract class GeckoApp
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mToast != null) {
mToast.onSaveInstanceState(outState);
}
outState.putBoolean(SAVED_STATE_IN_BACKGROUND, isApplicationInBackground());
outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession);
}
@ -813,6 +818,17 @@ public abstract class GeckoApp
});
}
protected ButtonToast getButtonToast() {
if (mToast != null) {
return mToast;
}
ViewStub toastStub = (ViewStub) findViewById(R.id.toast_stub);
mToast = new ButtonToast(toastStub.inflate());
return mToast;
}
void showButtonToast(final String message, final String buttonText,
final String buttonIcon, final String buttonId) {
BitmapUtils.getDrawable(GeckoApp.this, buttonIcon, new BitmapUtils.BitmapLoader() {
@ -821,7 +837,7 @@ public abstract class GeckoApp
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mToast.show(false, message, buttonText, d, new ButtonToast.ToastListener() {
getButtonToast().show(false, message, buttonText, d, new ButtonToast.ToastListener() {
@Override
public void onButtonClicked() {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Click", buttonId));
@ -1240,8 +1256,6 @@ public abstract class GeckoApp
mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
mToast = new ButtonToast(findViewById(R.id.toast));
// Determine whether we should restore tabs.
mShouldRestore = getSessionRestoreState(savedInstanceState);
if (mShouldRestore && savedInstanceState != null) {

View File

@ -469,6 +469,58 @@ public final class HomeConfig {
};
}
public static enum ItemType implements Parcelable {
ARTICLE("article"),
IMAGE("image");
private final String mId;
ItemType(String id) {
mId = id;
}
public static ItemType fromId(String id) {
if (id == null) {
throw new IllegalArgumentException("Could not convert null String to ItemType");
}
for (ItemType itemType : ItemType.values()) {
if (TextUtils.equals(itemType.mId, id.toLowerCase())) {
return itemType;
}
}
throw new IllegalArgumentException("Could not convert String id to ItemType");
}
@Override
public String toString() {
return mId;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
public static final Creator<ItemType> CREATOR = new Creator<ItemType>() {
@Override
public ItemType createFromParcel(final Parcel source) {
return ItemType.values()[source.readInt()];
}
@Override
public ItemType[] newArray(final int size) {
return new ItemType[size];
}
};
}
public static enum ItemHandler implements Parcelable {
BROWSER("browser"),
INTENT("intent");
@ -524,15 +576,18 @@ public final class HomeConfig {
public static class ViewConfig implements Parcelable {
private final ViewType mType;
private final String mDatasetId;
private final ItemType mItemType;
private final ItemHandler mItemHandler;
private static final String JSON_KEY_TYPE = "type";
private static final String JSON_KEY_DATASET = "dataset";
private static final String JSON_KEY_ITEM_TYPE = "itemType";
private static final String JSON_KEY_ITEM_HANDLER = "itemHandler";
public ViewConfig(JSONObject json) throws JSONException, IllegalArgumentException {
mType = ViewType.fromId(json.getString(JSON_KEY_TYPE));
mDatasetId = json.getString(JSON_KEY_DATASET);
mItemType = ItemType.fromId(json.getString(JSON_KEY_ITEM_TYPE));
mItemHandler = ItemHandler.fromId(json.getString(JSON_KEY_ITEM_HANDLER));
validate();
@ -542,6 +597,7 @@ public final class HomeConfig {
public ViewConfig(Parcel in) {
mType = (ViewType) in.readParcelable(getClass().getClassLoader());
mDatasetId = in.readString();
mItemType = (ItemType) in.readParcelable(getClass().getClassLoader());
mItemHandler = (ItemHandler) in.readParcelable(getClass().getClassLoader());
validate();
@ -550,14 +606,16 @@ public final class HomeConfig {
public ViewConfig(ViewConfig viewConfig) {
mType = viewConfig.mType;
mDatasetId = viewConfig.mDatasetId;
mItemType = viewConfig.mItemType;
mItemHandler = viewConfig.mItemHandler;
validate();
}
public ViewConfig(ViewType type, String datasetId, ItemHandler itemHandler) {
public ViewConfig(ViewType type, String datasetId, ItemType itemType, ItemHandler itemHandler) {
mType = type;
mDatasetId = datasetId;
mItemType = itemType;
mItemHandler = itemHandler;
validate();
@ -572,6 +630,10 @@ public final class HomeConfig {
throw new IllegalArgumentException("Can't create ViewConfig with empty dataset ID");
}
if (mItemType == null) {
throw new IllegalArgumentException("Can't create ViewConfig with null item type");
}
if (mItemHandler == null) {
throw new IllegalArgumentException("Can't create ViewConfig with null item handler");
}
@ -585,6 +647,10 @@ public final class HomeConfig {
return mDatasetId;
}
public ItemType getItemType() {
return mItemType;
}
public ItemHandler getItemHandler() {
return mItemHandler;
}
@ -594,6 +660,7 @@ public final class HomeConfig {
json.put(JSON_KEY_TYPE, mType.toString());
json.put(JSON_KEY_DATASET, mDatasetId);
json.put(JSON_KEY_ITEM_TYPE, mItemType.toString());
json.put(JSON_KEY_ITEM_HANDLER, mItemHandler.toString());
return json;
@ -608,6 +675,7 @@ public final class HomeConfig {
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(mType, 0);
dest.writeString(mDatasetId);
dest.writeParcelable(mItemType, 0);
dest.writeParcelable(mItemHandler, 0);
}

View File

@ -1,60 +0,0 @@
/* -*- 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 java.net.MalformedURLException;
import java.net.URL;
import org.mozilla.gecko.db.BrowserContract.HomeItems;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.R;
import com.squareup.picasso.Picasso;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.FrameLayout;
import android.widget.TextView;
public class PanelGridItemView extends FrameLayout {
private static final String LOGTAG = "GeckoPanelGridItemView";
private final ImageView mThumbnailView;
public PanelGridItemView(Context context) {
this(context, null);
}
public PanelGridItemView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.panelGridItemViewStyle);
}
public PanelGridItemView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater.from(context).inflate(R.layout.panel_grid_item_view, this);
mThumbnailView = (ImageView) findViewById(R.id.image);
}
public void updateFromCursor(Cursor cursor) {
int imageIndex = cursor.getColumnIndexOrThrow(HomeItems.IMAGE_URL);
final String imageUrl = cursor.getString(imageIndex);
Picasso.with(getContext())
.load(imageUrl)
.into(mThumbnailView);
}
}

View File

@ -29,13 +29,13 @@ public class PanelGridView extends GridView
private static final String LOGTAG = "GeckoPanelGridView";
private final ViewConfig mViewConfig;
private final PanelGridViewAdapter mAdapter;
private final PanelViewAdapter mAdapter;
protected OnUrlOpenListener mUrlOpenListener;
public PanelGridView(Context context, ViewConfig viewConfig) {
super(context, null, R.attr.panelGridViewStyle);
mViewConfig = viewConfig;
mAdapter = new PanelGridViewAdapter(context);
mAdapter = new PanelViewAdapter(context, viewConfig.getItemType());
setAdapter(mAdapter);
setOnItemClickListener(new PanelGridItemClickListener());
}
@ -56,24 +56,6 @@ public class PanelGridView extends GridView
mUrlOpenListener = listener;
}
private class PanelGridViewAdapter extends CursorAdapter {
public PanelGridViewAdapter(Context context) {
super(context, null, 0);
}
@Override
public void bindView(View bindView, Context context, Cursor cursor) {
final PanelGridItemView item = (PanelGridItemView) bindView;
item.updateFromCursor(cursor);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new PanelGridItemView(context);
}
}
private class PanelGridItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

View File

@ -0,0 +1,104 @@
/* -*- 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.db.BrowserContract.HomeItems;
import org.mozilla.gecko.home.HomeConfig.ItemType;
import com.squareup.picasso.Picasso;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.ref.WeakReference;
class PanelItemView extends LinearLayout {
private final TextView mTitle;
private final TextView mDescription;
private final ImageView mImage;
private final LinearLayout mTitleDescContainer;
private PanelItemView(Context context, int layoutId) {
super(context);
LayoutInflater.from(context).inflate(layoutId, this);
mTitle = (TextView) findViewById(R.id.title);
mDescription = (TextView) findViewById(R.id.description);
mImage = (ImageView) findViewById(R.id.image);
mTitleDescContainer = (LinearLayout) findViewById(R.id.title_desc_container);
}
public void updateFromCursor(Cursor cursor) {
int titleIndex = cursor.getColumnIndexOrThrow(HomeItems.TITLE);
final String title = cursor.getString(titleIndex);
// Only show title if the item has one
final boolean hasTitle = !TextUtils.isEmpty(title);
mTitleDescContainer.setVisibility(hasTitle ? View.VISIBLE : View.GONE);
if (hasTitle) {
mTitle.setText(title);
int descriptionIndex = cursor.getColumnIndexOrThrow(HomeItems.DESCRIPTION);
final String description = cursor.getString(descriptionIndex);
final boolean hasDescription = !TextUtils.isEmpty(description);
mDescription.setVisibility(hasDescription ? View.VISIBLE : View.GONE);
if (hasDescription) {
mDescription.setText(description);
}
}
int imageIndex = cursor.getColumnIndexOrThrow(HomeItems.IMAGE_URL);
final String imageUrl = cursor.getString(imageIndex);
// Only try to load the image if the item has define image URL
final boolean hasImageUrl = !TextUtils.isEmpty(imageUrl);
mImage.setVisibility(hasImageUrl ? View.VISIBLE : View.GONE);
if (hasImageUrl) {
Picasso.with(getContext())
.load(imageUrl)
.error(R.drawable.favicon)
.into(mImage);
}
}
private static class ArticleItemView extends PanelItemView {
private ArticleItemView(Context context) {
super(context, R.layout.panel_article_item);
setOrientation(LinearLayout.HORIZONTAL);
}
}
private static class ImageItemView extends PanelItemView {
private ImageItemView(Context context) {
super(context, R.layout.panel_image_item);
setOrientation(LinearLayout.VERTICAL);
}
}
public static PanelItemView create(Context context, ItemType itemType) {
switch(itemType) {
case ARTICLE:
return new ArticleItemView(context);
case IMAGE:
return new ImageItemView(context);
default:
throw new IllegalArgumentException("Could not create panel item view from " + itemType);
}
}
}

View File

@ -1,64 +0,0 @@
/* -*- 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.db.BrowserContract.HomeItems;
import com.squareup.picasso.Picasso;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
public class PanelListRow extends TwoLineRow {
private final ImageView mIcon;
public PanelListRow(Context context) {
this(context, null);
}
public PanelListRow(Context context, AttributeSet attrs) {
super(context, attrs);
mIcon = (ImageView) findViewById(R.id.icon);
}
@Override
public void updateFromCursor(Cursor cursor) {
if (cursor == null) {
return;
}
// XXX: This will have to be updated once we come up with the
// final schema for Panel datasets (see bug 942288).
int titleIndex = cursor.getColumnIndexOrThrow(HomeItems.TITLE);
final String title = cursor.getString(titleIndex);
setTitle(title);
int descriptionIndex = cursor.getColumnIndexOrThrow(HomeItems.DESCRIPTION);
final String description = cursor.getString(descriptionIndex);
setDescription(description);
int imageIndex = cursor.getColumnIndexOrThrow(HomeItems.IMAGE_URL);
final String imageUrl = cursor.getString(imageIndex);
final boolean hasImageUrl = !TextUtils.isEmpty(imageUrl);
mIcon.setVisibility(hasImageUrl ? View.VISIBLE : View.GONE);
if (hasImageUrl) {
Picasso.with(getContext())
.load(imageUrl)
.error(R.drawable.favicon)
.into(mIcon);
}
}
}

View File

@ -29,13 +29,13 @@ public class PanelListView extends HomeListView
private static final String LOGTAG = "GeckoPanelListView";
private final PanelListAdapter mAdapter;
private final PanelViewAdapter mAdapter;
private final ViewConfig mViewConfig;
public PanelListView(Context context, ViewConfig viewConfig) {
super(context);
mViewConfig = viewConfig;
mAdapter = new PanelListAdapter(context);
mAdapter = new PanelViewAdapter(context, viewConfig.getItemType());
setAdapter(mAdapter);
setOnItemClickListener(new PanelListItemClickListener());
}
@ -46,23 +46,6 @@ public class PanelListView extends HomeListView
mAdapter.swapCursor(cursor);
}
private class PanelListAdapter extends CursorAdapter {
public PanelListAdapter(Context context) {
super(context, null, 0);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final PanelListRow row = (PanelListRow) view;
row.updateFromCursor(cursor);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.panel_list_row, parent, false);
}
}
private class PanelListItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

View File

@ -0,0 +1,37 @@
/* -*- 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.ItemType;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.View;
import android.view.ViewGroup;
class PanelViewAdapter extends CursorAdapter {
private final Context mContext;
private final ItemType mItemType;
public PanelViewAdapter(Context context, ItemType itemType) {
super(context, null, 0);
mContext = context;
mItemType = itemType;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final PanelItemView item = (PanelItemView) view;
item.updateFromCursor(cursor);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return PanelItemView.create(mContext, mItemType);
}
}

View File

@ -233,12 +233,12 @@ gbjar.sources += [
'home/LastTabsPanel.java',
'home/MostRecentPanel.java',
'home/MultiTypeCursorAdapter.java',
'home/PanelGridItemView.java',
'home/PanelGridView.java',
'home/PanelItemView.java',
'home/PanelLayout.java',
'home/PanelListRow.java',
'home/PanelListView.java',
'home/PanelManager.java',
'home/PanelViewAdapter.java',
'home/PinSiteDialog.java',
'home/ReadingListPanel.java',
'home/SearchEngine.java',

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toast"
style="@style/Toast">
<TextView android:id="@+id/toast_message"
style="@style/ToastMessage" />
<ImageView android:id="@+id/toast_divider"
style="@style/ToastDivider" />
<Button android:id="@+id/toast_button"
style="@style/ToastButton" />
</LinearLayout>

View File

@ -98,18 +98,8 @@
</view>
<LinearLayout android:id="@+id/toast"
style="@style/Toast">
<TextView android:id="@+id/toast_message"
style="@style/ToastMessage" />
<ImageView android:id="@+id/toast_divider"
style="@style/ToastDivider" />
<Button android:id="@+id/toast_button"
style="@style/ToastButton" />
</LinearLayout>
<ViewStub android:id="@+id/toast_stub"
android:layout="@layout/button_toast"
style="@style/Toast"/>
</RelativeLayout>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView android:id="@+id/image"
android:layout_width="54dp"
android:layout_height="44dp"
android:layout_marginTop="10dip"
android:layout_marginLeft="10dip"
android:scaleType="centerCrop"/>
<LinearLayout android:id="@+id/title_desc_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:minHeight="@dimen/page_row_height"
android:orientation="vertical">
<TextView android:id="@+id/title"
style="@style/Widget.PanelItemView.Title"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center_vertical"/>
<TextView android:id="@+id/description"
style="@style/Widget.PanelItemView.Description"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:maxLength="1024"/>
</LinearLayout>
</merge>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<org.mozilla.gecko.widget.SquaredImageView android:id="@+id/image"
style="@style/Widget.PanelGridItemImageView" />
</merge>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<org.mozilla.gecko.widget.SquaredImageView
android:id="@+id/image"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:background="@color/panel_image_item_background"/>
<LinearLayout android:id="@+id/title_desc_container"
android:layout_width="fill_parent"
android:layout_height="@dimen/page_row_height"
android:paddingTop="7dip"
android:paddingBottom="7dip"
android:paddingLeft="5dip"
android:paddingRight="5dip"
android:orientation="vertical">
<TextView android:id="@+id/title"
style="@style/Widget.PanelItemView.Title"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:singleLine="true"/>
<TextView android:id="@+id/description"
style="@style/Widget.PanelItemView.Description"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="3dp"
android:singleLine="true"
android:maxLength="1024"/>
</LinearLayout>
</merge>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<org.mozilla.gecko.home.PanelListRow xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="@dimen/page_row_height"
android:paddingLeft="10dip"
android:minHeight="@dimen/page_row_height"/>

View File

@ -53,18 +53,8 @@
</RelativeLayout>
<LinearLayout android:id="@+id/toast"
style="@style/Toast">
<TextView android:id="@+id/toast_message"
style="@style/ToastMessage" />
<ImageView android:id="@+id/toast_divider"
style="@style/ToastDivider" />
<Button android:id="@+id/toast_button"
style="@style/ToastButton" />
</LinearLayout>
<ViewStub android:id="@+id/toast_stub"
android:layout="@layout/button_toast"
style="@style/Toast"/>
</RelativeLayout>

View File

@ -48,7 +48,6 @@
<item name="topSitesGridItemViewStyle">@style/Widget.TopSitesGridItemView</item>
<item name="topSitesGridViewStyle">@style/Widget.TopSitesGridView</item>
<item name="panelGridViewStyle">@style/Widget.PanelGridView</item>
<item name="panelGridItemViewStyle">@style/Widget.PanelGridItemView</item>
<item name="topSitesThumbnailViewStyle">@style/Widget.TopSitesThumbnailView</item>
<item name="homeListViewStyle">@style/Widget.HomeListView</item>
<item name="geckoMenuListViewStyle">@style/Widget.GeckoMenuListView</item>

View File

@ -9,7 +9,7 @@
<item name="android:fontFamily">sans-serif-light</item>
</style>
<style name="TextAppearance.Widget.TwoLineRow.Title" parent="TextAppearance.Medium">
<style name="TextAppearance.Widget.Home.ItemTitle" parent="TextAppearance.Medium">
<item name="android:fontFamily">sans-serif-light</item>
</style>

View File

@ -41,9 +41,6 @@
<!-- Style for the PanelGridView -->
<attr name="panelGridViewStyle" format="reference" />
<!-- Default style for the PanelGridItemView -->
<attr name="panelGridItemViewStyle" format="reference" />
<!-- Default style for the TopSitesGridView -->
<attr name="topSitesGridViewStyle" format="reference" />

View File

@ -90,6 +90,6 @@
<color name="home_last_tab_bar_bg">#FFF5F7F9</color>
<color name="panel_grid_item_image_background">#D1D9E1</color>
<color name="panel_image_item_background">#D1D9E1</color>
</resources>

View File

@ -112,13 +112,13 @@
<style name="Widget.TwoLineRow" />
<style name="Widget.TwoLineRow.Title">
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLineRow.Title</item>
<item name="android:textAppearance">@style/TextAppearance.Widget.Home.ItemTitle</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">none</item>
</style>
<style name="Widget.TwoLineRow.Description">
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLineRow.Description</item>
<item name="android:textAppearance">@style/TextAppearance.Widget.Home.ItemDescription</item>
<item name="android:includeFontPadding">false</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">middle</item>
@ -130,6 +130,21 @@
<item name="android:drawableLeft">@drawable/bookmark_folder</item>
</style>
<style name="Widget.PanelItemView" />
<style name="Widget.PanelItemView.Title">
<item name="android:textAppearance">@style/TextAppearance.Widget.Home.ItemTitle</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
</style>
<style name="Widget.PanelItemView.Description">
<item name="android:textAppearance">@style/TextAppearance.Widget.Home.ItemDescription</item>
<item name="android:includeFontPadding">false</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
</style>
<style name="Widget.HomeGridView" parent="Widget.GridView">
<item name="android:padding">7dp</item>
<item name="android:horizontalSpacing">0dp</item>
@ -156,19 +171,6 @@
<item name="android:verticalSpacing">2dp</item>
</style>
<style name="Widget.PanelGridItemView">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="Widget.PanelGridItemImageView">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:scaleType">centerCrop</item>
<item name="android:adjustViewBounds">true</item>
<item name="android:background">@color/panel_grid_item_image_background</item>
</style>
<style name="Widget.BookmarkItemView" parent="Widget.TwoLineRow"/>
<style name="Widget.BookmarksListView" parent="Widget.HomeListView"/>
@ -343,14 +345,6 @@
<item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.Widget.TwoLineRow" />
<style name="TextAppearance.Widget.TwoLineRow.Title" parent="TextAppearance.Medium"/>
<style name="TextAppearance.Widget.TwoLineRow.Description" parent="TextAppearance.Micro">
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearance.Widget.SuggestionsPrompt" parent="TextAppearance.Small">
<item name="android:textColor">@color/url_bar_title</item>
</style>
@ -367,6 +361,12 @@
<item name="android:textColor">#00ACFF</item>
</style>
<style name="TextAppearance.Widget.Home.ItemTitle" parent="TextAppearance.Medium"/>
<style name="TextAppearance.Widget.Home.ItemDescription" parent="TextAppearance.Micro">
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearance.Widget.HomeBanner" parent="TextAppearance.Small">
<item name="android:textColor">?android:attr/textColorHint</item>
</style>

View File

@ -81,7 +81,6 @@
<item name="topSitesGridItemViewStyle">@style/Widget.TopSitesGridItemView</item>
<item name="topSitesGridViewStyle">@style/Widget.TopSitesGridView</item>
<item name="panelGridViewStyle">@style/Widget.PanelGridView</item>
<item name="panelGridItemViewStyle">@style/Widget.PanelGridItemView</item>
<item name="topSitesThumbnailViewStyle">@style/Widget.TopSitesThumbnailView</item>
<item name="homeListViewStyle">@style/Widget.HomeListView</item>
<item name="geckoMenuListViewStyle">@style/Widget.GeckoMenuListView</item>

View File

@ -77,6 +77,7 @@ skip-if = processor == "x86"
# [testVkbOverlap] # see bug 907274
# Using JavascriptTest
[testAccounts]
[testBrowserDiscovery]
[testDeviceSearchEngine]
[testJNI]

View File

@ -0,0 +1,28 @@
/* 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.tests;
import org.mozilla.gecko.*;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
public class testAccounts extends JavascriptTest {
public testAccounts() {
super("testAccounts.js");
}
@Override
public void testJavascript() throws Exception {
super.testJavascript();
// Rather than waiting for the JS call to message
// Java and wait for the Activity to launch, we just
// don't test these.
/*
android.app.Activity activity = mSolo.getCurrentActivity();
System.out.println("Current activity: " + activity);
mAsserter.ok(activity instanceof FxAccountGetStartedActivity, "checking activity", "setup activity launched");
*/
}
}

View File

@ -0,0 +1,24 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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/. */
Components.utils.import("resource://gre/modules/Accounts.jsm");
add_task(function test_Accounts() {
let syncExists = yield Accounts.syncAccountsExist();
dump("Sync account exists? " + syncExists + "\n");
let firefoxExists = yield Accounts.firefoxAccountsExist();
dump("Firefox account exists? " + firefoxExists + "\n");
let anyExists = yield Accounts.anySyncAccountsExist();
dump("Any accounts exist? " + anyExists + "\n");
// Only one account should exist.
do_check_true(!syncExists || !firefoxExists);
do_check_eq(anyExists, firefoxExists || syncExists);
dump("Launching setup.\n");
Accounts.launchSetup();
});
run_next_test();

View File

@ -4,6 +4,7 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Accounts.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -292,25 +293,29 @@ function _httpGetRequest(url, callback) {
}
function loadSyncPromoBanner() {
// XXX: Use Accounts.jsm to check if a sync account exists (bug 917942).
let syncAccountExists = false;
if (syncAccountExists) {
// Don't show the promo banner if a sync account already exists.
return;
}
Accounts.anySyncAccountsExist().then(
(exist) => {
// Don't show the banner if sync accounts exist.
if (exist) {
return;
}
let stringBundle = Services.strings.createBundle("chrome://browser/locale/sync.properties");
let text = stringBundle.GetStringFromName("promoBanner.message.text");
let link = stringBundle.GetStringFromName("promoBanner.message.link");
let stringBundle = Services.strings.createBundle("chrome://browser/locale/sync.properties");
let text = stringBundle.GetStringFromName("promoBanner.message.text");
let link = stringBundle.GetStringFromName("promoBanner.message.link");
Home.banner.add({
text: text + "<a href=\"#\">" + link + "</a>",
icon: "drawable://sync_promo",
onclick: function() {
// XXX: Use Accounts.jsm to launch sync set-up activity (bug 917942).
gChromeWin.alert("Launch sync set-up activity!");
Home.banner.add({
text: text + "<a href=\"#\">" + link + "</a>",
icon: "drawable://sync_promo",
onclick: function() {
Accounts.launchSetup();
}
});
},
(err) => {
Cu.reportError("Error checking whether sync account exists: " + err);
}
});
);
}
function Snippets() {}

View File

@ -0,0 +1,76 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["Accounts"];
const { utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
/**
* A promise-based API for querying the existence of Sync accounts,
* and accessing the Sync setup wizard.
*
* Usage:
*
* Cu.import("resource://gre/modules/Accounts.jsm");
* Accounts.anySyncAccountsExist().then(
* (exist) => {
* console.log("Accounts exist? " + exist);
* if (!exist) {
* Accounts.launchSetup();
* }
* },
* (err) => {
* console.log("We failed so hard.");
* }
* );
*/
let Accounts = Object.freeze({
_accountsExist: function (kind) {
let deferred = Promise.defer();
sendMessageToJava({
type: "Accounts:Exist",
kind: kind,
}, (data, error) => {
if (error) {
deferred.reject(error);
} else {
deferred.resolve(JSON.parse(data).exists);
}
});
return deferred.promise;
},
firefoxAccountsExist: function () {
return this._accountsExist("fxa");
},
syncAccountsExist: function () {
return this._accountsExist("sync11");
},
anySyncAccountsExist: function () {
return this._accountsExist("any");
},
/**
* Fire-and-forget: open the Firefox accounts activity, which
* will be the Getting Started screen if FxA isn't yet set up.
*
* There is no return value from this method.
*/
launchSetup: function () {
sendMessageToJava({
type: "Accounts:Create",
});
},
});

View File

@ -228,6 +228,12 @@ let HomePanels = (function () {
REFRESH: "refresh"
}),
// Valid item types for a panel view.
Item: Object.freeze({
ARTICLE: "article",
IMAGE: "image"
}),
// Valid item handlers for a panel view.
ItemHandler: Object.freeze({
BROWSER: "browser",
@ -257,6 +263,18 @@ let HomePanels = (function () {
throw "Home.panels: Invalid view type: panel.id = " + panel.id + ", view.type = " + view.type;
}
if (!view.itemType) {
if (view.type == this.View.LIST) {
// Use ARTICLE item type by default in LIST views
view.itemType = this.Item.ARTICLE;
} else if (view.type == this.View.GRID) {
// Use IMAGE item type by default in GRID views
view.itemType = this.Item.IMAGE;
}
} else if (!_valueExists(this.Item, view.itemType)) {
throw "Home.panels: Invalid item type: panel.id = " + panel.id + ", view.itemType = " + view.itemType;
}
if (!view.itemHandler) {
// Use BROWSER item handler by default
view.itemHandler = this.ItemHandler.BROWSER;

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES += [
'Accounts.jsm',
'ContactService.jsm',
'HelperApps.jsm',
'Home.jsm',

View File

@ -260,6 +260,11 @@ this.isSafeJSObject = function isSafeJSObject(aObj) {
return true; // aObj is not a cross-compartment wrapper.
}
let principal = Services.scriptSecurityManager.getObjectPrincipal(aObj);
if (Services.scriptSecurityManager.isSystemPrincipal(principal)) {
return true; // allow chrome objects
}
return Cu.isXrayWrapper(aObj);
};

View File

@ -3656,7 +3656,7 @@ DebuggerServer.ObjectActorPreviewers.Object = [
},
function DOMNode({obj, threadActor}, aGrip, aRawObj) {
if (!aRawObj || !(aRawObj instanceof Ci.nsIDOMNode)) {
if (obj.class == "Object" || !aRawObj || !(aRawObj instanceof Ci.nsIDOMNode)) {
return false;
}

View File

@ -41,6 +41,9 @@ EXTRA_JS_MODULES += [
'RemoteWebProgress.jsm',
'SelectContentHelper.jsm',
'SelectParentHelper.jsm',
'sessionstore/FormData.jsm',
'sessionstore/ScrollPosition.jsm',
'sessionstore/XPathGenerator.jsm',
'ShortcutUtils.jsm',
'Sntp.jsm',
'SpatialNavigation.jsm',

View File

@ -10,7 +10,7 @@ const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource:///modules/sessionstore/XPathGenerator.jsm");
Cu.import("resource://gre/modules/XPathGenerator.jsm");
/**
* Returns whether the given URL very likely has input

View File

@ -2084,17 +2084,23 @@ var XPIProvider = {
try {
AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_begin");
for (let id in this.bootstrappedAddons) {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.persistentDescriptor = this.bootstrappedAddons[id].descriptor;
let reason = BOOTSTRAP_REASONS.APP_STARTUP;
// Eventually set INSTALLED reason when a bootstrap addon
// is dropped in profile folder and automatically installed
if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
.indexOf(id) !== -1)
reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
this.callBootstrapMethod(id, this.bootstrappedAddons[id].version,
this.bootstrappedAddons[id].type, file,
"startup", reason);
try {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.persistentDescriptor = this.bootstrappedAddons[id].descriptor;
let reason = BOOTSTRAP_REASONS.APP_STARTUP;
// Eventually set INSTALLED reason when a bootstrap addon
// is dropped in profile folder and automatically installed
if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
.indexOf(id) !== -1)
reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
this.callBootstrapMethod(id, this.bootstrappedAddons[id].version,
this.bootstrappedAddons[id].type, file,
"startup", reason);
}
catch (e) {
ERROR("Failed to load bootstrap addon " + id + " from " +
this.bootstrappedAddons[id].descriptor, e);
}
}
AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_end");
}
@ -2161,8 +2167,8 @@ var XPIProvider = {
// add-ons
if (Prefs.getBoolPref(PREF_PENDING_OPERATIONS, false)) {
XPIDatabase.updateActiveAddons();
XPIDatabase.writeAddonsList();
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
!XPIDatabase.writeAddonsList());
}
this.installs = null;
@ -2244,8 +2250,8 @@ var XPIProvider = {
}
// Ensure any changes to the add-ons list are flushed to disk
XPIDatabase.writeAddonsList();
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
!XPIDatabase.writeAddonsList());
},
/**
@ -2420,7 +2426,7 @@ var XPIProvider = {
targetDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
}
catch (e) {
ERROR("Failed to create staging directory for add-on " + id, e);
ERROR("Failed to create staging directory for add-on " + addon.id, e);
continue;
}
@ -2428,7 +2434,7 @@ var XPIProvider = {
extractFiles(stagedXPI, targetDir);
}
catch (e) {
ERROR("Failed to extract staged XPI for add-on " + id + " in " +
ERROR("Failed to extract staged XPI for add-on " + addon.id + " in " +
aLocation.name, e);
}
}
@ -2437,7 +2443,7 @@ var XPIProvider = {
stagedXPI.moveTo(stagingDir, addon.id + ".xpi");
}
catch (e) {
ERROR("Failed to move staged XPI for add-on " + id + " in " +
ERROR("Failed to move staged XPI for add-on " + addon.id + " in " +
aLocation.name, e);
}
}
@ -2455,8 +2461,14 @@ var XPIProvider = {
}
}
if (!stagingDir || !stagingDir.exists() || !stagingDir.isDirectory())
try {
if (!stagingDir || !stagingDir.exists() || !stagingDir.isDirectory())
return;
}
catch (e) {
WARN("Failed to find staging directory", e);
return;
}
let seenFiles = [];
// Use a snapshot of the directory contents to avoid possible issues with
@ -3587,8 +3599,8 @@ var XPIProvider = {
if (extensionListChanged || hasPendingChanges) {
LOG("Updating database with changes to installed add-ons");
XPIDatabase.updateActiveAddons();
XPIDatabase.writeAddonsList();
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
!XPIDatabase.writeAddonsList());
Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
JSON.stringify(this.bootstrappedAddons));
return true;

View File

@ -1409,6 +1409,7 @@ this.XPIDatabase = {
/**
* Writes out the XPI add-ons list for the platform to read.
* @return true if the file was successfully updated, false otherwise
*/
writeAddonsList: function XPIDB_writeAddonsList() {
if (!this.addonDB) {
@ -1473,22 +1474,36 @@ this.XPIDatabase = {
if (fullCount > 0) {
LOG("Writing add-ons list");
let addonsListTmp = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST + ".tmp"],
true);
var fos = FileUtils.openFileOutputStream(addonsListTmp);
fos.write(text, text.length);
fos.close();
addonsListTmp.moveTo(addonsListTmp.parent, FILE_XPI_ADDONS_LIST);
try {
let addonsListTmp = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST + ".tmp"],
true);
var fos = FileUtils.openFileOutputStream(addonsListTmp);
fos.write(text, text.length);
fos.close();
addonsListTmp.moveTo(addonsListTmp.parent, FILE_XPI_ADDONS_LIST);
Services.prefs.setCharPref(PREF_EM_ENABLED_ADDONS, enabledAddons.join(","));
Services.prefs.setCharPref(PREF_EM_ENABLED_ADDONS, enabledAddons.join(","));
}
catch (e) {
ERROR("Failed to write add-ons list to " + addonsListTmp.parent + "/" +
FILE_XPI_ADDONS_LIST, e);
return false;
}
}
else {
if (addonsList.exists()) {
LOG("Deleting add-ons list");
addonsList.remove(false);
try {
addonsList.remove(false);
}
catch (e) {
ERROR("Failed to remove " + addonsList.path, e);
return false;
}
}
Services.prefs.clearUserPref(PREF_EM_ENABLED_ADDONS);
}
return true;
}
};

View File

@ -7695,47 +7695,65 @@
!ifdef MOZ_METRO
; Removes the CEH registration if it's set to our installation directory.
; If it's set to some other installation directory, then it should be removed
; by that installation.
; by that installation.
!macro RemoveDEHRegistrationIfMatchingCall un
Function ${un}RemoveDEHRegistrationIfMatchingCall
; Move the old $R0 on the stack and set it to DEH ID
Exch $R0
; Backup the old values of R8 and R7 on the stack
Push $R8
Push $R7
; Retrieve DEH ID from the stack into $R9
Exch $R9
Exch 1
; Retrieve Protocol Activation ID from stack into $R8
Exch $R8
Exch 2
; Retrieve File Activation ID from stack into $R7
Exch $R7
; Backup the old values of R6 and R5 on the stack
Push $R6
Push $R5
; Conditionally remove the DEH as long as we are the default (HKCU)
ReadRegStr $R8 HKCU "Software\Classes\CLSID\$R0\LocalServer32" ""
${${un}GetLongPath} "$INSTDIR" $R7
StrCmp "$R8" "" next +1
IfFileExists "$R8" +1 clearHKCU
${${un}GetParent} "$R8" $R8
${${un}GetLongPath} "$R8" $R8
StrCmp "$R7" "$R8" clearHKCU next
ReadRegStr $R6 HKCU "Software\Classes\CLSID\$R9\LocalServer32" ""
${${un}GetLongPath} "$INSTDIR" $R5
StrCmp "$R6" "" next +1
IfFileExists "$R6" +1 clearHKCU
${${un}GetParent} "$R6" $R6
${${un}GetLongPath} "$R6" $R6
StrCmp "$R5" "$R6" clearHKCU next
clearHKCU:
DeleteRegKey HKCU "Software\Classes\CLSID\$R0"
DeleteRegKey HKCU "Software\Classes\CLSID\$R9"
DeleteRegValue HKCU "Software\Classes\$R8\shell\open\command" "DelegateExecute"
DeleteRegValue HKCU "Software\Classes\$R7\shell\open\command" "DelegateExecute"
next:
; Conditionally remove the DEH as long as we are the default (HKLM)
ReadRegStr $R8 HKLM "Software\Classes\CLSID\$R0\LocalServer32" ""
${${un}GetLongPath} "$INSTDIR" $R7
StrCmp "$R8" "" done +1
IfFileExists "$R8" +1 clearHKLM
${${un}GetParent} "$R8" $R8
${${un}GetLongPath} "$R8" $R8
StrCmp "$R7" "$R8" clearHKLM done
ReadRegStr $R6 HKLM "Software\Classes\CLSID\$R9\LocalServer32" ""
${${un}GetLongPath} "$INSTDIR" $R5
StrCmp "$R6" "" done +1
IfFileExists "$R6" +1 clearHKLM
${${un}GetParent} "$R6" $R6
${${un}GetLongPath} "$R6" $R6
StrCmp "$R5" "$R6" clearHKLM done
clearHKLM:
DeleteRegKey HKLM "Software\Classes\CLSID\$R0"
DeleteRegKey HKLM "Software\Classes\CLSID\$R9"
DeleteRegValue HKLM "Software\Classes\$R8\shell\open\command" "DelegateExecute"
DeleteRegValue HKLM "Software\Classes\$R7\shell\open\command" "DelegateExecute"
done:
; Always remove the AppUserModelID keys for this installation
DeleteRegKey HKCU "Software\Classes\$AppUserModelID"
DeleteRegKey HKLM "Software\Classes\$AppUserModelID"
; Restore the stack back to its original state
Pop $R7
Pop $R8
Pop $R0
; Restore the registers back to their original state
Pop $R5
Pop $R6
Exch $R7
Exch 2
Exch $R8
Exch 1
Exch $R9
FunctionEnd
!macroend
@ -7747,7 +7765,11 @@
!insertmacro RemoveDEHRegistrationIfMatchingCall "un."
!macroend
!macro CleanupMetroBrowserHandlerValues un DELEGATE_EXECUTE_HANDLER_ID
!macro CleanupMetroBrowserHandlerValues un DELEGATE_EXECUTE_HANDLER_ID \
PROTOCOL_ACTIVATION_ID \
FILE_ACTIVATION_ID
Push ${FILE_ACTIVATION_ID}
Push ${PROTOCOL_ACTIVATION_ID}
Push ${DELEGATE_EXECUTE_HANDLER_ID}
Call ${un}RemoveDEHRegistrationIfMatchingCall
!macroend