mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c.
This commit is contained in:
commit
8190806d88
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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 += [
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -91,7 +91,7 @@ function test()
|
||||
},
|
||||
{
|
||||
name: "console.dir output",
|
||||
consoleDir: "XULDocument {",
|
||||
consoleDir: /XULDocument .+ chrome:\/\/.+\/browser\.xul/,
|
||||
},
|
||||
{
|
||||
name: "console.time output",
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
@ -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();
|
||||
|
@ -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.");
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
=============== ==================================== =================
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
104
mobile/android/base/home/PanelItemView.java
Normal file
104
mobile/android/base/home/PanelItemView.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
37
mobile/android/base/home/PanelViewAdapter.java
Normal file
37
mobile/android/base/home/PanelViewAdapter.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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',
|
||||
|
19
mobile/android/base/resources/layout/button_toast.xml
Normal file
19
mobile/android/base/resources/layout/button_toast.xml
Normal 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>
|
@ -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>
|
||||
|
41
mobile/android/base/resources/layout/panel_article_item.xml
Normal file
41
mobile/android/base/resources/layout/panel_article_item.xml
Normal 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>
|
@ -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>
|
44
mobile/android/base/resources/layout/panel_image_item.xml
Normal file
44
mobile/android/base/resources/layout/panel_image_item.xml
Normal 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>
|
@ -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"/>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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" />
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -77,6 +77,7 @@ skip-if = processor == "x86"
|
||||
# [testVkbOverlap] # see bug 907274
|
||||
|
||||
# Using JavascriptTest
|
||||
[testAccounts]
|
||||
[testBrowserDiscovery]
|
||||
[testDeviceSearchEngine]
|
||||
[testJNI]
|
||||
|
28
mobile/android/base/tests/testAccounts.java
Normal file
28
mobile/android/base/tests/testAccounts.java
Normal 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");
|
||||
*/
|
||||
}
|
||||
}
|
24
mobile/android/base/tests/testAccounts.js
Normal file
24
mobile/android/base/tests/testAccounts.js
Normal 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();
|
@ -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() {}
|
||||
|
76
mobile/android/modules/Accounts.jsm
Normal file
76
mobile/android/modules/Accounts.jsm
Normal 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",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user