Merge m-c to inbound

This commit is contained in:
Wes Kocher 2013-09-27 20:38:44 -07:00
commit 13a762accd
71 changed files with 1346 additions and 294 deletions

View File

@ -184,10 +184,14 @@ pref("content.sink.perf_deflect_count", 1000000);
pref("content.sink.perf_parse_time", 50000000);
// Maximum scripts runtime before showing an alert
pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
// Disable the watchdog thread for B2G. See bug 870043 comment 31.
pref("dom.use_watchdog", false);
// The slow script dialog can be triggered from inside the JS engine as well,
// ensure that those calls don't accidentally trigger the dialog.
pref("dom.max_script_run_time", 0);
pref("dom.max_chrome_script_run_time", 0);
// plugins
pref("plugin.disable", true);
pref("dom.ipc.plugins.enabled", true);
@ -433,7 +437,7 @@ pref("services.push.udp.wakeupEnabled", true);
#ifdef MOZ_B2G_RIL
pref("dom.mozNetworkStats.enabled", true);
pref("ril.cellbroadcast.disabled", false);
pref("dom.webapps.firstRunWithSIM", false);
pref("dom.webapps.firstRunWithSIM", true);
#endif
// WebSettings
@ -448,6 +452,9 @@ pref("media.realtime_decoder.enabled", true);
// TCPSocket
pref("dom.mozTCPSocket.enabled", true);
// WebPayment
pref("dom.mozPay.enabled", true);
// "Preview" landing of bug 710563, which is bogged down in analysis
// of talos regression. This is a needed change for higher-framerate
// CSS animations, and incidentally works around an apparent bug in
@ -794,6 +801,9 @@ pref("dom.inter-app-communication-api.enabled", true);
// 0 disables the timer.
pref("b2g.adb.timeout-hours", 12);
// InputMethod so we can do soft keyboards
pref("dom.mozInputMethod.enabled", true);
// Absolute path to the devtool unix domain socket file used
// to communicate with a usb cable via adb forward
pref("devtools.debugger.unix-domain-socket", "/data/local/debugger-socket");

View File

@ -15,14 +15,6 @@ component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
#endif
# MozKeyboard.js
component {397a7fdf-2254-47be-b74e-76625a1a66d5} MozKeyboard.js
contract @mozilla.org/b2g-keyboard;1 {397a7fdf-2254-47be-b74e-76625a1a66d5}
category JavaScript-navigator-property mozKeyboard @mozilla.org/b2g-keyboard;1
component {4607330d-e7d2-40a4-9eb8-43967eae0142} MozKeyboard.js
contract @mozilla.org/b2g-inputmethod;1 {4607330d-e7d2-40a4-9eb8-43967eae0142}
# DirectoryProvider.js
component {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5} DirectoryProvider.js
contract @mozilla.org/browser/directory-provider;1 {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}

View File

@ -13,7 +13,11 @@ const Cr = Components.results;
const Cu = Components.utils;
const Cc = Components.classes;
const PROMPT_FOR_UNKNOWN = ["geolocation", "desktop-notification", "audio-capture"];
const PROMPT_FOR_UNKNOWN = ["geolocation", "desktop-notification",
"audio-capture"];
// Due to privary issue, permission requests like GetUserMedia should prompt
// every time instead of providing session persistence.
const PERMISSION_NO_SESSION = ["audio-capture"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -43,7 +47,7 @@ function rememberPermission(aPermission, aPrincipal, aSession)
permissionManager.addFromPrincipal(aPrincipal,
aPerm,
Ci.nsIPermissionManager.ALLOW_ACTION);
} else {
} else if (PERMISSION_NO_SESSION.indexOf(aPermission) < 0) {
permissionManager.addFromPrincipal(aPrincipal,
aPerm,
Ci.nsIPermissionManager.ALLOW_ACTION,

View File

@ -4,77 +4,9 @@
#include "domstubs.idl"
interface nsIDOMDOMRequest;
[scriptable, uuid(3615a616-571d-4194-bf54-ccf546067b14)]
interface nsIB2GCameraContent : nsISupports
{
/* temporary solution, waiting for getUserMedia */
DOMString getCameraURI([optional] in jsval options);
};
[scriptable, uuid(40ad96b2-9efa-41fb-84c7-fbcec9b153f0)]
interface nsIB2GKeyboard : nsISupports
{
void sendKey(in long keyCode, in long charCode);
// Select the <select> option specified by index.
// If this method is called on a <select> that support multiple
// selection, then the option specified by index will be added to
// the selection.
// If this method is called for a select that does not support multiple
// selection the previous element will be unselected.
void setSelectedOption(in jsval index);
// Select the <select> options specified by indexes. All other options
// will be deselected.
// If this method is called for a <select> that does not support multiple
// selection, then the last index specified in indexes will be selected.
void setSelectedOptions(in jsval indexes);
// Set the value on the currently focused element. This has to be used
// for special situations where the value had to be chosen amongst a
// list (type=month) or a widget (type=date, time, etc.).
// If the value passed in parameter isn't valid (in the term of HTML5
// Forms Validation), the value will simply be ignored by the element.
void setValue(in jsval value);
void removeFocus();
attribute nsIDOMEventListener onfocuschange;
// Fires when user moves the cursor, changes the selection, or alters the
// composing text length
attribute nsIDOMEventListener onselectionchange;
// The start position of the selection.
readonly attribute long selectionStart;
// The stop position of the selection.
readonly attribute long selectionEnd;
/*
* Set the selection range of the the editable text.
*
* @param start The beginning of the selected text.
* @param end The end of the selected text.
*
* Note that the start position should be less or equal to the end position.
* To move the cursor, set the start and end position to the same value.
*/
void setSelectionRange(in long start, in long end);
/*
* Replace text around the beginning of the current selection range of the
* editable text.
*
* @param text The string to be replaced with.
* @param beforeLength The number of characters to be deleted before the
* beginning of the current selection range. Defaults to 0.
* @param afterLength The number of characters to be deleted after the
* beginning of the current selection range. Defaults to 0.
*/
void replaceSurroundingText(in DOMString text,
[optional] in long beforeLength,
[optional] in long afterLength);
};

View File

@ -20,7 +20,6 @@ EXTRA_COMPONENTS += [
'ContentPermissionPrompt.js',
'FilePicker.js',
'MailtoProtocolHandler.js',
'MozKeyboard.js',
'PaymentGlue.js',
'ProcessGlobal.js',
'SmsProtocolHandler.js',
@ -42,7 +41,6 @@ if CONFIG['MOZ_UPDATER']:
EXTRA_JS_MODULES += [
'ErrorPage.jsm',
'Keyboard.jsm',
'SignInToWebsite.jsm',
'TelURIParser.jsm',
'WebappsUpdater.jsm',

View File

@ -1,4 +1,4 @@
{
"revision": "6b82ab58ac7e7cbce89a6718659311a1ce6b7436",
"revision": "e836e146aa9bdc2b4141dfa86662eed46d735fb2",
"repo_path": "/integration/gaia-central"
}

View File

@ -190,6 +190,7 @@
@BINPATH@/components/dom_notification.xpt
@BINPATH@/components/dom_html.xpt
@BINPATH@/components/dom_indexeddb.xpt
@BINPATH@/components/dom_inputmethod.xpt
@BINPATH@/components/dom_offline.xpt
@BINPATH@/components/dom_payment.xpt
@BINPATH@/components/dom_json.xpt
@ -547,6 +548,10 @@
@BINPATH@/components/PaymentRequestInfo.js
@BINPATH@/components/Payment.manifest
; InputMethod API
@BINPATH@/components/MozKeyboard.js
@BINPATH@/components/InputMethod.manifest
; Modules
@BINPATH@/modules/*
@ -748,7 +753,6 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/UpdatePrompt.js
#endif
@BINPATH@/components/WebappsUpdateTimer.js
@BINPATH@/components/MozKeyboard.js
@BINPATH@/components/DirectoryProvider.js
@BINPATH@/components/ActivitiesGlue.js
@BINPATH@/components/ProcessGlobal.js

View File

@ -58,3 +58,5 @@ if test "$OS_TARGET" = "WINNT" -o "$OS_TARGET" = "Darwin"; then
MOZ_FOLD_LIBS=1
fi
MOZ_WEBGL_CONFORMANT=1
# Enable navigator.mozPay
MOZ_PAY=1

View File

@ -9,7 +9,11 @@ MOCHITEST_BROWSER_FILES = \
browser_observableobject.js \
browser_layoutHelpers.js \
browser_require_basic.js \
browser_telemetry_buttonsandsidebar.js \
browser_telemetry_sidebar.js \
browser_telemetry_button_responsive.js \
browser_telemetry_button_scratchpad.js \
browser_telemetry_button_tilt.js \
browser_telemetry_button_paintflashing.js \
browser_telemetry_toolboxtabs_inspector.js \
browser_telemetry_toolboxtabs_jsdebugger.js \
browser_telemetry_toolboxtabs_jsprofiler.js \

View File

@ -0,0 +1,127 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_paintflashing.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
function init() {
Telemetry.prototype.telemetryInfo = {};
Telemetry.prototype._oldlog = Telemetry.prototype.log;
Telemetry.prototype.log = function(histogramId, value) {
if (histogramId) {
if (!this.telemetryInfo[histogramId]) {
this.telemetryInfo[histogramId] = [];
}
this.telemetryInfo[histogramId].push(value);
}
};
testButton("command-button-paintflashing");
}
function testButton(id) {
info("Testing " + id);
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
info("inspector opened");
let button = toolbox.doc.querySelector("#" + id);
ok(button, "Captain, we have the button");
delayedClicks(button, 4).then(function() {
checkResults("_PAINTFLASHING_");
});
}).then(null, console.error);
}
function delayedClicks(node, clicks) {
let deferred = promise.defer();
let clicked = 0;
// See TOOL_DELAY for why we need setTimeout here
setTimeout(function delayedClick() {
info("Clicking button " + node.id);
node.click();
clicked++;
if (clicked >= clicks) {
deferred.resolve(node);
} else {
setTimeout(delayedClick, TOOL_DELAY);
}
}, TOOL_DELAY);
return deferred.promise;
}
function checkResults(histIdFocus) {
let result = Telemetry.prototype.telemetryInfo;
for (let [histId, value] of Iterator(result)) {
if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
!histId.contains(histIdFocus)) {
// Inspector stats are tested in
// browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
// because we only open the inspector once for this test.
continue;
}
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
ok(value.length === 1 && value[0] === true,
"Per user value " + histId + " has a single value of true");
} else if (histId.endsWith("OPENED_BOOLEAN")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element === true;
});
ok(okay, "All " + histId + " entries are === true");
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element > 0;
});
ok(okay, "All " + histId + " entries have time > 0");
}
}
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();
Telemetry.prototype.log = Telemetry.prototype._oldlog;
delete Telemetry.prototype._oldlog;
delete Telemetry.prototype.telemetryInfo;
TargetFactory = Services = promise = require = null;
finish();
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForFocus(init, content);
}, true);
content.location = TEST_URI;
}

View File

@ -0,0 +1,127 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_responsive.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
function init() {
Telemetry.prototype.telemetryInfo = {};
Telemetry.prototype._oldlog = Telemetry.prototype.log;
Telemetry.prototype.log = function(histogramId, value) {
if (histogramId) {
if (!this.telemetryInfo[histogramId]) {
this.telemetryInfo[histogramId] = [];
}
this.telemetryInfo[histogramId].push(value);
}
};
testButton("command-button-responsive");
}
function testButton(id) {
info("Testing " + id);
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
info("inspector opened");
let button = toolbox.doc.querySelector("#" + id);
ok(button, "Captain, we have the button");
delayedClicks(button, 4).then(function() {
checkResults("_RESPONSIVE_");
});
}).then(null, console.error);
}
function delayedClicks(node, clicks) {
let deferred = promise.defer();
let clicked = 0;
// See TOOL_DELAY for why we need setTimeout here
setTimeout(function delayedClick() {
info("Clicking button " + node.id);
node.click();
clicked++;
if (clicked >= clicks) {
deferred.resolve(node);
} else {
setTimeout(delayedClick, TOOL_DELAY);
}
}, TOOL_DELAY);
return deferred.promise;
}
function checkResults(histIdFocus) {
let result = Telemetry.prototype.telemetryInfo;
for (let [histId, value] of Iterator(result)) {
if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
!histId.contains(histIdFocus)) {
// Inspector stats are tested in
// browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
// because we only open the inspector once for this test.
continue;
}
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
ok(value.length === 1 && value[0] === true,
"Per user value " + histId + " has a single value of true");
} else if (histId.endsWith("OPENED_BOOLEAN")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element === true;
});
ok(okay, "All " + histId + " entries are === true");
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element > 0;
});
ok(okay, "All " + histId + " entries have time > 0");
}
}
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();
Telemetry.prototype.log = Telemetry.prototype._oldlog;
delete Telemetry.prototype._oldlog;
delete Telemetry.prototype.telemetryInfo;
TargetFactory = Services = promise = require = null;
finish();
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForFocus(init, content);
}, true);
content.location = TEST_URI;
}

View File

@ -0,0 +1,155 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_scratchpad.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
let numScratchpads = 0;
function init() {
Telemetry.prototype.telemetryInfo = {};
Telemetry.prototype._oldlog = Telemetry.prototype.log;
Telemetry.prototype.log = function(histogramId, value) {
if (histogramId) {
if (!this.telemetryInfo[histogramId]) {
this.telemetryInfo[histogramId] = [];
}
this.telemetryInfo[histogramId].push(value);
}
};
Services.ww.registerNotification(windowObserver);
testButton("command-button-scratchpad");
}
function testButton(id) {
info("Testing " + id);
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
info("inspector opened");
let button = toolbox.doc.querySelector("#" + id);
ok(button, "Captain, we have the button");
delayedClicks(button, 4).then(null, console.error);
}).then(null, console.error);
}
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
if (win.Scratchpad) {
win.Scratchpad.addObserver({
onReady: function() {
win.Scratchpad.removeObserver(this);
numScratchpads++;
win.close();
info("another scratchpad was opened and closed, count is now " + numScratchpads);
if (numScratchpads === 4) {
Services.ww.unregisterNotification(windowObserver);
info("4 scratchpads have been opened and closed, checking results");
checkResults("_SCRATCHPAD_");
}
},
});
}
}, false);
}
}
function delayedClicks(node, clicks) {
let deferred = promise.defer();
let clicked = 0;
// See TOOL_DELAY for why we need setTimeout here
setTimeout(function delayedClick() {
info("Clicking button " + node.id);
node.click();
clicked++;
if (clicked >= clicks) {
deferred.resolve(node);
} else {
setTimeout(delayedClick, TOOL_DELAY);
}
}, TOOL_DELAY);
return deferred.promise;
}
function checkResults(histIdFocus) {
let result = Telemetry.prototype.telemetryInfo;
for (let [histId, value] of Iterator(result)) {
if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
!histId.contains(histIdFocus)) {
// Inspector stats are tested in
// browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
// because we only open the inspector once for this test.
continue;
}
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
ok(value.length === 1 && value[0] === true,
"Per user value " + histId + " has a single value of true");
} else if (histId.endsWith("OPENED_BOOLEAN")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element === true;
});
ok(okay, "All " + histId + " entries are === true");
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element > 0;
});
ok(okay, "All " + histId + " entries have time > 0");
}
}
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();
Telemetry.prototype.log = Telemetry.prototype._oldlog;
delete Telemetry.prototype._oldlog;
delete Telemetry.prototype.telemetryInfo;
TargetFactory = Services = promise = require = numScratchpads = null;
finish();
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForFocus(init, content);
}, true);
content.location = TEST_URI;
}

View File

@ -0,0 +1,127 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_button_tilt.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
const TOOL_DELAY = 200;
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
let require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
let Telemetry = require("devtools/shared/telemetry");
function init() {
Telemetry.prototype.telemetryInfo = {};
Telemetry.prototype._oldlog = Telemetry.prototype.log;
Telemetry.prototype.log = function(histogramId, value) {
if (histogramId) {
if (!this.telemetryInfo[histogramId]) {
this.telemetryInfo[histogramId] = [];
}
this.telemetryInfo[histogramId].push(value);
}
};
testButton("command-button-tilt");
}
function testButton(id) {
info("Testing " + id);
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
info("inspector opened");
let button = toolbox.doc.querySelector("#" + id);
ok(button, "Captain, we have the button");
delayedClicks(button, 4).then(function() {
checkResults("_TILT_");
});
}).then(null, console.error);
}
function delayedClicks(node, clicks) {
let deferred = promise.defer();
let clicked = 0;
// See TOOL_DELAY for why we need setTimeout here
setTimeout(function delayedClick() {
info("Clicking button " + node.id);
node.click();
clicked++;
if (clicked >= clicks) {
deferred.resolve(node);
} else {
setTimeout(delayedClick, TOOL_DELAY);
}
}, TOOL_DELAY);
return deferred.promise;
}
function checkResults(histIdFocus) {
let result = Telemetry.prototype.telemetryInfo;
for (let [histId, value] of Iterator(result)) {
if (histId.startsWith("DEVTOOLS_INSPECTOR_") ||
!histId.contains(histIdFocus)) {
// Inspector stats are tested in
// browser_telemetry_toolboxtabs_{toolname}.js so we skip them here
// because we only open the inspector once for this test.
continue;
}
if (histId.endsWith("OPENED_PER_USER_FLAG")) {
ok(value.length === 1 && value[0] === true,
"Per user value " + histId + " has a single value of true");
} else if (histId.endsWith("OPENED_BOOLEAN")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element === true;
});
ok(okay, "All " + histId + " entries are === true");
} else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
ok(value.length > 1, histId + " has more than one entry");
let okay = value.every(function(element) {
return element > 0;
});
ok(okay, "All " + histId + " entries have time > 0");
}
}
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();
Telemetry.prototype.log = Telemetry.prototype._oldlog;
delete Telemetry.prototype._oldlog;
delete Telemetry.prototype.telemetryInfo;
TargetFactory = Services = promise = require = null;
finish();
}
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForFocus(init, content);
}, true);
content.location = TEST_URI;
}

View File

@ -1,7 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_buttonsandsidebar.js</p>";
const TEST_URI = "data:text/html;charset=utf-8,<p>browser_telemetry_sidebar.js</p>";
// Because we need to gather stats for the period of time that a tool has been
// opened we make use of setTimeout() to create tool active times.
@ -24,66 +24,9 @@ function init() {
this.telemetryInfo[histogramId].push(value);
}
}
};
testButtons();
}
function testButtons() {
info("Testing buttons");
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
let container = toolbox.doc.getElementById("toolbox-buttons");
let buttons = container.getElementsByTagName("toolbarbutton");
// Copy HTMLCollection to array.
buttons = Array.prototype.slice.call(buttons);
(function testButton() {
let button = buttons.pop();
if (button) {
info("Clicking button " + button.id);
button.click();
delayedClicks(button, 3).then(function(button) {
if (buttons.length == 0) {
// Remove scratchpads
let wins = Services.wm.getEnumerator("devtools:scratchpad");
while (wins.hasMoreElements()) {
let win = wins.getNext();
info("Closing scratchpad window");
win.close();
}
testSidebar();
} else {
setTimeout(testButton, TOOL_DELAY);
}
});
}
})();
}).then(null, reportError);
}
function delayedClicks(node, clicks) {
let deferred = promise.defer();
let clicked = 0;
setTimeout(function delayedClick() {
info("Clicking button " + node.id);
node.click();
clicked++;
if (clicked >= clicks) {
deferred.resolve(node);
} else {
setTimeout(delayedClick, TOOL_DELAY);
}
}, TOOL_DELAY);
return deferred.promise;
testSidebar();
}
function testSidebar() {
@ -98,6 +41,7 @@ function testSidebar() {
// Concatenate the array with itself so that we can open each tool twice.
sidebarTools.push.apply(sidebarTools, sidebarTools);
// See TOOL_DELAY for why we need setTimeout here
setTimeout(function selectSidebarTab() {
let tool = sidebarTools.pop();
if (tool) {
@ -147,14 +91,6 @@ function checkResults() {
finishUp();
}
function reportError(error) {
let stack = " " + error.stack.replace(/\n?.*?@/g, "\n JS frame :: ");
ok(false, "ERROR: " + error + " at " + error.fileName + ":" +
error.lineNumber + "\n\nStack trace:" + stack);
finishUp();
}
function finishUp() {
gBrowser.removeCurrentTab();

View File

@ -4,6 +4,7 @@
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let TargetFactory = devtools.TargetFactory;
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
/**
* Open a new tab at a URL and call a callback on load

View File

@ -200,6 +200,7 @@
@BINPATH@/components/dom_notification.xpt
@BINPATH@/components/dom_html.xpt
@BINPATH@/components/dom_indexeddb.xpt
@BINPATH@/components/dom_inputmethod.xpt
@BINPATH@/components/dom_offline.xpt
@BINPATH@/components/dom_json.xpt
@BINPATH@/components/dom_power.xpt
@ -222,6 +223,7 @@
#ifdef MOZ_GAMEPAD
@BINPATH@/components/dom_gamepad.xpt
#endif
@BINPATH@/components/dom_payment.xpt
@BINPATH@/components/downloads.xpt
@BINPATH@/components/editor.xpt
@BINPATH@/components/embed_base.xpt
@ -539,6 +541,11 @@
@BINPATH@/components/AppProtocolHandler.js
@BINPATH@/components/AppProtocolHandler.manifest
@BINPATH@/components/Payment.js
@BINPATH@/components/PaymentFlowInfo.js
@BINPATH@/components/PaymentRequestInfo.js
@BINPATH@/components/Payment.manifest
#ifdef MOZ_WEBRTC
@BINPATH@/components/PeerConnection.js
@BINPATH@/components/PeerConnection.manifest
@ -556,6 +563,10 @@
@BINPATH@/components/dom_webspeechsynth.xpt
#endif
; InputMethod API
@BINPATH@/components/MozKeyboard.js
@BINPATH@/components/InputMethod.manifest
; Modules
@BINPATH@/browser/modules/*
@BINPATH@/modules/*
@ -767,6 +778,7 @@ bin/libfreebl_32int64_3.so
@BINPATH@/webapprt/components/CommandLineHandler.js
@BINPATH@/webapprt/components/ContentPermission.js
@BINPATH@/webapprt/components/DirectoryProvider.js
@BINPATH@/webapprt/components/PaymentUIGlue.js
@BINPATH@/webapprt/components/components.manifest
@BINPATH@/webapprt/defaults/preferences/prefs.js
@BINPATH@/webapprt/modules/Startup.jsm

View File

@ -183,6 +183,12 @@ public:
if (!mReadThread) {
nsresult rv = NS_NewNamedThread("Media Encoder", getter_AddRefs(mReadThread));
if (NS_FAILED(rv)) {
if (mInputPort.get()) {
mInputPort->Destroy();
}
if (mTrackUnionStream.get()) {
mTrackUnionStream->Destroy();
}
mRecorder->NotifyError(rv);
return;
}
@ -363,6 +369,16 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
return;
}
if (!mStream->GetPrincipal()) {
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (!CheckPrincipal()) {
aResult.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
int32_t timeSlice = 0;
if (aTimeSlice.WasPassed()) {
if (aTimeSlice.Value() < 0) {
@ -373,11 +389,6 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
timeSlice = aTimeSlice.Value();
}
if (!CheckPrincipal()) {
aResult.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
mState = RecordingState::Recording;
// Start a session
mSession = new Session(this, timeSlice);

View File

@ -113,6 +113,7 @@ TextTrack::AddRegion(TextTrackRegion& aRegion)
{
TextTrackRegion* region = mRegionList->GetRegionById(aRegion.Id());
if (!region) {
aRegion.SetTextTrack(this);
mRegionList->AddTextTrackRegion(&aRegion);
return;
}

View File

@ -4,7 +4,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/TextTrack.h"
#include "mozilla/dom/TextTrackRegion.h"
#include "mozilla/dom/VTTRegionBinding.h"

View File

@ -12,6 +12,7 @@
#include "nsString.h"
#include "nsWrapperCache.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/TextTrack.h"
namespace mozilla {
namespace dom {
@ -144,6 +145,11 @@ public:
/** end WebIDL Methods. */
void SetTextTrack(TextTrack* aTrack)
{
mTrack = aTrack;
}
// Helper to aid copying of a given TextTrackRegion's width, lines,
// anchor, viewport and scroll values.
void CopyValues(TextTrackRegion& aRegion);

View File

@ -121,6 +121,7 @@ MOCHITEST_FILES = \
test_mediarecorder_record_timeslice.html \
test_mediarecorder_record_audiocontext.html \
test_mediarecorder_record_stopms.html \
test_mediarecorder_record_nosrc.html \
test_mozHasAudio.html \
test_source_media.html \
test_autoplay_contentEditable.html \

View File

@ -0,0 +1,33 @@
<html>
<head>
<title>Bug 919051 - Media Recording - memory leak when record a media stream without any tracks</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=919051">Mozill
a Bug 919051</a>
<pre id="test">
<script class="testbody" type="text/javascript">
function startTest() {
var mediastream = document.createElement('video').mozCaptureStream();
var mr = new MediaRecorder(mediastream);
info('create MediaRecorder');
try {
mr.start();
ok(false, 'should get exception');
} catch(e) {
is(e.name, 'InvalidStateError', "get correct exception");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
startTest();
</script>
</head>
</html>

View File

@ -54,6 +54,7 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true]]},
is(region.viewportAnchorX, 10, "Region viewportAnchorX should be 10.");
is(region.viewportAnchorY, 90, "Region viewportAnchorY should be 90.");
is(region.scroll, "up", "Region scroll should be 'up'");
is(region.track, trackElement.track, "Tracks should be equal.");
SimpleTest.finish();
});

View File

@ -403,7 +403,9 @@ NS_IMETHODIMP nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword,
// the search engine's name through various function calls.
nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
if (obsSvc) {
obsSvc->NotifyObservers(defaultEngine, "keyword-search", NS_ConvertUTF8toUTF16(keyword).get());
// Note that "keyword-search" refers to a search via the url
// bar, not a bookmarks keyword search.
obsSvc->NotifyObservers(defaultEngine, "keyword-search", NS_ConvertUTF8toUTF16(keyword).get());
}
return submission->GetUri(aURI);

View File

@ -232,6 +232,7 @@ this.OperatorAppsRegistry = {
}
this.eraseVariantAppsNotInList(aIdsApp);
Services.prefs.setBoolPref(PREF_FIRST_RUN_WITH_SIM, false);
Services.prefs.savePrefFile(null);
}.bind(this)).then(null, function(aError) {
debug("Error: " + aError);
});

View File

@ -42,7 +42,7 @@ var PackagedTestHelper = (function PackagedTestHelper() {
}
function mozAppsError() {
ok(false, "mozApps error: " + self.error.name);
ok(false, "mozApps error: " + this.error.name);
finish();
}

View File

@ -30,20 +30,22 @@ var miniManifestURL;
SimpleTest.waitForExplicitFinish();
function checkForUpdate(aExpected, aOnSuccess, aOnApplied, aLaunchDownload, aOnError) {
function checkForUpdate(aExpected, aOnSuccess, aOnApplied, aOnDownloadError,
aLaunchDownload, aOnRequestError) {
var lApp = PackagedTestHelper.gApp;
lApp.ondownloadsuccess = aOnSuccess || null;
lApp.ondownloadapplied = aOnApplied || null;
lApp.ondownloaderror = aOnDownloadError || null;
var request = lApp.checkForUpdate();
request.onerror = aOnError ? aOnError.bind(undefined, request) :
PackagedTestHelper.mozAppsError;
request.onerror = aOnRequestError ? aOnRequestError.bind(undefined, request) :
PackagedTestHelper.mozAppsError;
request.onsuccess = function(event) {
var expectingDownload = aExpected ? "": "not ";
ok(lApp.downloadAvailable === aExpected,
"Download should " + expectingDownload + "be available");
if (aLaunchDownload && lApp.downloadAvailable) {
if (aLaunchDownload) {
lApp.download();
} else {
PackagedTestHelper.next();
@ -84,7 +86,7 @@ function updateApp(aExpectedReady, aPreviousVersion, aNextVersion) {
navigator.mozApps.mgmt.applyDownload(lApp);
});
checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler,
checkForUpdate(true, ondownloadsuccesshandler, ondownloadappliedhandler, null,
true);
}
@ -126,6 +128,21 @@ var steps = [
ok(true, "Application installed");
};
},
function() {
ok(true, "== TEST == Check for Update and try to download it without update available");
function onerror() {
is(PackagedTestHelper.gApp.downloadError.name, "NO_DOWNLOAD_AVAILABLE", "Download not available");
ok(!PackagedTestHelper.gApp.readyToApplyDownload, "Not ready to apply download");
PackagedTestHelper.next();
}
function onsuccess() {
ok(false, "ondownloadsuccess fired");
PackagedTestHelper.next();
}
checkForUpdate(false, onsuccess, null, onerror, true);
},
function() {
PackagedTestHelper.setAppVersion(3, PackagedTestHelper.next);
},
@ -161,7 +178,7 @@ var steps = [
"MANIFEST_MISMATCH", 2, false, true,
"arandomname",
function () {
checkForUpdate(false, null, null, false,
checkForUpdate(false, null, null, null, false,
function (request) {
if (request.error.name === "PENDING_APP_NOT_UPDATABLE") {
ok(true, "Got expected PENDING_APP_NOT_UPDATEABLE");

View File

@ -0,0 +1,6 @@
component {397a7fdf-2254-47be-b74e-76625a1a66d5} MozKeyboard.js
contract @mozilla.org/b2g-keyboard;1 {397a7fdf-2254-47be-b74e-76625a1a66d5}
category JavaScript-navigator-property mozKeyboard @mozilla.org/b2g-keyboard;1
component {4607330d-e7d2-40a4-9eb8-43967eae0142} MozKeyboard.js
contract @mozilla.org/b2g-inputmethod;1 {4607330d-e7d2-40a4-9eb8-43967eae0142}

View File

@ -331,7 +331,7 @@ MozInputMethod.prototype = {
if (perm != Ci.nsIPermissionManager.ALLOW_ACTION) {
dump("No permission to use the keyboard API for " +
principal.origin + "\n");
return null;
return;
}
}

26
dom/inputmethod/moz.build Normal file
View File

@ -0,0 +1,26 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIB2GKeyboard.idl',
]
XPIDL_MODULE = 'dom_inputmethod'
MODULE = 'dom'
EXTRA_COMPONENTS += [
'InputMethod.manifest',
'MozKeyboard.js',
]
EXTRA_JS_MODULES += [
'Keyboard.jsm',
]
FAIL_ON_WARNINGS = True
LIBXUL_LIBRARY = True

View File

@ -0,0 +1,71 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
[scriptable, uuid(40ad96b2-9efa-41fb-84c7-fbcec9b153f0)]
interface nsIB2GKeyboard : nsISupports
{
void sendKey(in long keyCode, in long charCode);
// Select the <select> option specified by index.
// If this method is called on a <select> that support multiple
// selection, then the option specified by index will be added to
// the selection.
// If this method is called for a select that does not support multiple
// selection the previous element will be unselected.
void setSelectedOption(in jsval index);
// Select the <select> options specified by indexes. All other options
// will be deselected.
// If this method is called for a <select> that does not support multiple
// selection, then the last index specified in indexes will be selected.
void setSelectedOptions(in jsval indexes);
// Set the value on the currently focused element. This has to be used
// for special situations where the value had to be chosen amongst a
// list (type=month) or a widget (type=date, time, etc.).
// If the value passed in parameter isn't valid (in the term of HTML5
// Forms Validation), the value will simply be ignored by the element.
void setValue(in jsval value);
void removeFocus();
attribute nsIDOMEventListener onfocuschange;
// Fires when user moves the cursor, changes the selection, or alters the
// composing text length
attribute nsIDOMEventListener onselectionchange;
// The start position of the selection.
readonly attribute long selectionStart;
// The stop position of the selection.
readonly attribute long selectionEnd;
/*
* Set the selection range of the the editable text.
*
* @param start The beginning of the selected text.
* @param end The end of the selected text.
*
* Note that the start position should be less or equal to the end position.
* To move the cursor, set the start and end position to the same value.
*/
void setSelectionRange(in long start, in long end);
/*
* Replace text around the beginning of the current selection range of the
* editable text.
*
* @param text The string to be replaced with.
* @param beforeLength The number of characters to be deleted before the
* beginning of the current selection range. Defaults to 0.
* @param afterLength The number of characters to be deleted after the
* beginning of the current selection range. Defaults to 0.
*/
void replaceSurroundingText(in DOMString text,
[optional] in long beforeLength,
[optional] in long afterLength);
};

View File

@ -74,6 +74,7 @@ PARALLEL_DIRS += [
'promise',
'wappush',
'telephony',
'inputmethod',
'webidl',
]

View File

@ -78,6 +78,14 @@ PaymentContentHelper.prototype = {
// nsIDOMGlobalPropertyInitializer
init: function(aWindow) {
try {
if (!Services.prefs.getBoolPref("dom.mozPay.enabled")) {
return null;
}
} catch (e) {
return null;
}
this._window = aWindow;
this.initDOMRequestHelper(aWindow, PAYMENT_IPC_MSG_NAMES);

View File

@ -5,7 +5,8 @@
*/
[JSImplementation="@mozilla.org/b2g-inputmethod;1",
NavigatorProperty="mozInputMethod"]
NavigatorProperty="mozInputMethod",
Pref="dom.mozInputMethod.enabled"]
interface MozInputMethod : EventTarget {
// Input Method Manager contain a few global methods expose to apps
readonly attribute MozInputMethodManager mgmt;
@ -31,7 +32,8 @@ interface MozInputMethod : EventTarget {
// Manages the list of IMEs, enables/disables IME and switches to an
// IME.
[JSImplementation="@mozilla.org/b2g-imm;1"]
[JSImplementation="@mozilla.org/b2g-imm;1",
Pref="dom.mozInputMethod.enabled"]
interface MozInputMethodManager {
// Ask the OS to show a list of available IMEs for users to switch from.
// OS should ignore this request if the app is currently not the active one.
@ -59,7 +61,8 @@ interface MozInputMethodManager {
// It also hosts the methods available to the keyboard app to mutate the input field represented.
// An "input context" gets void when the app is no longer allowed to interact with the text field,
// e.g., the text field does no longer exist, the app is being switched to background, and etc.
[JSImplementation="@mozilla.org/b2g-inputcontext;1"]
[JSImplementation="@mozilla.org/b2g-inputcontext;1",
Pref="dom.mozInputMethod.enabled"]
interface MozInputContext: EventTarget {
// The tag name of input field, which is enum of "input", "textarea", or "contenteditable"
readonly attribute DOMString? type;
@ -86,6 +89,10 @@ interface MozInputContext: EventTarget {
readonly attribute long selectionStart;
readonly attribute long selectionEnd;
// The start and stop position of the selection.
readonly attribute DOMString? textBeforeCursor;
readonly attribute DOMString? textAfterCursor;
/*
* Set the selection range of the the editable text.
* Note: This method cannot be used to move the cursor during composition. Calling this

View File

@ -186,6 +186,7 @@ WEBIDL_FILES = [
'IDBVersionChangeEvent.webidl',
'ImageData.webidl',
'ImageDocument.webidl',
'InputMethod.webidl',
'InspectorUtils.webidl',
'InterAppConnection.webidl',
'InterAppConnectionRequest.webidl',
@ -531,11 +532,6 @@ if CONFIG['ENABLE_TESTS']:
'TestJSImplGen.webidl',
]
if CONFIG['MOZ_B2G']:
WEBIDL_FILES += [
'InputMethod.webidl',
]
GENERATED_EVENTS_WEBIDL_FILES = [
'BlobEvent.webidl',
'DeviceLightEvent.webidl',

View File

@ -729,8 +729,8 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
// the page is zoomed in, the frame's size might be larger than the widget
// bounds, but we don't want the composition bounds to be.
bool useWidgetBounds = false;
bool isRootContentDocRootScrollFrame = aForFrame->GetParent() == nullptr
&& presContext->IsRootContentDocument();
bool isRootContentDocRootScrollFrame = presContext->IsRootContentDocument()
&& aScrollFrame == presShell->GetRootScrollFrame();
if (isRootContentDocRootScrollFrame) {
if (nsIWidget* widget = aForFrame->GetNearestWidget()) {
nsIntRect bounds;

View File

@ -750,6 +750,9 @@ pref("browser.contentHandlers.types.3.title", "chrome://browser/locale/region.pr
pref("browser.contentHandlers.types.3.uri", "chrome://browser/locale/region.properties");
pref("browser.contentHandlers.types.3.type", "application/vnd.mozilla.maybe.feed");
// WebPayment
pref("dom.mozPay.enabled", true);
#ifndef RELEASE_BUILD
pref("dom.payment.provider.0.name", "Firefox Marketplace");
pref("dom.payment.provider.0.description", "marketplace.firefox.com");

View File

@ -1444,7 +1444,8 @@ abstract public class BrowserApp extends GeckoApp
final String keywordUrl = BrowserDB.getUrlForKeyword(getContentResolver(), keyword);
// If there isn't a bookmark keyword, just load the URL.
// If there isn't a bookmark keyword, load the url. This may result in a query
// using the default search engine.
if (TextUtils.isEmpty(keywordUrl)) {
Tabs.getInstance().loadUrl(url, Tabs.LOADURL_USER_ENTERED);
return;

View File

@ -194,6 +194,7 @@ FENNEC_JAVA_FILES = \
gfx/LayerView.java \
gfx/NativePanZoomController.java \
gfx/NinePatchTileLayer.java \
gfx/Overscroll.java \
gfx/PanningPerfAPI.java \
gfx/PanZoomController.java \
gfx/PanZoomTarget.java \

View File

@ -154,6 +154,10 @@ abstract class Axis {
mRecentVelocities = new float[FLING_VELOCITY_POINTS];
}
// Implementors can override these to show effects when the axis overscrolls
protected void overscrollFling(float velocity) { }
protected void overscrollPan(float displacement) { }
public void setOverScrollMode(int overscrollMode) {
mOverscrollMode = overscrollMode;
}
@ -379,12 +383,22 @@ abstract class Axis {
// getOverscroll which doesn't take into account any new displacment being applied.
// If we using a subscroller, we don't want to alter the scrolling being done
if (getOverScrollMode() == View.OVER_SCROLL_NEVER && !mSubscroller.scrolling()) {
float originalDisplacement = mDisplacement;
if (mDisplacement + getOrigin() < getPageStart() - getMarginStart()) {
mDisplacement = getPageStart() - getMarginStart() - getOrigin();
stopFling();
} else if (mDisplacement + getViewportEnd() > getPageEnd() + getMarginEnd()) {
mDisplacement = getPageEnd() - getMarginEnd() - getViewportEnd();
stopFling();
}
// Return the amount of overscroll so that the overscroll controller can draw it for us
if (originalDisplacement != mDisplacement) {
if (mFlingState == FlingStates.FLINGING) {
overscrollFling(mVelocity / MS_PER_FRAME * 1000);
stopFling();
} else if (mFlingState == FlingStates.PANNING) {
overscrollPan(originalDisplacement - mDisplacement);
}
}
}
}

View File

@ -132,6 +132,10 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
mContentDocumentIsDisplayed = true;
}
public void setOverscrollHandler(final Overscroll listener) {
mPanZoomController.setOverscrollHandler(listener);
}
/** Attaches to root layer so that Gecko appears. */
public void notifyGeckoReady() {
mGeckoIsReady = true;

View File

@ -129,6 +129,9 @@ class JavaPanZoomController
/* Used to change the scrollY direction */
private boolean mNegateWheelScrollY;
// Handler to be notified when overscroll occurs
private Overscroll mOverscroll;
public JavaPanZoomController(PanZoomTarget target, View view, EventDispatcher eventDispatcher) {
mTarget = target;
mSubscroller = new SubdocumentScrollHelper(eventDispatcher);
@ -1113,6 +1116,18 @@ class JavaPanZoomController
RectF maxMargins = mTarget.getMaxMargins();
return (metrics.marginLeft < maxMargins.left || metrics.marginRight < maxMargins.right);
}
@Override
protected void overscrollFling(final float velocity) {
if (mOverscroll != null) {
mOverscroll.setVelocity(velocity, Overscroll.Axis.X);
}
}
@Override
protected void overscrollPan(final float distance) {
if (mOverscroll != null) {
mOverscroll.setDistance(distance, Overscroll.Axis.X);
}
}
}
private class AxisY extends Axis {
@ -1135,6 +1150,18 @@ class JavaPanZoomController
RectF maxMargins = mTarget.getMaxMargins();
return (metrics.marginTop < maxMargins.top || metrics.marginBottom < maxMargins.bottom);
}
@Override
protected void overscrollFling(final float velocity) {
if (mOverscroll != null) {
mOverscroll.setVelocity(velocity, Overscroll.Axis.Y);
}
}
@Override
protected void overscrollPan(final float distance) {
if (mOverscroll != null) {
mOverscroll.setDistance(distance, Overscroll.Axis.Y);
}
}
}
/*
@ -1434,4 +1461,9 @@ class JavaPanZoomController
public void updateScrollOffset(float cssX, float cssY) {
// Nothing to update, this class doesn't store the scroll offset locally.
}
@Override
public void setOverscrollHandler(final Overscroll handler) {
mOverscroll = handler;
}
}

View File

@ -18,6 +18,7 @@ import org.mozilla.gecko.util.EventDispatcher;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PointF;
@ -67,6 +68,7 @@ public class LayerView extends FrameLayout {
/* This should only be modified on the Java UI thread. */
private final ArrayList<TouchEventInterceptor> mTouchInterceptors;
private final Overscroll mOverscroll;
/* Flags used to determine when to show the painted surface. */
public static final int PAINT_START = 0;
@ -104,10 +106,13 @@ public class LayerView extends FrameLayout {
mBackgroundColor = Color.WHITE;
mTouchInterceptors = new ArrayList<TouchEventInterceptor>();
mOverscroll = new Overscroll(this);
}
public void initializeView(EventDispatcher eventDispatcher) {
mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher);
mLayerClient.setOverscrollHandler(mOverscroll);
mPanZoomController = mLayerClient.getPanZoomController();
mMarginsAnimator = mLayerClient.getLayerMarginsAnimator();
@ -218,6 +223,16 @@ public class LayerView extends FrameLayout {
return result;
}
@Override
public void dispatchDraw(final Canvas canvas) {
super.dispatchDraw(canvas);
// We must have a layer client to get valid viewport metrics
if (mLayerClient != null) {
mOverscroll.draw(canvas, getViewportMetrics());
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
@ -487,6 +502,8 @@ public class LayerView extends FrameLayout {
if (mListener != null) {
mListener.sizeChanged(width, height);
}
mOverscroll.setSize(width, height);
}
private void surfaceChanged(int width, int height) {
@ -495,6 +512,8 @@ public class LayerView extends FrameLayout {
if (mListener != null) {
mListener.surfaceChanged(width, height);
}
mOverscroll.setSize(width, height);
}
private void onDestroyed() {

View File

@ -104,4 +104,7 @@ class NativePanZoomController implements PanZoomController, GeckoEventListener {
}
public native void updateScrollOffset(float cssX, float cssY);
public void setOverscrollHandler(final Overscroll listener) {
}
}

View File

@ -0,0 +1,127 @@
/* -*- 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.gfx;
import android.content.Context;
import android.graphics.Canvas;
import android.support.v4.widget.EdgeEffectCompat;
import android.support.v4.view.ViewCompat;
import android.view.View;
public class Overscroll {
// Used to index particular edges in the edges array
private static final int TOP = 0;
private static final int BOTTOM = 1;
private static final int LEFT = 2;
private static final int RIGHT = 3;
// All four edges of the screen
private final EdgeEffectCompat[] mEdges = new EdgeEffectCompat[4];
// The view we're showing this overscroll on.
private final View mView;
// The axis to show overscroll on.
public enum Axis {
X,
Y,
};
public Overscroll(final View v) {
mView = v;
Context context = v.getContext();
for (int i = 0; i < 4; i++) {
mEdges[i] = new EdgeEffectCompat(context);
}
}
public void setSize(final int width, final int height) {
mEdges[LEFT].setSize(height, width);
mEdges[RIGHT].setSize(height, width);
mEdges[TOP].setSize(width, height);
mEdges[BOTTOM].setSize(width, height);
}
private EdgeEffectCompat getEdgeForAxisAndSide(final Axis axis, final float side) {
if (axis == Axis.Y) {
if (side < 0) {
return mEdges[TOP];
} else {
return mEdges[BOTTOM];
}
} else {
if (side < 0) {
return mEdges[LEFT];
} else {
return mEdges[RIGHT];
}
}
}
public void setVelocity(final float velocity, final Axis axis) {
final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, velocity);
// If we're showing overscroll already, start fading it out.
if (!edge.isFinished()) {
edge.onRelease();
} else {
// Otherwise, show an absorb effect
edge.onAbsorb((int)velocity);
}
ViewCompat.postInvalidateOnAnimation(mView);
}
public void setDistance(final float distance, final Axis axis) {
// The first overscroll event often has zero distance. Throw it out
if (distance == 0.0f) {
return;
}
final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, (int)distance);
edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight()));
ViewCompat.postInvalidateOnAnimation(mView);
}
public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
if (metrics == null) {
return;
}
// If we're pulling an edge, or fading it out, draw!
boolean invalidate = false;
if (!mEdges[TOP].isFinished()) {
invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0);
}
if (!mEdges[BOTTOM].isFinished()) {
invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180);
}
if (!mEdges[LEFT].isFinished()) {
invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270);
}
if (!mEdges[RIGHT].isFinished()) {
invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90);
}
// If the edge effect is animating off screen, invalidate.
if (invalidate) {
ViewCompat.postInvalidateOnAnimation(mView);
}
}
public boolean draw(final EdgeEffectCompat edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) {
final int state = canvas.save();
canvas.translate(translateX, translateY);
canvas.rotate(rotation);
boolean invalidate = edge.draw(canvas);
canvas.restoreToCount(state);
return invalidate;
}
}

View File

@ -42,4 +42,6 @@ public interface PanZoomController {
public int getOverScrollMode();
public void updateScrollOffset(float cssX, float cssY);
public void setOverscrollHandler(final Overscroll controller);
}

View File

@ -65,9 +65,10 @@ public class BrowserHealthRecorder implements GeckoEventListener {
private static final String EVENT_ADDONS_CHANGE = "Addons:Change";
private static final String EVENT_ADDONS_UNINSTALLING = "Addons:Uninstalling";
private static final String EVENT_PREF_CHANGE = "Pref:Change";
// This is raised from Gecko. It avoids browser.js having to know about the
// location that invoked it (the URL bar).
// This is raised from Gecko and signifies a search via the URL bar (not a bookmarks keyword
// search). Using this event (rather than passing the invocation location as an arg) avoids
// browser.js having to know about the invocation location.
public static final String EVENT_KEYWORD_SEARCH = "Search:Keyword";
// This is raised from Java. We include the location in the message.
@ -681,6 +682,9 @@ public class BrowserHealthRecorder implements GeckoEventListener {
// Searches.
if (EVENT_KEYWORD_SEARCH.equals(event)) {
// A search via the URL bar. Since we eliminate all other search possibilities
// (e.g. bookmarks keyword, search suggestion) when we initially process the
// search URL, this is considered a default search.
recordSearch(message.getString("identifier"), "bartext");
return;
}

View File

@ -3,17 +3,16 @@
- 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:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<org.mozilla.gecko.home.HomeListView
android:id="@+id/list"
style="@style/Widget.TopSitesListView"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
android:layout_height="fill_parent"/>
<org.mozilla.gecko.home.HomeBanner android:id="@+id/home_banner"
style="@style/Widget.HomeBanner"
@ -26,4 +25,4 @@
android:clickable="true"
android:focusable="true"/>
</LinearLayout>
</FrameLayout>

View File

@ -39,7 +39,7 @@
[testDistribution]
[testFindInPage]
[testInputUrlBar]
# [testAddSearchEngine] # disabled on fig - bug 880060
[testAddSearchEngine]
[testImportFromAndroid]
[testMasterPassword]
[testDeviceSearchEngine]

View File

@ -4,7 +4,7 @@
</header>
<body>
<form method="get" action="http://www.google.com/search">
<input type="text" name="q" size="50" maxlength="255" value="" />
<input type="text" name="q" style="width:300px; height:500px;" maxlength="255" value="" />
<input type="submit" value="Google Search" />
</form>
</body>

View File

@ -5,7 +5,6 @@ import @ANDROID_PACKAGE_NAME@.*;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.util.Log;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
@ -13,29 +12,36 @@ import org.json.JSONObject;
/**
* Test adding a search engine from an input field context menu.
* 1. Get the number of existing search engines (As shown in the AwesomeScreen).
* 1. Get the number of existing search engines from the SearchEngine:Data event and as displayed in about:home.
* 2. Load a page with a text field, open the context menu and add a search engine from the page.
* 3. Get the number of search engines after adding the new one and verify it has increased by 1.
*/
public class testAddSearchEngine extends PixelTest {
public class testAddSearchEngine extends AboutHomeTest {
private final int MAX_WAIT_TEST_MS = 5000;
private final String SEARCH_TEXT = "Firefox for Android";
private final String ADD_SEARCHENGINE_OPTION_TEXT = "Add Search Engine";
@Override
protected int getTestType() {
return TEST_MOCHITEST;
}
public void testAddSearchEngine() {
String blankPageURL = getAbsoluteUrl("/robocop/robocop_blank_01.html");
String searchEngineURL = getAbsoluteUrl("/robocop/robocop_search.html");
String blankPageURL = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
String searchEngineURL = getAbsoluteUrl(StringHelper.ROBOCOP_SEARCH_URL);
blockForGeckoReady();
int height = mDriver.getGeckoTop() + 150;
int width = mDriver.getGeckoLeft() + 150;
inputAndLoadUrl(blankPageURL);
waitForText("Browser Blank Page 01");
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
// Get the searchengine data by clicking the awesomebar - this causes Gecko to send Java the list
// of search engines.
Actions.EventExpecter searchEngineDataEventExpector = mActions.expectGeckoEvent("SearchEngines:Data");
focusUrlBar();
mActions.sendKeys(SEARCH_TEXT);
String eventData = searchEngineDataEventExpector.blockForEventData();
searchEngineDataEventExpector.unregisterListener();
@ -51,38 +57,36 @@ public class testAddSearchEngine extends PixelTest {
mAsserter.dumpLog("Search Engines list = " + searchEngines.toString());
// Verify that the number of displayed search engines is the same as the one received through the SearchEngines:Data event.
verifyDisplayedSearchEnginesCount("Browser Blank Page 01", initialNumSearchEngines);
verifyDisplayedSearchEnginesCount(initialNumSearchEngines);
// Load the page for the search engine to add.
inputAndLoadUrl(searchEngineURL);
waitForText("Robocop Search Engine");
waitForText(StringHelper.ROBOCOP_SEARCH_TITLE);
verifyPageTitle(StringHelper.ROBOCOP_SEARCH_TITLE);
// Used to long-tap on the search input box for the search engine to add.
int height = mDriver.getGeckoTop() + 10;
int width = mDriver.getGeckoLeft() + 20;
getInstrumentation().waitForIdleSync();
mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
mSolo.clickLongOnScreen(width,height);
if (!waitForText("Add Search Engine")) {
// TODO: clickLongOnScreen does not always work - known Robotium issue - . Clicking a second time seems to work.
mAsserter.dumpLog("Something went wrong and the context menu was not opened. Trying again");
mSolo.clickLongOnScreen(width,height);
}
mAsserter.ok(waitForText("Add Search Engine"), "Waiting for the context menu to be opened", "The context menu was opened");
mAsserter.ok(waitForText(ADD_SEARCHENGINE_OPTION_TEXT), "Waiting for the context menu to be opened", "The context menu was opened");
// Add the search engine
mSolo.clickOnText("Add Search Engine");
mSolo.clickOnText(ADD_SEARCHENGINE_OPTION_TEXT);
waitForText("Cancel");
clickOnButton("OK");
mAsserter.ok(!mSolo.searchText("Add Search Engine"), "Adding the Search Engine", "The add Search Engine pop-up has been closed");
mAsserter.ok(!mSolo.searchText(ADD_SEARCHENGINE_OPTION_TEXT), "Adding the Search Engine", "The add Search Engine pop-up has been closed");
waitForText(StringHelper.ROBOCOP_SEARCH_TITLE); // Make sure the pop-up is closed and we are back at the searchengine page
// Load Robocop Blank 1 again to give the time for the searchengine to be added
// TODO: This is a potential source of intermittent oranges - it's a race condition!
loadAndPaint(blankPageURL);
waitForText("Browser Blank Page 01");
loadUrl(blankPageURL);
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
// Load search engines again and check that the quantity of engines has increased by 1.
searchEngineDataEventExpector = mActions.expectGeckoEvent("SearchEngines:Data");
focusUrlBar();
mActions.sendKeys(SEARCH_TEXT);
eventData = searchEngineDataEventExpector.blockForEventData();
try {
@ -97,7 +101,7 @@ public class testAddSearchEngine extends PixelTest {
mAsserter.is(searchEngines.size(), initialNumSearchEngines + 1, "Checking the number of Search Engines has increased");
// Verify that the number of displayed searchengines is the same as the one received through the SearchEngines:Data event.
verifyDisplayedSearchEnginesCount("Browser Blank Page 01", initialNumSearchEngines + 1);
verifyDisplayedSearchEnginesCount(initialNumSearchEngines + 1);
searchEngineDataEventExpector.unregisterListener();
}
@ -123,51 +127,21 @@ public class testAddSearchEngine extends PixelTest {
/**
* Method to verify that the displayed number of search engines matches the expected number.
* Uses a BooleanTest which counts how many SearchEngineRow instances are being displayed
* in the Awesomescreen.
* @param waitText Text from the loaded page to expect. Used to detect when the Awesomescreen
* close animation has completed.
* @param expectedCountParam The expected number of search engines.
* @param expectedCount The expected number of search engines.
*/
public void verifyDisplayedSearchEnginesCount(String waitText, int expectedCountParam) {
final int expectedCount = expectedCountParam;
mActions.sendKeys("Firefox for Android");
public void verifyDisplayedSearchEnginesCount(final int expectedCount) {
mSolo.clearEditText(0);
mActions.sendKeys(SEARCH_TEXT);
boolean correctNumSearchEnginesDisplayed = waitForTest(new BooleanTest() {
@Override
public boolean test() {
ArrayList<ListView> views;
int searchEngineCount = 0;
views = mSolo.getCurrentViews(ListView.class);
for (ListView view : views) {
ListAdapter adapter = view.getAdapter();
if (adapter != null) {
// Only count SearchEngineRow views - other views are not relavent to this test.
try {
ClassLoader classLoader = getActivity().getClassLoader();
Class searchEngineRow = classLoader.loadClass("org.mozilla.gecko.SearchEngineRow");
for (int i = 0; i < adapter.getCount(); i++ ) {
View item = view.getChildAt(i);
if (searchEngineRow.isInstance(item)) {
searchEngineCount++;
}
}
} catch (Exception e) {
mAsserter.dumpLog("Exception in verifyDisplayedSearchEnginesCount", e);
}
}
}
if (searchEngineCount == expectedCount) {
return true;
} else {
mAsserter.dumpLog("The wrong number of search engines was found. Found " + searchEngineCount + " search engines");
return false;
}
return (findListViewWithTag("browser_search").getAdapter().getCount() == expectedCount);
}
}, MAX_WAIT_TEST_MS);
// Close the Awesomescreen
// Exit about:home
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
waitForText(waitText);
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
mAsserter.ok(correctNumSearchEnginesDisplayed, expectedCount + " Search Engines should be displayed" , "The correct number of Search Engines has been displayed");
}
}

View File

@ -62,17 +62,9 @@ public class FaviconView extends ImageView {
* in this view with the coloured background.
*/
private void formatImage() {
// If we're called before bitmap is set, just show the default.
if (mIconBitmap == null) {
setImageResource(R.drawable.favicon);
hideBackground();
return;
}
// If we're called before size set, abort.
if (mActualWidth == 0 || mActualHeight == 0) {
hideBackground();
setImageResource(R.drawable.favicon);
// If we're called before bitmap is set, or before size is set, show blank.
if (mIconBitmap == null || mActualWidth == 0 || mActualHeight == 0) {
clearImage();
return;
}
@ -142,6 +134,11 @@ public class FaviconView extends ImageView {
* (Favicons class), so as to exploit caching.
*/
private void updateImageInternal(Bitmap bitmap, String key, boolean allowScaling) {
if (bitmap == null) {
showDefaultFavicon();
return;
}
// Reassigning the same bitmap? Don't bother.
if (mUnscaledBitmap == bitmap) {
return;
@ -155,12 +152,21 @@ public class FaviconView extends ImageView {
formatImage();
}
private void showDefaultFavicon() {
setImageResource(R.drawable.favicon);
hideBackground();
}
/**
* Clear image and background shown by this view.
*/
public void clearImage() {
setImageResource(0);
hideBackground();
mUnscaledBitmap = null;
mIconBitmap = null;
mIconKey = null;
mScalingExpected = false;
}
/**

View File

@ -1402,7 +1402,9 @@ var BrowserApp = {
break;
case "keyword-search":
// This assumes that the user can only perform a keyword search on the selected tab.
// This event refers to a search via the URL bar, not a bookmarks
// keyword search. Note that this code assumes that the user can only
// perform a keyword search on the selected tab.
this.selectedTab.userSearch = aData;
let engine = aSubject.QueryInterface(Ci.nsISearchEngine);

View File

@ -13,6 +13,11 @@ const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
// -----------------------------------------------------------------------
// HelperApp Launcher Dialog
// -----------------------------------------------------------------------
@ -30,15 +35,16 @@ HelperAppLauncherDialog.prototype = {
},
promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
// Retrieve the user's default download directory
let dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
let defaultFolder = dnldMgr.userDownloadsDirectory;
return Task.spawn(function() {
// Retrieve the user's default download directory
let defaultFolder = yield Downloads.getPreferredDownloadsDirectory();
try {
file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
} catch (e) { }
try {
file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
} catch (e) { }
return file;
throw new Task.Result(file);
}.bind(this));
},
validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) {

View File

@ -182,7 +182,7 @@ NSSDialogs.prototype = {
this.getString("nssdialogs.cancel.label")
])
.addLabel({ id: "requestedDetails", label: serverRequestedDetails } )
.addMenuList({
.addMenulist({
id: "nicknames",
label: this.getString("clientAuthAsk.message2"),
values: certNickList, selected: selectedIndex

View File

@ -4424,6 +4424,9 @@ pref("dom.forms.inputmode", false);
pref("dom.forms.inputmode", true);
#endif
// InputMethods for soft keyboards in B2G
pref("dom.mozInputMethod.enabled", false);
// Telephony API
pref("dom.telephony.enabled", false);

View File

@ -1,6 +1,7 @@
"use strict";
const isB2G = ("@mozilla.org/b2g-keyboard;1" in Components.classes);
const isB2G = ("@mozilla.org/b2g-process-global;1" in Cc);
do_get_profile(); // must be called before getting nsIX509CertDB
const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);

View File

@ -704,7 +704,9 @@ Connection::databaseElementExists(enum DatabaseElementType aElementType,
{
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
nsAutoCString query("SELECT name FROM sqlite_master WHERE type = '");
nsCString query("SELECT name FROM (SELECT * FROM sqlite_master UNION ALL "
"SELECT * FROM sqlite_temp_master) "
"WHERE type = '");
switch (aElementType) {
case INDEX:
query.Append("index");

View File

@ -108,6 +108,19 @@ add_task(function test_indexExists_not_created()
do_check_false(msc.indexExists("foo"));
});
add_task(function test_temp_tableExists_and_indexExists()
{
var msc = getOpenedDatabase();
msc.executeSimpleSQL("CREATE TEMP TABLE test_temp(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
do_check_true(msc.tableExists("test_temp"));
msc.executeSimpleSQL("CREATE INDEX test_temp_ind ON test_temp (name)");
do_check_true(msc.indexExists("test_temp_ind"));
msc.executeSimpleSQL("DROP INDEX test_temp_ind");
msc.executeSimpleSQL("DROP TABLE test_temp");
});
add_task(function test_createTable_not_created()
{
var msc = getOpenedDatabase();

View File

@ -464,15 +464,26 @@ class MochiRemote(Mochitest):
return 1
return 0
def printScreenshot(self):
try:
image = self._dm.pullFile("/mnt/sdcard/Robotium-Screenshots/robocop-screenshot.jpg")
encoded = base64.b64encode(image)
log.info("SCREENSHOT: data:image/jpg;base64,%s", encoded)
except:
# If the test passes, no screenshot will be generated and
# pullFile will fail -- continue silently.
pass
def printScreenshots(self, screenShotDir):
# TODO: This can be re-written after completion of bug 749421
if not self._dm.dirExists(screenShotDir):
log.info("SCREENSHOT: No ScreenShots directory available: " + screenShotDir)
return
printed = 0
for name in self._dm.listFiles(screenShotDir):
fullName = screenShotDir + "/" + name
log.info("SCREENSHOT: FOUND: [%s]", fullName)
try:
image = self._dm.pullFile(fullName)
encoded = base64.b64encode(image)
log.info("SCREENSHOT: data:image/jpg;base64,%s", encoded)
printed += 1
except:
log.info("SCREENSHOT: Could not be parsed")
pass
log.info("SCREENSHOT: TOTAL PRINTED: [%s]", printed)
def printDeviceInfo(self, printLogcat=False):
try:
@ -654,7 +665,8 @@ def main():
if (options.dm_trans == "sut"):
dm._runCmds([{"cmd": " ".join(cmd)}])
try:
dm.removeDir("/mnt/sdcard/Robotium-Screenshots")
screenShotDir = "/mnt/sdcard/Robotium-Screenshots"
dm.removeDir(screenShotDir)
dm.recordLogcat()
result = mochitest.runTests(options)
if result != 0:
@ -662,7 +674,7 @@ def main():
log_result = mochitest.addLogData()
if result != 0 or log_result != 0:
mochitest.printDeviceInfo(printLogcat=True)
mochitest.printScreenshot()
mochitest.printScreenshots(screenShotDir)
# Ensure earlier failures aren't overwritten by success on this run
if retVal is None or retVal == 0:
retVal = result

View File

@ -715,8 +715,15 @@ var StyleRuleActor = protocol.ActorClass({
// Use a fresh element for each call to this function to prevent side effects
// that pop up based on property values that were already set on the element.
let tempElement = Services.appShell.hiddenDOMWindow.
document.createElement("div");
let document;
if (this.rawNode) {
document = this.rawNode.ownerDocument;
} else {
document = this.rawRule.parentStyleSheet.ownerNode.ownerDocument;
}
let tempElement = document.createElement("div");
for (let mod of modifications) {
if (mod.type === "set") {

View File

@ -600,9 +600,7 @@ OpenedConnection.prototype = Object.freeze({
},
/**
* Whether a table exists in the database.
*
* IMPROVEMENT: Look for temporary tables.
* Whether a table exists in the database (both persistent and temporary tables).
*
* @param name
* (string) Name of the table.
@ -611,7 +609,9 @@ OpenedConnection.prototype = Object.freeze({
*/
tableExists: function (name) {
return this.execute(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
"SELECT name FROM (SELECT * FROM sqlite_master UNION ALL " +
"SELECT * FROM sqlite_temp_master) " +
"WHERE type = 'table' AND name=?",
[name])
.then(function onResult(rows) {
return Promise.resolve(rows.length > 0);
@ -620,9 +620,7 @@ OpenedConnection.prototype = Object.freeze({
},
/**
* Whether a named index exists.
*
* IMPROVEMENT: Look for indexes in temporary tables.
* Whether a named index exists (both persistent and temporary tables).
*
* @param name
* (string) Name of the index.
@ -631,7 +629,9 @@ OpenedConnection.prototype = Object.freeze({
*/
indexExists: function (name) {
return this.execute(
"SELECT name FROM sqlite_master WHERE type='index' AND name=?",
"SELECT name FROM (SELECT * FROM sqlite_master UNION ALL " +
"SELECT * FROM sqlite_temp_master) " +
"WHERE type = 'index' AND name=?",
[name])
.then(function onResult(rows) {
return Promise.resolve(rows.length > 0);

View File

@ -60,6 +60,22 @@ function getDummyDatabase(name, extraOptions={}) {
throw new Task.Result(c);
}
function getDummyTempDatabase(name, extraOptions={}) {
const TABLES = {
dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT",
files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
};
let c = yield getConnection(name, extraOptions);
c._initialStatementCount = 0;
for (let [k, v] in Iterator(TABLES)) {
yield c.execute("CREATE TEMP TABLE " + k + "(" + v + ")");
c._initialStatementCount++;
}
throw new Task.Result(c);
}
function run_test() {
Cu.import("resource://testing-common/services-common/logging.js");
@ -203,6 +219,27 @@ add_task(function test_index_exists() {
yield c.close();
});
add_task(function test_temp_table_exists() {
let c = yield getDummyTempDatabase("temp_table_exists");
do_check_false(yield c.tableExists("temp_does_not_exist"));
do_check_true(yield c.tableExists("dirs"));
do_check_true(yield c.tableExists("files"));
yield c.close();
});
add_task(function test_temp_index_exists() {
let c = yield getDummyTempDatabase("temp_index_exists");
do_check_false(yield c.indexExists("temp_does_not_exist"));
yield c.execute("CREATE INDEX my_index ON dirs (path)");
do_check_true(yield c.indexExists("my_index"));
yield c.close();
});
add_task(function test_close_cached() {
let c = yield getDummyDatabase("close_cached");

View File

@ -26,6 +26,7 @@ const PREF_EM_CERT_CHECKATTRIBUTES = "extensions.hotfix.cert.checkAttributes"
const PREF_EM_HOTFIX_CERTS = "extensions.hotfix.certs.";
const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
const PREF_SELECTED_LOCALE = "general.useragent.locale";
const UNKNOWN_XPCOM_ABI = "unknownABI";
const UPDATE_REQUEST_VERSION = 2;
const CATEGORY_UPDATE_PARAMS = "extension-update-params";

140
webapprt/PaymentUIGlue.js Normal file
View File

@ -0,0 +1,140 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
function paymentSuccess(aRequestId) {
return paymentCallback(aRequestId, "Payment:Success");
}
function paymentFailed(aRequestId) {
return paymentCallback(aRequestId, "Payment:Failed");
}
function paymentCallback(aRequestId, aMsg) {
return function(aResult) {
closePaymentWindow(aRequestId, function() {
cpmm.sendAsyncMessage(aMsg, { result: aResult,
requestId: aRequestId });
});
};
}
let payments = {};
function closePaymentWindow(aId, aCallback) {
if (payments[aId]) {
payments[aId].handled = true;
payments[aId].win.close();
payments[aId] = null;
}
aCallback();
}
function PaymentUI() {}
PaymentUI.prototype = {
classID: Components.ID("{ede1124f-72e8-4a31-9567-3270d46f21fb}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]),
confirmPaymentRequest: function(aRequestId, aRequests, aSuccessCb, aErrorCb) {
// If there's only one payment provider that will work, just move on
// without prompting the user.
if (aRequests.length == 1) {
aSuccessCb.onresult(aRequestId, aRequests[0].wrappedJSObject.type);
return;
}
let items = [];
// Otherwise, let the user select a payment provider from a list.
for (let i = 0; i < aRequests.length; i++) {
let request = aRequests[i].wrappedJSObject;
let requestText = request.providerName;
if (request.productPrice && Array.isArray(request.productPrice)) {
// We should guess the user currency and use that instead.
requestText += " (" + request.productPrice[0].amount + " " +
request.productPrice[0].currency + ")";
}
items.push(requestText);
}
let selected = {};
let bundle = Services.strings.
createBundle("chrome://webapprt/locale/webapp.properties");
let result = Services.prompt.
select(null, bundle.GetStringFromName("paymentDialog.title"),
bundle.GetStringFromName("paymentDialog.message"),
items.length, items, selected);
if (result) {
aSuccessCb.onresult(aRequestId,
aRequests[selected.value].wrappedJSObject.type);
} else {
aErrorCb.onresult(aRequestId, "USER_CANCELLED");
}
},
showPaymentFlow: function(aRequestId, aPaymentFlowInfo, aErrorCb) {
let win = Services.ww.
openWindow(null,
"chrome://webapprt/content/webapp.xul",
"_blank",
"chrome,dialog=no,resizable,scrollbars,centerscreen",
null);
// Store a reference to the window so that we can close it when the payment
// succeeds or fails.
payments[aRequestId] = { win: win, handled: false };
// Inject paymentSuccess and paymentFailed methods into the document after
// its loaded.
win.addEventListener("DOMWindowCreated", function() {
let browserElement = win.document.getElementById("content");
browserElement.
setAttribute("src", aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt);
browserElement.addEventListener("DOMWindowCreated", function() {
win.document.getElementById("content").contentDocument.defaultView
.wrappedJSObject.mozPaymentProvider = {
__exposedProps__: {
paymentSuccess: 'r',
paymentFailed: 'r'
},
paymentSuccess: paymentSuccess(aRequestId),
paymentFailed: paymentFailed(aRequestId)
};
}, true);
});
let winObserver = function(aClosedWin, aTopic) {
if (aTopic == "domwindowclosed") {
// Fail the payment if the window is closed.
if (aClosedWin == win) {
Services.ww.unregisterNotification(winObserver);
if (!payments[aRequestId].handled) {
aErrorCb.onresult(aRequestId, "USER_CANCELLED");
}
}
}
}
Services.ww.registerNotification(winObserver);
},
cleanup: function() {
},
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUI]);

View File

@ -17,6 +17,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Webapps.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
Cu.import('resource://gre/modules/Payment.jsm');
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Promise.jsm");

View File

@ -11,3 +11,7 @@ contract @mozilla.org/content-permission/prompt;1 {07ef5b2e-88fb-47bd-8cec-d3b0b
component {e1799fda-4b2f-4457-b671-e0641d95698d} DirectoryProvider.js
contract @mozilla.org/webapprt/directory-provider;1 {e1799fda-4b2f-4457-b671-e0641d95698d}
category xpcom-directory-providers webapprt-directory-provider @mozilla.org/webapprt/directory-provider;1
# PaymentUIGlue.js
component {ede1124f-72e8-4a31-9567-3270d46f21fb} PaymentUIGlue.js
contract @mozilla.org/payment/ui-glue;1 {ede1124f-72e8-4a31-9567-3270d46f21fb}

View File

@ -40,3 +40,6 @@ webapps.install.title=Install %S
webapps.install.description=Do you want to install %S?
webapps.install.install=Install App
webapps.install.dontinstall=Don't Install
paymentDialog.title=Payment
paymentDialog.message=Which payment provider do you want to use?

View File

@ -18,6 +18,7 @@ EXTRA_COMPONENTS += [
'CommandLineHandler.js',
'ContentPermission.js',
'DirectoryProvider.js',
'PaymentUIGlue.js',
'components.manifest',
]

View File

@ -46,6 +46,18 @@ pref("dom.mozTCPSocket.enabled", true);
// Enable smooth scrolling
pref("general.smoothScroll", true);
// WebPayment
pref("dom.mozPay.enabled", true);
#ifndef RELEASE_BUILD
// Enable mozPay default provider
pref("dom.payment.provider.0.name", "Firefox Marketplace");
pref("dom.payment.provider.0.description", "marketplace.firefox.com");
pref("dom.payment.provider.0.uri", "https://marketplace.firefox.com/mozpay/?req=");
pref("dom.payment.provider.0.type", "mozilla/payments/pay/v1");
pref("dom.payment.provider.0.requestMethod", "GET");
#endif
// Enable window resize and move
pref("dom.always_allow_move_resize_window", true);