mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 12:25:53 +00:00
Merge m-c to inbound
This commit is contained in:
commit
13a762accd
@ -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");
|
||||
|
@ -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}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "6b82ab58ac7e7cbce89a6718659311a1ce6b7436",
|
||||
"revision": "e836e146aa9bdc2b4141dfa86662eed46d735fb2",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
127
browser/devtools/shared/test/browser_telemetry_button_tilt.js
Normal file
127
browser/devtools/shared/test/browser_telemetry_button_tilt.js
Normal 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;
|
||||
}
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -113,6 +113,7 @@ TextTrack::AddRegion(TextTrackRegion& aRegion)
|
||||
{
|
||||
TextTrackRegion* region = mRegionList->GetRegionById(aRegion.Id());
|
||||
if (!region) {
|
||||
aRegion.SetTextTrack(this);
|
||||
mRegionList->AddTextTrackRegion(&aRegion);
|
||||
return;
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
33
content/media/test/test_mediarecorder_record_nosrc.html
Normal file
33
content/media/test/test_mediarecorder_record_nosrc.html
Normal 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>
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
6
dom/inputmethod/InputMethod.manifest
Normal file
6
dom/inputmethod/InputMethod.manifest
Normal 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}
|
@ -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
26
dom/inputmethod/moz.build
Normal 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
|
71
dom/inputmethod/nsIB2GKeyboard.idl
Normal file
71
dom/inputmethod/nsIB2GKeyboard.idl
Normal 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);
|
||||
};
|
@ -74,6 +74,7 @@ PARALLEL_DIRS += [
|
||||
'promise',
|
||||
'wappush',
|
||||
'telephony',
|
||||
'inputmethod',
|
||||
'webidl',
|
||||
]
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -104,4 +104,7 @@ class NativePanZoomController implements PanZoomController, GeckoEventListener {
|
||||
}
|
||||
|
||||
public native void updateScrollOffset(float cssX, float cssY);
|
||||
|
||||
public void setOverscrollHandler(final Overscroll listener) {
|
||||
}
|
||||
}
|
||||
|
127
mobile/android/base/gfx/Overscroll.java
Normal file
127
mobile/android/base/gfx/Overscroll.java
Normal 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;
|
||||
}
|
||||
}
|
@ -42,4 +42,6 @@ public interface PanZoomController {
|
||||
public int getOverScrollMode();
|
||||
|
||||
public void updateScrollOffset(float cssX, float cssY);
|
||||
|
||||
public void setOverscrollHandler(final Overscroll controller);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -39,7 +39,7 @@
|
||||
[testDistribution]
|
||||
[testFindInPage]
|
||||
[testInputUrlBar]
|
||||
# [testAddSearchEngine] # disabled on fig - bug 880060
|
||||
[testAddSearchEngine]
|
||||
[testImportFromAndroid]
|
||||
[testMasterPassword]
|
||||
[testDeviceSearchEngine]
|
||||
|
@ -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>
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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") {
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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
140
webapprt/PaymentUIGlue.js
Normal 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]);
|
@ -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");
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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?
|
||||
|
@ -18,6 +18,7 @@ EXTRA_COMPONENTS += [
|
||||
'CommandLineHandler.js',
|
||||
'ContentPermission.js',
|
||||
'DirectoryProvider.js',
|
||||
'PaymentUIGlue.js',
|
||||
'components.manifest',
|
||||
]
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user