mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Merge m-c to inbound.
This commit is contained in:
commit
7a6613cc88
@ -5,7 +5,7 @@
|
||||
"use strict"
|
||||
|
||||
function debug(str) {
|
||||
//dump("-*- ContentPermissionPrompt: " + str + "\n");
|
||||
//dump("-*- ContentPermissionPrompt: " + s + "\n");
|
||||
}
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
@ -13,14 +13,11 @@ const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
|
||||
const PROMPT_FOR_UNKNOWN = ["audio-capture",
|
||||
"desktop-notification",
|
||||
"geolocation",
|
||||
"video-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", "video-capture"];
|
||||
const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"];
|
||||
const PERMISSION_NO_SESSION = ["audio-capture"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -44,21 +41,7 @@ XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"@mozilla.org/telephony/audiomanager;1",
|
||||
"nsIAudioManager");
|
||||
|
||||
/**
|
||||
* aTypesInfo is an array of {permission, access, action, deny} which keeps
|
||||
* the information of each permission. This arrary is initialized in
|
||||
* ContentPermissionPrompt.prompt and used among functions.
|
||||
*
|
||||
* aTypesInfo[].permission : permission name
|
||||
* aTypesInfo[].access : permission name + request.access
|
||||
* aTypesInfo[].action : the default action of this permission
|
||||
* aTypesInfo[].deny : true if security manager denied this app's origin
|
||||
* principal.
|
||||
* Note:
|
||||
* aTypesInfo[].permission will be sent to prompt only when
|
||||
* aTypesInfo[].action is PROMPT_ACTION and aTypesInfo[].deny is false.
|
||||
*/
|
||||
function rememberPermission(aTypesInfo, aPrincipal, aSession)
|
||||
function rememberPermission(aPermission, aPrincipal, aSession)
|
||||
{
|
||||
function convertPermToAllow(aPerm, aPrincipal)
|
||||
{
|
||||
@ -66,13 +49,12 @@ function rememberPermission(aTypesInfo, aPrincipal, aSession)
|
||||
permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm);
|
||||
if (type == Ci.nsIPermissionManager.PROMPT_ACTION ||
|
||||
(type == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)) {
|
||||
debug("add " + aPerm + " to permission manager with ALLOW_ACTION");
|
||||
PROMPT_FOR_UNKNOWN.indexOf(aPermission) >= 0)) {
|
||||
if (!aSession) {
|
||||
permissionManager.addFromPrincipal(aPrincipal,
|
||||
aPerm,
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
} else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
|
||||
} else if (PERMISSION_NO_SESSION.indexOf(aPermission) < 0) {
|
||||
permissionManager.addFromPrincipal(aPrincipal,
|
||||
aPerm,
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
@ -81,18 +63,14 @@ function rememberPermission(aTypesInfo, aPrincipal, aSession)
|
||||
}
|
||||
}
|
||||
|
||||
for (let i in aTypesInfo) {
|
||||
// Expand the permission to see if we have multiple access properties
|
||||
// to convert
|
||||
let perm = aTypesInfo[i].permission;
|
||||
let access = PermissionsTable[perm].access;
|
||||
if (access) {
|
||||
for (let idx in access) {
|
||||
convertPermToAllow(perm + "-" + access[idx], aPrincipal);
|
||||
}
|
||||
} else {
|
||||
convertPermToAllow(perm, aPrincipal);
|
||||
// Expand the permission to see if we have multiple access properties to convert
|
||||
let access = PermissionsTable[aPermission].access;
|
||||
if (access) {
|
||||
for (let idx in access) {
|
||||
convertPermToAllow(aPermission + "-" + access[idx], aPrincipal);
|
||||
}
|
||||
} else {
|
||||
convertPermToAllow(aPermission, aPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,66 +78,23 @@ function ContentPermissionPrompt() {}
|
||||
|
||||
ContentPermissionPrompt.prototype = {
|
||||
|
||||
handleExistingPermission: function handleExistingPermission(request,
|
||||
typesInfo) {
|
||||
typesInfo.forEach(function(type) {
|
||||
type.action =
|
||||
Services.perms.testExactPermissionFromPrincipal(request.principal,
|
||||
type.access);
|
||||
if (type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(type.access) >= 0) {
|
||||
type.action = Ci.nsIPermissionManager.PROMPT_ACTION;
|
||||
}
|
||||
});
|
||||
|
||||
// If all permissions are allowed already, call allow() without prompting.
|
||||
let checkAllowPermission = function(type) {
|
||||
if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (typesInfo.every(checkAllowPermission)) {
|
||||
debug("all permission requests are allowed");
|
||||
handleExistingPermission: function handleExistingPermission(request) {
|
||||
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
|
||||
request.type;
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, access);
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If all permissions are DENY_ACTION or UNKNOWN_ACTION, call cancel()
|
||||
// without prompting.
|
||||
let checkDenyPermission = function(type) {
|
||||
if (type.action == Ci.nsIPermissionManager.DENY_ACTION ||
|
||||
type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (typesInfo.every(checkDenyPermission)) {
|
||||
debug("all permission requests are denied");
|
||||
if (result == Ci.nsIPermissionManager.DENY_ACTION ||
|
||||
result == Ci.nsIPermissionManager.UNKNOWN_ACTION && PROMPT_FOR_UNKNOWN.indexOf(access) < 0) {
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// multiple requests should be audio and video
|
||||
checkMultipleRequest: function checkMultipleRequest(typesInfo) {
|
||||
if (typesInfo.length == 1) {
|
||||
return true;
|
||||
} else if (typesInfo.length > 1) {
|
||||
let checkIfAllowMultiRequest = function(type) {
|
||||
return (ALLOW_MULTIPLE_REQUESTS.indexOf(type.access) !== -1);
|
||||
}
|
||||
if (typesInfo.every(checkIfAllowMultiRequest)) {
|
||||
debug("legal multiple requests");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
handledByApp: function handledByApp(request, typesInfo) {
|
||||
handledByApp: function handledByApp(request) {
|
||||
if (request.principal.appId == Ci.nsIScriptSecurityManager.NO_APP_ID ||
|
||||
request.principal.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
|
||||
// This should not really happen
|
||||
@ -171,94 +106,49 @@ ContentPermissionPrompt.prototype = {
|
||||
.getService(Ci.nsIAppsService);
|
||||
let app = appsService.getAppByLocalId(request.principal.appId);
|
||||
|
||||
// Check each permission if it's denied by permission manager with app's
|
||||
// URL.
|
||||
let notDenyAppPrincipal = function(type) {
|
||||
let url = Services.io.newURI(app.origin, null, null);
|
||||
let principal = secMan.getAppCodebasePrincipal(url,
|
||||
request.principal.appId,
|
||||
/*mozbrowser*/false);
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(principal,
|
||||
type.access);
|
||||
let url = Services.io.newURI(app.origin, null, null);
|
||||
let principal = secMan.getAppCodebasePrincipal(url, request.principal.appId,
|
||||
/*mozbrowser*/false);
|
||||
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
|
||||
request.type;
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(principal, access);
|
||||
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
|
||||
result == Ci.nsIPermissionManager.PROMPT_ACTION) {
|
||||
type.deny = false;
|
||||
}
|
||||
return !type.deny;
|
||||
}
|
||||
if (typesInfo.filter(notDenyAppPrincipal).length === 0) {
|
||||
request.cancel();
|
||||
return true;
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
|
||||
result == Ci.nsIPermissionManager.PROMPT_ACTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
request.cancel();
|
||||
return true;
|
||||
},
|
||||
|
||||
handledByPermissionType: function handledByPermissionType(request, typesInfo) {
|
||||
for (let i in typesInfo) {
|
||||
if (permissionSpecificChecker.hasOwnProperty(typesInfo[i].permission) &&
|
||||
permissionSpecificChecker[typesInfo[i].permission](request)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
handledByPermissionType: function handledByPermissionType(request) {
|
||||
return permissionSpecificChecker.hasOwnProperty(request.type)
|
||||
? permissionSpecificChecker[request.type](request)
|
||||
: false;
|
||||
},
|
||||
|
||||
_id: 0,
|
||||
prompt: function(request) {
|
||||
if (secMan.isSystemPrincipal(request.principal)) {
|
||||
request.allow();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize the typesInfo and set the default value.
|
||||
let typesInfo = [];
|
||||
let perms = request.types.QueryInterface(Ci.nsIArray);
|
||||
for (let idx = 0; idx < perms.length; idx++) {
|
||||
let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
|
||||
let tmp = {
|
||||
permission: perm.type,
|
||||
access: (perm.access && perm.access !== "unused") ?
|
||||
perm.type + "-" + perm.access : perm.type,
|
||||
deny: true,
|
||||
action: Ci.nsIPermissionManager.UNKNOWN_ACTION
|
||||
};
|
||||
typesInfo.push(tmp);
|
||||
}
|
||||
if (typesInfo.length == 0) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.checkMultipleRequest(typesInfo)) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.handledByApp(request, typesInfo) ||
|
||||
this.handledByPermissionType(request, typesInfo)) {
|
||||
if (this.handledByApp(request) ||
|
||||
this.handledByPermissionType(request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// returns true if the request was handled
|
||||
if (this.handleExistingPermission(request, typesInfo)) {
|
||||
if (this.handleExistingPermission(request))
|
||||
return;
|
||||
}
|
||||
|
||||
// prompt PROMPT_ACTION request only.
|
||||
typesInfo.forEach(function(aType, aIndex) {
|
||||
if (aType.action != Ci.nsIPermissionManager.PROMPT_ACTION || aType.deny) {
|
||||
typesInfo.splice(aIndex);
|
||||
}
|
||||
});
|
||||
|
||||
let frame = request.element;
|
||||
let requestId = this._id++;
|
||||
|
||||
if (!frame) {
|
||||
this.delegatePrompt(request, requestId, typesInfo);
|
||||
this.delegatePrompt(request, requestId);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -273,7 +163,7 @@ ContentPermissionPrompt.prototype = {
|
||||
if (evt.detail.visible === true)
|
||||
return;
|
||||
|
||||
self.cancelPrompt(request, requestId, typesInfo);
|
||||
self.cancelPrompt(request, requestId);
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
@ -290,7 +180,7 @@ ContentPermissionPrompt.prototype = {
|
||||
// away but the request is still here.
|
||||
frame.addEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
|
||||
self.delegatePrompt(request, requestId, typesInfo, function onCallback() {
|
||||
self.delegatePrompt(request, requestId, function onCallback() {
|
||||
frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
});
|
||||
};
|
||||
@ -301,17 +191,22 @@ ContentPermissionPrompt.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
cancelPrompt: function(request, requestId, typesInfo) {
|
||||
this.sendToBrowserWindow("cancel-permission-prompt", request, requestId,
|
||||
typesInfo);
|
||||
cancelPrompt: function(request, requestId) {
|
||||
this.sendToBrowserWindow("cancel-permission-prompt", request, requestId);
|
||||
},
|
||||
|
||||
delegatePrompt: function(request, requestId, typesInfo, callback) {
|
||||
delegatePrompt: function(request, requestId, callback) {
|
||||
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
|
||||
request.type;
|
||||
let principal = request.principal;
|
||||
|
||||
this.sendToBrowserWindow("permission-prompt", request, requestId, typesInfo,
|
||||
function(type, remember) {
|
||||
this._permission = access;
|
||||
this._uri = principal.URI.spec;
|
||||
this._origin = principal.origin;
|
||||
|
||||
this.sendToBrowserWindow("permission-prompt", request, requestId, function(type, remember) {
|
||||
if (type == "permission-allow") {
|
||||
rememberPermission(typesInfo, request.principal, !remember);
|
||||
rememberPermission(request.type, principal, !remember);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
@ -319,20 +214,14 @@ ContentPermissionPrompt.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let addDenyPermission = function(type) {
|
||||
debug("add " + type.permission +
|
||||
" to permission manager with DENY_ACTION");
|
||||
if (remember) {
|
||||
Services.perms.addFromPrincipal(request.principal, type.access,
|
||||
Ci.nsIPermissionManager.DENY_ACTION);
|
||||
} else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
|
||||
Services.perms.addFromPrincipal(request.principal, type.access,
|
||||
Ci.nsIPermissionManager.DENY_ACTION,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION,
|
||||
0);
|
||||
}
|
||||
if (remember) {
|
||||
Services.perms.addFromPrincipal(principal, access,
|
||||
Ci.nsIPermissionManager.DENY_ACTION);
|
||||
} else {
|
||||
Services.perms.addFromPrincipal(principal, access,
|
||||
Ci.nsIPermissionManager.DENY_ACTION,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
|
||||
}
|
||||
typesInfo.forEach(addDenyPermission);
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
@ -341,7 +230,7 @@ ContentPermissionPrompt.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
sendToBrowserWindow: function(type, request, requestId, typesInfo, callback) {
|
||||
sendToBrowserWindow: function(type, request, requestId, callback) {
|
||||
let browser = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let content = browser.getContentWindow();
|
||||
if (!content)
|
||||
@ -364,15 +253,10 @@ ContentPermissionPrompt.prototype = {
|
||||
principal.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED)
|
||||
? true
|
||||
: request.remember;
|
||||
let permissions = {};
|
||||
for (let i in typesInfo) {
|
||||
debug("prompt " + typesInfo[i].permission);
|
||||
permissions[typesInfo[i].permission] = [];
|
||||
}
|
||||
|
||||
let details = {
|
||||
type: type,
|
||||
permissions: permissions,
|
||||
permission: request.type,
|
||||
id: requestId,
|
||||
origin: principal.origin,
|
||||
isApp: isApp,
|
||||
@ -405,5 +289,6 @@ ContentPermissionPrompt.prototype = {
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
//module initialization
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);
|
||||
|
@ -47,8 +47,8 @@ FilePicker.prototype = {
|
||||
/* members */
|
||||
|
||||
mParent: undefined,
|
||||
mExtraProps: {},
|
||||
mFilterTypes: [],
|
||||
mExtraProps: undefined,
|
||||
mFilterTypes: undefined,
|
||||
mFileEnumerator: undefined,
|
||||
mFilePickerShownCallback: undefined,
|
||||
|
||||
@ -56,6 +56,8 @@ FilePicker.prototype = {
|
||||
|
||||
init: function(parent, title, mode) {
|
||||
this.mParent = parent;
|
||||
this.mExtraProps = {};
|
||||
this.mFilterTypes = [];
|
||||
this.mMode = mode;
|
||||
|
||||
if (mode != Ci.nsIFilePicker.modeOpen &&
|
||||
@ -177,12 +179,13 @@ FilePicker.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
var mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||
var mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
|
||||
|
||||
var name = 'blob';
|
||||
if (mimeInfo) {
|
||||
name += '.' + mimeInfo.primaryExtension;
|
||||
if (data.result.blob.type) {
|
||||
let mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
|
||||
let mimeInfo = mimeSvc.getFromTypeAndExtension(data.result.blob.type, '');
|
||||
if (mimeInfo) {
|
||||
name += '.' + mimeInfo.primaryExtension;
|
||||
}
|
||||
}
|
||||
|
||||
let file = new this.mParent.File(data.result.blob,
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "bb23044d4a97be1381be924064817dddbd2ea47b",
|
||||
"revision": "9f1117fde1d221d998c065a87e0614af6239b585",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -1056,11 +1056,6 @@ let CustomizableUIInternal = {
|
||||
}
|
||||
}
|
||||
|
||||
if (aWidget.id == "switch-to-metro-button") {
|
||||
let brandBundle = aDocument.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
additionalTooltipArguments = [brandShortName];
|
||||
}
|
||||
let tooltip = this.getLocalizedProperty(aWidget, "tooltiptext", additionalTooltipArguments);
|
||||
node.setAttribute("tooltiptext", tooltip);
|
||||
node.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
|
||||
@ -1072,9 +1067,8 @@ let CustomizableUIInternal = {
|
||||
|
||||
// If the widget has a view, and has view showing / hiding listeners,
|
||||
// hook those up to this widget.
|
||||
if (aWidget.type == "view" &&
|
||||
(aWidget.onViewShowing || aWidget.onViewHiding)) {
|
||||
LOG("Widget " + aWidget.id + " has a view with showing and hiding events. Auto-registering event handlers.");
|
||||
if (aWidget.type == "view") {
|
||||
LOG("Widget " + aWidget.id + " has a view. Auto-registering event handlers.");
|
||||
let viewNode = aDocument.getElementById(aWidget.viewId);
|
||||
|
||||
if (viewNode) {
|
||||
@ -1753,10 +1747,6 @@ let CustomizableUIInternal = {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (aData.id == "switch-to-metro-button") {
|
||||
widget.showInPrivateBrowsing = false;
|
||||
}
|
||||
|
||||
delete widget.implementation.currentArea;
|
||||
widget.implementation.__defineGetter__("currentArea", function() widget.currentArea);
|
||||
|
||||
@ -2270,8 +2260,8 @@ this.CustomizableUI = {
|
||||
* and an onWidgetBeforeDOMChange and onWidgetAfterDOMChange notification
|
||||
* for each window CustomizableUI knows about.
|
||||
*
|
||||
* @param aWidgetId the widget to add
|
||||
* @param aArea the area to add the widget to
|
||||
* @param aWidgetId the ID of the widget to add
|
||||
* @param aArea the ID of the area to add the widget to
|
||||
* @param aPosition the position at which to add the widget. If you do not
|
||||
* pass a position, the widget will be added to the end
|
||||
* of the area.
|
||||
@ -2286,7 +2276,7 @@ this.CustomizableUI = {
|
||||
* onWidgetAfterDOMChange notification for each window CustomizableUI knows
|
||||
* about.
|
||||
*
|
||||
* @param aWidgetId the widget to remove
|
||||
* @param aWidgetId the ID of the widget to remove
|
||||
*/
|
||||
removeWidgetFromArea: function(aWidgetId) {
|
||||
CustomizableUIInternal.removeWidgetFromArea(aWidgetId);
|
||||
@ -2300,7 +2290,7 @@ this.CustomizableUI = {
|
||||
* and an onWidgetBeforeDOMChange and onWidgetAfterDOMChange notification for
|
||||
* each window CustomizableUI knows about.
|
||||
*
|
||||
* @param aWidgetid the widget to move
|
||||
* @param aWidgetId the ID of the widget to move
|
||||
* @param aPosition the position to move the widget to.
|
||||
* Negative values or values greater than the number of
|
||||
* widgets will be interpreted to mean moving the widget to
|
||||
@ -2318,7 +2308,7 @@ this.CustomizableUI = {
|
||||
* because it delegates to addWidgetToArea) or, worse, moving items in the
|
||||
* DOM yourself.
|
||||
*
|
||||
* @param aWidgetId the widget that was just created
|
||||
* @param aWidgetId the ID of the widget that was just created
|
||||
* @param aWindow the window in which you want to ensure it was added.
|
||||
*
|
||||
* NB: why is this API per-window, you wonder? Because if you need this,
|
||||
@ -2335,7 +2325,7 @@ this.CustomizableUI = {
|
||||
* Calls to begin/endBatchUpdate may be nested.
|
||||
*
|
||||
* Callers should ensure that NO MATTER WHAT they call endBatchUpdate once
|
||||
* for each call to endBatchUpdate, even if there are exceptions in the
|
||||
* for each call to beginBatchUpdate, even if there are exceptions in the
|
||||
* code in the batch update. Otherwise, for the duration of the
|
||||
* Firefox session, customization state is never saved. Typically, you
|
||||
* would do this using a try...finally block.
|
||||
@ -2406,6 +2396,7 @@ this.CustomizableUI = {
|
||||
* mode (optional, default: true)
|
||||
*
|
||||
* @param aProperties the specifications for the widget.
|
||||
* @return a wrapper around the created widget (see getWidget)
|
||||
*/
|
||||
createWidget: function(aProperties) {
|
||||
return CustomizableUIInternal.wrapWidget(
|
||||
@ -2421,20 +2412,105 @@ this.CustomizableUI = {
|
||||
* in at the time. You can remove it from there yourself by calling
|
||||
* CustomizableUI.removeWidgetFromArea(aWidgetId).
|
||||
*
|
||||
* @param aWidgetId the widget to destroy
|
||||
* @param aWidgetId the ID of the widget to destroy
|
||||
*/
|
||||
destroyWidget: function(aWidgetId) {
|
||||
CustomizableUIInternal.destroyWidget(aWidgetId);
|
||||
},
|
||||
/**
|
||||
* Get a wrapper object with information about the widget.
|
||||
* The object provides the following properties
|
||||
* (all read-only unless otherwise indicated):
|
||||
*
|
||||
* - id: the widget's ID;
|
||||
* - type: the type of widget (button, view, custom). For
|
||||
* XUL-provided widgets, this is always 'custom';
|
||||
* - provider: the provider type of the widget, id est one of
|
||||
* PROVIDER_API or PROVIDER_XUL;
|
||||
* - forWindow(w): a method to obtain a single window wrapper for a widget,
|
||||
* in the window w passed as the only argument;
|
||||
* - instances: an array of all instances (single window wrappers)
|
||||
* of the widget. This array is NOT live;
|
||||
* - areaType: the type of the widget's current area
|
||||
* - isGroup: true; will be false for wrappers around single widget nodes;
|
||||
* - source: for API-provided widgets, whether they are built-in to
|
||||
* Firefox or add-on-provided;
|
||||
* - disabled: for API-provided widgets, whether the widget is currently
|
||||
* disabled. NB: this property is writable, and will toggle
|
||||
* all the widgets' disabled state;
|
||||
* - label: for API-provied widgets, the label of the widget;
|
||||
* - tooltiptext: for API-provided widgets, the tooltip of the widget;
|
||||
* - showInPrivateBrowsing: for API-provided widgets, whether the widget is
|
||||
* visible in private browsing;
|
||||
*
|
||||
* Single window wrappers obtained through forWindow(someWindow) or from the
|
||||
* instances array have the following properties
|
||||
* (all read-only unless otherwise indicated):
|
||||
*
|
||||
* - id: the widget's ID;
|
||||
* - type: the type of widget (button, view, custom). For
|
||||
* XUL-provided widgets, this is always 'custom';
|
||||
* - provider: the provider type of the widget, id est one of
|
||||
* PROVIDER_API or PROVIDER_XUL;
|
||||
* - node: reference to the corresponding DOM node;
|
||||
* - anchor: the anchor on which to anchor panels opened from this
|
||||
* node. This will point to the overflow chevron on
|
||||
* overflowable toolbars if and only if your widget node
|
||||
* is overflowed, to the anchor for the panel menu
|
||||
* if your widget is inside the panel menu, and to the
|
||||
* node itself in all other cases;
|
||||
* - overflowed: boolean indicating whether the node is currently in the
|
||||
* overflow panel of the toolbar;
|
||||
* - isGroup: false; will be true for the group widget;
|
||||
* - label: for API-provided widgets, convenience getter for the
|
||||
* label attribute of the DOM node;
|
||||
* - tooltiptext: for API-provided widgets, convenience getter for the
|
||||
* tooltiptext attribute of the DOM node;
|
||||
* - disabled: for API-provided widgets, convenience getter *and setter*
|
||||
* for the disabled state of this single widget. Note that
|
||||
* you may prefer to use the group wrapper's getter/setter
|
||||
* instead.
|
||||
*
|
||||
* @param aWidgetId the ID of the widget whose information you need
|
||||
* @return a wrapper around the widget as described above, or null if the
|
||||
* widget is known not to exist (anymore). NB: non-null return
|
||||
* is no guarantee the widget exists because we cannot know in
|
||||
* advance if a XUL widget exists or not.
|
||||
*/
|
||||
getWidget: function(aWidgetId) {
|
||||
return CustomizableUIInternal.wrapWidget(aWidgetId);
|
||||
},
|
||||
/**
|
||||
* Get an array of widget wrappers (see getWidget) for all the widgets
|
||||
* which are currently not in any area (so which are in the palette).
|
||||
*
|
||||
* @param aWindowPalette the palette (and by extension, the window) in which
|
||||
* CustomizableUI should look. This matters because of
|
||||
* course XUL-provided widgets could be available in
|
||||
* some windows but not others, and likewise
|
||||
* API-provided widgets might not exist in a private
|
||||
* window (because of the showInPrivateBrowsing
|
||||
* property).
|
||||
*
|
||||
* @return an array of widget wrappers (see getWidget)
|
||||
*/
|
||||
getUnusedWidgets: function(aWindowPalette) {
|
||||
return CustomizableUIInternal.getUnusedWidgets(aWindowPalette).map(
|
||||
CustomizableUIInternal.wrapWidget,
|
||||
CustomizableUIInternal
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Get an array of all the widget IDs placed in an area. This is roughly
|
||||
* equivalent to fetching the currentset attribute and splitting by commas
|
||||
* in the legacy APIs. Modifying the array will not affect CustomizableUI.
|
||||
*
|
||||
* @param aArea the ID of the area whose placements you want to obtain.
|
||||
* @return an array containing the widget IDs that are in the area.
|
||||
*
|
||||
* NB: will throw if called too early (before placements have been fetched)
|
||||
* or if the area is not currently known to CustomizableUI.
|
||||
*/
|
||||
getWidgetIdsInArea: function(aArea) {
|
||||
if (!gAreas.has(aArea)) {
|
||||
throw new Error("Unknown customization area: " + aArea);
|
||||
@ -2446,67 +2522,261 @@ this.CustomizableUI = {
|
||||
// We need to clone this, as we don't want to let consumers muck with placements
|
||||
return [...gPlacements.get(aArea)];
|
||||
},
|
||||
/**
|
||||
* Get an array of widget wrappers for all the widgets in an area. This is
|
||||
* the same as calling getWidgetIdsInArea and .map() ing the result through
|
||||
* CustomizableUI.getWidget. Careful: this means that if there are IDs in there
|
||||
* which don't have corresponding DOM nodes (like in the old-style currentset
|
||||
* attribute), there might be nulls in this array, or items for which
|
||||
* wrapper.forWindow(win) will return null.
|
||||
*
|
||||
* @param aArea the ID of the area whose widgets you want to obtain.
|
||||
* @return an array of widget wrappers and/or null values for the widget IDs
|
||||
* placed in an area.
|
||||
*
|
||||
* NB: will throw if called too early (before placements have been fetched)
|
||||
* or if the area is not currently known to CustomizableUI.
|
||||
*/
|
||||
getWidgetsInArea: function(aArea) {
|
||||
return this.getWidgetIdsInArea(aArea).map(
|
||||
CustomizableUIInternal.wrapWidget,
|
||||
CustomizableUIInternal
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Obtain an array of all the area IDs known to CustomizableUI.
|
||||
* This array is created for you, so is modifiable without CustomizableUI
|
||||
* being affected.
|
||||
*/
|
||||
get areas() {
|
||||
return [area for ([area, props] of gAreas)];
|
||||
},
|
||||
/**
|
||||
* Check what kind of area (toolbar or menu panel) an area is. This is
|
||||
* useful if you have a widget that needs to behave differently depending
|
||||
* on its location. Note that widget wrappers have a convenience getter
|
||||
* property (areaType) for this purpose.
|
||||
*
|
||||
* @param aArea the ID of the area whose type you want to know
|
||||
* @return TYPE_TOOLBAR or TYPE_MENU_PANEL depending on the area, null if
|
||||
* the area is unknown.
|
||||
*/
|
||||
getAreaType: function(aArea) {
|
||||
let area = gAreas.get(aArea);
|
||||
return area ? area.get("type") : null;
|
||||
},
|
||||
/**
|
||||
* Obtain the DOM node that is the customize target for an area in a
|
||||
* specific window.
|
||||
*
|
||||
* Areas can have a customization target that does not correspond to the
|
||||
* node itself. In particular, toolbars that have a customizationtarget
|
||||
* attribute set will have their customization target set to that node.
|
||||
* This means widgets will end up in the customization target, not in the
|
||||
* DOM node with the ID that corresponds to the area ID. This is useful
|
||||
* because it lets you have fixed content in a toolbar (e.g. the panel
|
||||
* menu item in the navbar) and have all the customizable widgets use
|
||||
* the customization target.
|
||||
*
|
||||
* Using this API yourself is discouraged; you should generally not need
|
||||
* to be asking for the DOM container node used for a particular area.
|
||||
* In particular, if you're wanting to check it in relation to a widget's
|
||||
* node, your DOM node might not be a direct child of the customize target
|
||||
* in a window if, for instance, the window is in customization mode, or if
|
||||
* this is an overflowable toolbar and the widget has been overflowed.
|
||||
*
|
||||
* @param aArea the ID of the area whose customize target you want to have
|
||||
* @param aWindow the window where you want to fetch the DOM node.
|
||||
* @return the customize target DOM node for aArea in aWindow
|
||||
*/
|
||||
getCustomizeTargetForArea: function(aArea, aWindow) {
|
||||
return CustomizableUIInternal.getCustomizeTargetForArea(aArea, aWindow);
|
||||
},
|
||||
/**
|
||||
* Reset the customization state back to its default.
|
||||
*
|
||||
* This is the nuclear option. You should never call this except if the user
|
||||
* explicitly requests it. Firefox does this when the user clicks the
|
||||
* "Restore Defaults" button in customize mode.
|
||||
*/
|
||||
reset: function() {
|
||||
CustomizableUIInternal.reset();
|
||||
},
|
||||
/**
|
||||
* Get the placement of a widget. This is by far the best way to obtain
|
||||
* information about what the state of your widget is. The internals of
|
||||
* this call are cheap (no DOM necessary) and you will know where the user
|
||||
* has put your widget.
|
||||
*
|
||||
* @param aWidgetId the ID of the widget whose placement you want to know
|
||||
* @return
|
||||
* {
|
||||
* area: "somearea", // The ID of the area where the widget is placed
|
||||
* position: 42 // the index in the placements array corresponding to
|
||||
* // your widget.
|
||||
* }
|
||||
*
|
||||
* OR
|
||||
*
|
||||
* null // if the widget is not placed anywhere (ie in the palette)
|
||||
*/
|
||||
getPlacementOfWidget: function(aWidgetId) {
|
||||
return CustomizableUIInternal.getPlacementOfWidget(aWidgetId, true);
|
||||
},
|
||||
/**
|
||||
* Check if a widget can be removed from the area it's in.
|
||||
*
|
||||
* Note that if you're wanting to move the widget somewhere, you should
|
||||
* generally be checking canWidgetMoveToArea, because that will return
|
||||
* true if the widget is already in the area where you want to move it (!).
|
||||
*
|
||||
* NB: oh, also, this method might lie if the widget in question is a
|
||||
* XUL-provided widget and there are no windows open, because it
|
||||
* can obviously not check anything in this case. It will return
|
||||
* true. You will be able to move the widget elsewhere. However,
|
||||
* once the user reopens a window, the widget will move back to its
|
||||
* 'proper' area automagically.
|
||||
*
|
||||
* @param aWidgetId a widget ID or DOM node to check
|
||||
* @return true if the widget can be removed from its area,
|
||||
* false otherwise.
|
||||
*/
|
||||
isWidgetRemovable: function(aWidgetId) {
|
||||
return CustomizableUIInternal.isWidgetRemovable(aWidgetId);
|
||||
},
|
||||
/**
|
||||
* Check if a widget can be moved to a particular area. Like
|
||||
* isWidgetRemovable but better, because it'll return true if the widget
|
||||
* is already in the right area.
|
||||
*
|
||||
* @param aWidgetId the widget ID or DOM node you want to move somewhere
|
||||
* @param aArea the area ID you want to move it to.
|
||||
* @return true if this is possible, false if it is not. Same caveats as
|
||||
* for isWidgetRemovable apply, however, if no windows are open.
|
||||
*/
|
||||
canWidgetMoveToArea: function(aWidgetId, aArea) {
|
||||
return CustomizableUIInternal.canWidgetMoveToArea(aWidgetId, aArea);
|
||||
},
|
||||
/**
|
||||
* Whether we're in a default state.
|
||||
*
|
||||
* NB: this is a property with a getter. The getter is NOT cheap, because
|
||||
* it does smart things with non-removable non-default items, non-existent
|
||||
* items, and so forth. Please don't call unless necessary.
|
||||
*/
|
||||
get inDefaultState() {
|
||||
return CustomizableUIInternal.inDefaultState;
|
||||
},
|
||||
/**
|
||||
* Get a localized property off a (widget?) object.
|
||||
*
|
||||
* NB: this is unlikely to be useful unless you're in Firefox code, because
|
||||
* this code uses the builtin widget stringbundle, and can't be told
|
||||
* to use add-on-provided strings. It's mainly here as convenience for
|
||||
* custom builtin widgets that build their own DOM but use the same
|
||||
* stringbundle as the other builtin widgets.
|
||||
*
|
||||
* @param aWidget the object whose property we should use to fetch a
|
||||
* localizable string;
|
||||
* @param aProp the property on the object to use for the fetching;
|
||||
* @param aFormatArgs (optional) any extra arguments to use for a formatted
|
||||
* string;
|
||||
* @param aDef (optional) the default to return if we don't find the
|
||||
* string in the stringbundle;
|
||||
*
|
||||
* @return the localized string, or aDef if the string isn't in the bundle.
|
||||
* If no default is provided,
|
||||
* if aProp exists on aWidget, we'll return that,
|
||||
* otherwise we'll return the empty string
|
||||
*
|
||||
*/
|
||||
getLocalizedProperty: function(aWidget, aProp, aFormatArgs, aDef) {
|
||||
return CustomizableUIInternal.getLocalizedProperty(aWidget, aProp,
|
||||
aFormatArgs, aDef);
|
||||
},
|
||||
/**
|
||||
* Given a node, walk up to the first panel in its ancestor chain, and
|
||||
* close it.
|
||||
*
|
||||
* @param aNode a node whose panel should be closed;
|
||||
*/
|
||||
hidePanelForNode: function(aNode) {
|
||||
CustomizableUIInternal.hidePanelForNode(aNode);
|
||||
},
|
||||
/**
|
||||
* Check if a widget is a "special" widget: a spring, spacer or separator.
|
||||
*
|
||||
* @param aWidgetId the widget ID to check.
|
||||
*/
|
||||
isSpecialWidget: function(aWidgetId) {
|
||||
return CustomizableUIInternal.isSpecialWidget(aWidgetId);
|
||||
},
|
||||
/**
|
||||
* Add listeners to a panel that will close it. For use from PanelUI and
|
||||
* the overflowable toolbars, unlikely to be useful for consumers.
|
||||
*
|
||||
* @param aPanel the panel to which listeners should be attached.
|
||||
*/
|
||||
addPanelCloseListeners: function(aPanel) {
|
||||
CustomizableUIInternal.addPanelCloseListeners(aPanel);
|
||||
},
|
||||
/**
|
||||
* Remove close listeners that have been added to a panel with
|
||||
* addPanelCloseListeners. For use from PanelUI and the overflowable
|
||||
* toolbars, unlikely to be useful for consumers.
|
||||
*
|
||||
* @param aPanel the panel from which listeners should be removed.
|
||||
*/
|
||||
removePanelCloseListeners: function(aPanel) {
|
||||
CustomizableUIInternal.removePanelCloseListeners(aPanel);
|
||||
},
|
||||
/**
|
||||
* Notify listeners a widget is about to be dragged to an area. For use from
|
||||
* Customize Mode only, do not use otherwise.
|
||||
*
|
||||
* @param aWidgetId the ID of the widget that is being dragged to an area.
|
||||
* @param aArea the ID of the area to which the widget is being dragged.
|
||||
*/
|
||||
onWidgetDrag: function(aWidgetId, aArea) {
|
||||
CustomizableUIInternal.notifyListeners("onWidgetDrag", aWidgetId, aArea);
|
||||
},
|
||||
/**
|
||||
* Notify listeners that a window is entering customize mode. For use from
|
||||
* Customize Mode only, do not use otherwise.
|
||||
* @param aWindow the window entering customize mode
|
||||
*/
|
||||
notifyStartCustomizing: function(aWindow) {
|
||||
CustomizableUIInternal.notifyListeners("onCustomizeStart", aWindow);
|
||||
},
|
||||
/**
|
||||
* Notify listeners that a window is exiting customize mode. For use from
|
||||
* Customize Mode only, do not use otherwise.
|
||||
* @param aWindow the window exiting customize mode
|
||||
*/
|
||||
notifyEndCustomizing: function(aWindow) {
|
||||
CustomizableUIInternal.notifyListeners("onCustomizeEnd", aWindow);
|
||||
},
|
||||
/**
|
||||
* Check whether an area is overflowable.
|
||||
*
|
||||
* @param aAreaId the ID of an area to check for overflowable-ness
|
||||
* @return true if the area is overflowable, false otherwise.
|
||||
*/
|
||||
isAreaOverflowable: function(aAreaId) {
|
||||
let area = gAreas.get(aAreaId);
|
||||
return area ? area.get("type") == this.TYPE_TOOLBAR && area.get("overflowable")
|
||||
: false;
|
||||
},
|
||||
/**
|
||||
* Obtain a string indicating the place of an element. This is intended
|
||||
* for use from customize mode; You should generally use getPlacementOfWidget
|
||||
* instead, which is cheaper because it does not use the DOM.
|
||||
*
|
||||
* @param aElement the DOM node whose place we need to check
|
||||
* @return "toolbar" if the node is in a toolbar, "panel" if it is in the
|
||||
* menu panel, "palette" if it is in the (visible!) customization
|
||||
* palette, undefined otherwise.
|
||||
*/
|
||||
getPlaceForItem: function(aElement) {
|
||||
let place;
|
||||
let node = aElement;
|
||||
|
@ -17,6 +17,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager",
|
||||
"@mozilla.org/charset-converter-manager;1",
|
||||
"nsICharsetConverterManager");
|
||||
XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
|
||||
const kBrandBundle = "chrome://branding/locale/brand.properties";
|
||||
return Services.strings.createBundle(kBrandBundle);
|
||||
});
|
||||
|
||||
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
|
||||
@ -25,14 +29,6 @@ const kWidePanelItemClass = "panel-wide-item";
|
||||
let gModuleName = "[CustomizableWidgets]";
|
||||
#include logging.js
|
||||
|
||||
function isWin8OrHigher() {
|
||||
let osName = Services.sysinfo.getProperty("name");
|
||||
let version = Services.sysinfo.getProperty("version");
|
||||
|
||||
// Windows 8 is version >= 6.2
|
||||
return osName == "Windows_NT" && Services.vc.compare(version, "6.2") >= 0;
|
||||
}
|
||||
|
||||
function setAttributes(aNode, aAttrs) {
|
||||
for (let [name, value] of Iterator(aAttrs)) {
|
||||
if (!value) {
|
||||
@ -796,17 +792,20 @@ const CustomizableWidgets = [{
|
||||
|
||||
#ifdef XP_WIN
|
||||
#ifdef MOZ_METRO
|
||||
if (isWin8OrHigher()) {
|
||||
if (Services.sysinfo.getProperty("hasWindowsTouchInterface")) {
|
||||
let widgetArgs = {tooltiptext: "switch-to-metro-button2.tooltiptext"};
|
||||
let brandShortName = BrandBundle.GetStringFromName("brandShortName");
|
||||
let metroTooltip = CustomizableUI.getLocalizedProperty(widgetArgs, "tooltiptext",
|
||||
[brandShortName]);
|
||||
CustomizableWidgets.push({
|
||||
id: "switch-to-metro-button",
|
||||
label: "switch-to-metro-button2.label",
|
||||
tooltiptext: "switch-to-metro-button2.tooltiptext",
|
||||
tooltiptext: metroTooltip,
|
||||
removable: true,
|
||||
defaultArea: CustomizableUI.AREA_PANEL,
|
||||
showInPrivateBrowsing: false, /* See bug 928068 */
|
||||
onCommand: function(aEvent) {
|
||||
let win = aEvent.target &&
|
||||
aEvent.target.ownerDocument &&
|
||||
aEvent.target.ownerDocument.defaultView;
|
||||
let win = aEvent.view;
|
||||
if (win && typeof win.SwitchToMetro == "function") {
|
||||
win.SwitchToMetro();
|
||||
}
|
||||
|
@ -1591,7 +1591,7 @@ BrowserGlue.prototype = {
|
||||
// be set to the version it has been added in, we will compare its value
|
||||
// to users' smartBookmarksVersion and add new smart bookmarks without
|
||||
// recreating old deleted ones.
|
||||
const SMART_BOOKMARKS_VERSION = 5;
|
||||
const SMART_BOOKMARKS_VERSION = 6;
|
||||
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
|
||||
const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
|
||||
|
||||
@ -1653,7 +1653,10 @@ BrowserGlue.prototype = {
|
||||
position: menuIndex++,
|
||||
newInVersion: 1
|
||||
},
|
||||
Windows8Touch: {
|
||||
};
|
||||
|
||||
if (Services.sysinfo.getProperty("hasWindowsTouchInterface")) {
|
||||
smartBookmarks.Windows8Touch = {
|
||||
title: bundle.GetStringFromName("windows8TouchTitle"),
|
||||
uri: NetUtil.newURI("place:folder=" +
|
||||
PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {})[0] +
|
||||
@ -1665,9 +1668,9 @@ BrowserGlue.prototype = {
|
||||
"&excludeQueries=1"),
|
||||
parent: PlacesUtils.bookmarksMenuFolderId,
|
||||
position: menuIndex++,
|
||||
newInVersion: 5
|
||||
},
|
||||
};
|
||||
newInVersion: 6
|
||||
};
|
||||
}
|
||||
|
||||
// Set current itemId, parent and position if Smart Bookmark exists,
|
||||
// we will use these informations to create the new version at the same
|
||||
@ -2030,21 +2033,13 @@ ContentPermissionPrompt.prototype = {
|
||||
|
||||
prompt: function CPP_prompt(request) {
|
||||
|
||||
// Only allow exactly one permission rquest here.
|
||||
let types = request.types.QueryInterface(Ci.nsIArray);
|
||||
if (types.length != 1) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
|
||||
|
||||
const kFeatureKeys = { "geolocation" : "geo",
|
||||
"desktop-notification" : "desktop-notification",
|
||||
"pointerLock" : "pointerLock",
|
||||
};
|
||||
|
||||
// Make sure that we support the request.
|
||||
if (!(perm.type in kFeatureKeys)) {
|
||||
if (!(request.type in kFeatureKeys)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2056,7 +2051,7 @@ ContentPermissionPrompt.prototype = {
|
||||
return;
|
||||
|
||||
var autoAllow = false;
|
||||
var permissionKey = kFeatureKeys[perm.type];
|
||||
var permissionKey = kFeatureKeys[request.type];
|
||||
var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey);
|
||||
|
||||
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
@ -2067,7 +2062,7 @@ ContentPermissionPrompt.prototype = {
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
autoAllow = true;
|
||||
// For pointerLock, we still want to show a warning prompt.
|
||||
if (perm.type != "pointerLock") {
|
||||
if (request.type != "pointerLock") {
|
||||
request.allow();
|
||||
return;
|
||||
}
|
||||
@ -2081,7 +2076,7 @@ ContentPermissionPrompt.prototype = {
|
||||
return;
|
||||
|
||||
// Show the prompt.
|
||||
switch (perm.type) {
|
||||
switch (request.type) {
|
||||
case "geolocation":
|
||||
this._promptGeo(request);
|
||||
break;
|
||||
|
@ -63,9 +63,10 @@ let (XULAppInfo = {
|
||||
}
|
||||
|
||||
// Smart bookmarks constants.
|
||||
const SMART_BOOKMARKS_VERSION = 5;
|
||||
let isWin8OrHigher = Services.sysinfo.getProperty("hasWindowsTouchInterface");
|
||||
const SMART_BOOKMARKS_VERSION = 6
|
||||
const SMART_BOOKMARKS_ON_TOOLBAR = 1;
|
||||
const SMART_BOOKMARKS_ON_MENU = 4; // Takes in count the additional separator.
|
||||
const SMART_BOOKMARKS_ON_MENU = isWin8OrHigher ? 4 : 3; // Takes in count the additional separator.
|
||||
|
||||
// Default bookmarks constants.
|
||||
const DEFAULT_BOOKMARKS_ON_TOOLBAR = 1;
|
||||
|
@ -78,6 +78,11 @@ const TAB_EVENTS = [
|
||||
"TabUnpinned"
|
||||
];
|
||||
|
||||
// Browser events observed.
|
||||
const BROWSER_EVENTS = [
|
||||
"load", "SwapDocShells", "UserTypedValueChanged"
|
||||
];
|
||||
|
||||
// The number of milliseconds in a day
|
||||
const MS_PER_DAY = 1000.0 * 60.0 * 60.0 * 24.0;
|
||||
|
||||
@ -1243,9 +1248,7 @@ let SessionStoreInternal = {
|
||||
*/
|
||||
onTabAdd: function ssi_onTabAdd(aWindow, aTab, aNoNotification) {
|
||||
let browser = aTab.linkedBrowser;
|
||||
browser.addEventListener("load", this, true);
|
||||
browser.addEventListener("SwapDocShells", this, true);
|
||||
browser.addEventListener("UserTypedValueChanged", this, true);
|
||||
BROWSER_EVENTS.forEach(msg => browser.addEventListener(msg, this, true));
|
||||
|
||||
let mm = browser.messageManager;
|
||||
MESSAGES.forEach(msg => mm.addMessageListener(msg, this));
|
||||
@ -1271,9 +1274,7 @@ let SessionStoreInternal = {
|
||||
*/
|
||||
onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
|
||||
let browser = aTab.linkedBrowser;
|
||||
browser.removeEventListener("load", this, true);
|
||||
browser.removeEventListener("SwapDocShells", this, true);
|
||||
browser.removeEventListener("UserTypedValueChanged", this, true);
|
||||
BROWSER_EVENTS.forEach(msg => browser.removeEventListener(msg, this, true));
|
||||
|
||||
let mm = browser.messageManager;
|
||||
MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
|
||||
|
@ -237,8 +237,20 @@ Toolbox.prototype = {
|
||||
}, true);
|
||||
},
|
||||
|
||||
_isResponsiveModeActive: function() {
|
||||
let responsiveModeActive = false;
|
||||
if (this.target.isLocalTab) {
|
||||
let tab = this.target.tab;
|
||||
let browserWindow = tab.ownerDocument.defaultView;
|
||||
let responsiveUIManager = browserWindow.ResponsiveUI.ResponsiveUIManager;
|
||||
responsiveModeActive = responsiveUIManager.isActiveForTab(tab);
|
||||
}
|
||||
return responsiveModeActive;
|
||||
},
|
||||
|
||||
_splitConsoleOnKeypress: function(e) {
|
||||
if (e.keyCode === e.DOM_VK_ESCAPE) {
|
||||
let responsiveModeActive = this._isResponsiveModeActive();
|
||||
if (e.keyCode === e.DOM_VK_ESCAPE && !responsiveModeActive) {
|
||||
this.toggleSplitConsole();
|
||||
}
|
||||
},
|
||||
|
@ -176,7 +176,7 @@ function ResponsiveUI(aWindow, aTab)
|
||||
// Events
|
||||
this.tab.addEventListener("TabClose", this);
|
||||
this.tabContainer.addEventListener("TabSelect", this);
|
||||
this.mainWindow.document.addEventListener("keypress", this.bound_onKeypress, true);
|
||||
this.mainWindow.document.addEventListener("keypress", this.bound_onKeypress, false);
|
||||
|
||||
this.buildUI();
|
||||
this.checkMenus();
|
||||
@ -276,7 +276,7 @@ ResponsiveUI.prototype = {
|
||||
this.stopResizing();
|
||||
|
||||
// Remove listeners.
|
||||
this.mainWindow.document.removeEventListener("keypress", this.bound_onKeypress, true);
|
||||
this.mainWindow.document.removeEventListener("keypress", this.bound_onKeypress, false);
|
||||
this.menulist.removeEventListener("select", this.bound_presetSelected, true);
|
||||
this.tab.removeEventListener("TabClose", this);
|
||||
this.tabContainer.removeEventListener("TabSelect", this);
|
||||
|
@ -6,6 +6,7 @@ function test() {
|
||||
|
||||
let ruleView;
|
||||
let inspector;
|
||||
let mgr = ResponsiveUI.ResponsiveUIManager;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
@ -78,14 +79,22 @@ function test() {
|
||||
ruleView.element.addEventListener("CssRuleViewRefreshed", function refresh() {
|
||||
ruleView.element.removeEventListener("CssRuleViewRefreshed", refresh, false);
|
||||
is(numberOfRules(), 2, "Should have two rules after growing.");
|
||||
finishUp();
|
||||
testEscapeCloses();
|
||||
}, false);
|
||||
|
||||
instance.setSize(500, 500);
|
||||
}
|
||||
|
||||
function testEscapeCloses() {
|
||||
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "true", "menu checked");
|
||||
ok(!inspector._toolbox._splitConsole, "Console is not split.");
|
||||
|
||||
mgr.once("off", function() {executeSoon(finishUp)});
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
ok(!inspector._toolbox._splitConsole, "Console is still not split after pressing escape.");
|
||||
|
||||
// Menus are correctly updated?
|
||||
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
|
||||
|
@ -44,8 +44,6 @@ let IndexedDB = {
|
||||
}
|
||||
|
||||
let prompt = Cc["@mozilla.org/content-permission/prompt;1"].createInstance(Ci.nsIContentPermissionPrompt);
|
||||
let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
types.appendElement({type: type, access: "unused"}, false);
|
||||
|
||||
// If the user waits a long time before responding, we default to UNKNOWN_ACTION.
|
||||
let timeoutId = setTimeout(function() {
|
||||
@ -62,7 +60,7 @@ let IndexedDB = {
|
||||
}
|
||||
|
||||
prompt.prompt({
|
||||
types: types,
|
||||
type: type,
|
||||
uri: Services.io.newURI(payload.location, null, null),
|
||||
window: null,
|
||||
element: aMessage.target,
|
||||
|
@ -56,8 +56,8 @@ ContentPermissionPrompt.prototype = {
|
||||
return chromeWin.Browser.getNotificationBox(request.element);
|
||||
},
|
||||
|
||||
handleExistingPermission: function handleExistingPermission(request, type) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
|
||||
handleExistingPermission: function handleExistingPermission(request) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type);
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
return true;
|
||||
@ -70,28 +70,20 @@ ContentPermissionPrompt.prototype = {
|
||||
},
|
||||
|
||||
prompt: function(request) {
|
||||
// Only allow exactly one permission rquest here.
|
||||
let types = request.types.QueryInterface(Ci.nsIArray);
|
||||
if (types.length != 1) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
|
||||
|
||||
// returns true if the request was handled
|
||||
if (this.handleExistingPermission(request, perm.type))
|
||||
if (this.handleExistingPermission(request))
|
||||
return;
|
||||
|
||||
let pm = Services.perms;
|
||||
let notificationBox = this.getNotificationBoxForRequest(request);
|
||||
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
let notification = notificationBox.getNotificationWithValue(perm.type);
|
||||
let notification = notificationBox.getNotificationWithValue(request.type);
|
||||
if (notification)
|
||||
return;
|
||||
|
||||
let entityName = kEntities[perm.type];
|
||||
let icon = kIcons[perm.type] || "";
|
||||
let entityName = kEntities[request.type];
|
||||
let icon = kIcons[request.type] || "";
|
||||
|
||||
let buttons = [{
|
||||
label: browserBundle.GetStringFromName(entityName + ".allow"),
|
||||
@ -104,7 +96,7 @@ ContentPermissionPrompt.prototype = {
|
||||
label: browserBundle.GetStringFromName("contentPermissions.alwaysForSite"),
|
||||
accessKey: "",
|
||||
callback: function(notification) {
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
request.allow();
|
||||
}
|
||||
},
|
||||
@ -112,7 +104,7 @@ ContentPermissionPrompt.prototype = {
|
||||
label: browserBundle.GetStringFromName("contentPermissions.neverForSite"),
|
||||
accessKey: "",
|
||||
callback: function(notification) {
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
request.cancel();
|
||||
}
|
||||
}];
|
||||
@ -120,12 +112,12 @@ ContentPermissionPrompt.prototype = {
|
||||
let message = browserBundle.formatStringFromName(entityName + ".wantsTo",
|
||||
[request.principal.URI.host], 1);
|
||||
let newBar = notificationBox.appendNotification(message,
|
||||
perm.type,
|
||||
request.type,
|
||||
icon,
|
||||
notificationBox.PRIORITY_WARNING_MEDIUM,
|
||||
buttons);
|
||||
|
||||
if (perm.type == "geolocation") {
|
||||
if (request.type == "geolocation") {
|
||||
// Add the "learn more" link.
|
||||
let link = newBar.ownerDocument.createElement("label");
|
||||
link.setAttribute("value", browserBundle.GetStringFromName("geolocation.learnMore"));
|
||||
|
@ -1,9 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
browser_taskbar_preview.js \
|
||||
$(NULL)
|
||||
endif
|
@ -4,3 +4,5 @@
|
||||
[browser_SignInToWebsite.js]
|
||||
[browser_UITour.js]
|
||||
support-files = uitour.*
|
||||
[browser_taskbar_preview.js]
|
||||
run-if = os == "win"
|
||||
|
@ -104,17 +104,6 @@ toolbarpaletteitem[place="toolbar"] {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
#customization-palette > #wrapper-edit-controls,
|
||||
#customization-palette > #wrapper-zoom-controls {
|
||||
width: 225px;
|
||||
}
|
||||
|
||||
#wrapper-edit-controls[place="palette"] > .toolbarpaletteitem-box,
|
||||
#wrapper-zoom-controls[place="palette"] > .toolbarpaletteitem-box {
|
||||
width: 225px;
|
||||
max-width: 225px;
|
||||
}
|
||||
|
||||
#wrapper-edit-controls[place="palette"] > #edit-controls > toolbarbutton,
|
||||
#wrapper-edit-controls[place="palette"] > #edit-controls > separator,
|
||||
#wrapper-zoom-controls[place="palette"] > #zoom-controls > toolbarbutton,
|
||||
@ -126,21 +115,25 @@ toolbarpaletteitem[place="toolbar"] {
|
||||
#wrapper-zoom-controls[place="palette"] > #zoom-controls > toolbarbutton {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
max-width: 70px;
|
||||
min-width: 70px;
|
||||
max-width: 24px;
|
||||
min-width: 24px;
|
||||
max-height: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#wrapper-edit-controls[place="palette"] > #edit-controls > toolbarbutton > .toolbarbutton-text,
|
||||
#wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button > .toolbarbutton-text {
|
||||
display: inline;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#wrapper-edit-controls[place="palette"] > #edit-controls > toolbarbutton > .toolbarbutton-icon,
|
||||
#wrapper-zoom-controls[place="palette"] > #zoom-controls > toolbarbutton > .toolbarbutton-icon {
|
||||
margin: 0;
|
||||
-moz-margin-start: 5px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#wrapper-edit-controls > #edit-controls > toolbarbutton > .toolbarbutton-icon {
|
||||
opacity: 1; /* To ensure these buttons always look enabled in customize mode */
|
||||
}
|
||||
|
||||
#wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button,
|
||||
#wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button + separator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#customization-palette > toolbarpaletteitem > label {
|
||||
|
@ -12,11 +12,11 @@ public interface Assert {
|
||||
void endTest();
|
||||
|
||||
void ok(boolean condition, String name, String diag);
|
||||
void is(Object a, Object b, String name);
|
||||
void isnot(Object a, Object b, String name);
|
||||
void is(Object actual, Object expected, String name);
|
||||
void isnot(Object actual, Object notExpected, String name);
|
||||
void todo(boolean condition, String name, String diag);
|
||||
void todo_is(Object a, Object b, String name);
|
||||
void todo_isnot(Object a, Object b, String name);
|
||||
void todo_is(Object actual, Object expected, String name);
|
||||
void todo_isnot(Object actual, Object notExpected, String name);
|
||||
void info(String name, String message);
|
||||
|
||||
// robocop-specific asserts
|
||||
|
@ -15,7 +15,7 @@ public class FennecMochitestAssert implements Assert {
|
||||
private int mPassed = 0;
|
||||
private int mFailed = 0;
|
||||
private int mTodo = 0;
|
||||
|
||||
|
||||
// Used to write the first line of the test file
|
||||
private boolean mLogStarted = false;
|
||||
|
||||
@ -140,14 +140,14 @@ public class FennecMochitestAssert implements Assert {
|
||||
mTestList.add(test);
|
||||
}
|
||||
|
||||
public void is(Object a, Object b, String name) {
|
||||
boolean pass = checkObjectsEqual(a,b);
|
||||
ok(pass, name, getEqualString(a,b, pass));
|
||||
public void is(Object actual, Object expected, String name) {
|
||||
boolean pass = checkObjectsEqual(actual, expected);
|
||||
ok(pass, name, getEqualString(actual, expected, pass));
|
||||
}
|
||||
|
||||
public void isnot(Object a, Object b, String name) {
|
||||
boolean pass = checkObjectsNotEqual(a,b);
|
||||
ok(pass, name, getNotEqualString(a,b,pass));
|
||||
|
||||
public void isnot(Object actual, Object notExpected, String name) {
|
||||
boolean pass = checkObjectsNotEqual(actual, notExpected);
|
||||
ok(pass, name, getNotEqualString(actual, notExpected, pass));
|
||||
}
|
||||
|
||||
public void ispixel(int actual, int r, int g, int b, String name) {
|
||||
@ -197,14 +197,14 @@ public class FennecMochitestAssert implements Assert {
|
||||
mTestList.add(test);
|
||||
}
|
||||
|
||||
public void todo_is(Object a, Object b, String name) {
|
||||
boolean pass = checkObjectsEqual(a,b);
|
||||
todo(pass, name, getEqualString(a,b,pass));
|
||||
public void todo_is(Object actual, Object expected, String name) {
|
||||
boolean pass = checkObjectsEqual(actual, expected);
|
||||
todo(pass, name, getEqualString(actual, expected, pass));
|
||||
}
|
||||
|
||||
public void todo_isnot(Object a, Object b, String name) {
|
||||
boolean pass = checkObjectsNotEqual(a,b);
|
||||
todo(pass, name, getNotEqualString(a,b,pass));
|
||||
public void todo_isnot(Object actual, Object notExpected, String name) {
|
||||
boolean pass = checkObjectsNotEqual(actual, notExpected);
|
||||
todo(pass, name, getNotEqualString(actual, notExpected, pass));
|
||||
}
|
||||
|
||||
private boolean checkObjectsEqual(Object a, Object b) {
|
||||
|
@ -6,7 +6,7 @@ package org.mozilla.gecko;
|
||||
|
||||
|
||||
public class FennecTalosAssert implements Assert {
|
||||
|
||||
|
||||
public FennecTalosAssert() { }
|
||||
|
||||
/**
|
||||
@ -38,14 +38,14 @@ public class FennecTalosAssert implements Assert {
|
||||
}
|
||||
}
|
||||
|
||||
public void is(Object a, Object b, String name) {
|
||||
boolean pass = (a == null ? b == null : a.equals(b));
|
||||
ok(pass, name, "got " + a + ", expected " + b);
|
||||
public void is(Object actual, Object expected, String name) {
|
||||
boolean pass = (actual == null ? expected == null : actual.equals(expected));
|
||||
ok(pass, name, "got " + actual + ", expected " + expected);
|
||||
}
|
||||
|
||||
public void isnot(Object a, Object b, String name) {
|
||||
boolean fail = (a == null ? b == null : a.equals(b));
|
||||
ok(!fail, name, "got " + a + ", expected not " + b);
|
||||
|
||||
public void isnot(Object actual, Object notExpected, String name) {
|
||||
boolean fail = (actual == null ? notExpected == null : actual.equals(notExpected));
|
||||
ok(!fail, name, "got " + actual + ", expected not " + notExpected);
|
||||
}
|
||||
|
||||
public void ispixel(int actual, int r, int g, int b, String name) {
|
||||
@ -60,11 +60,11 @@ public class FennecTalosAssert implements Assert {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void todo_is(Object a, Object b, String name) {
|
||||
public void todo_is(Object actual, Object expected, String name) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void todo_isnot(Object a, Object b, String name) {
|
||||
public void todo_isnot(Object actual, Object notExpected, String name) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ dir-tests := $(DEPTH)/$(mobile-tests)
|
||||
ANDROID_APK_NAME := robocop-debug
|
||||
|
||||
ANDROID_EXTRA_JARS += \
|
||||
$(srcdir)/robotium-solo-4.3.jar \
|
||||
$(srcdir)/robotium-solo-4.3.1.jar \
|
||||
$(NULL)
|
||||
|
||||
ANDROID_ASSETS_DIR := $(TESTPATH)/assets
|
||||
|
@ -4,7 +4,7 @@ Robotium is an open source tool licensed under the Apache 2.0 license and the or
|
||||
source can be found here:
|
||||
http://code.google.com/p/robotium/
|
||||
|
||||
We are including robotium-solo-4.3.jar as a binary and are not modifying it in any way
|
||||
We are including robotium-solo-4.3.1.jar as a binary and are not modifying it in any way
|
||||
from the original download found at:
|
||||
http://code.google.com/p/robotium/
|
||||
|
||||
|
Binary file not shown.
@ -217,8 +217,6 @@
|
||||
#include "mozilla/dom/XPathEvaluator.h"
|
||||
#include "nsIDocumentEncoder.h"
|
||||
#include "nsIStructuredCloneContainer.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -2645,6 +2643,33 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
#endif
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// If Document is an app check to see if we already set CSP and return early
|
||||
// if that is indeed the case.
|
||||
//
|
||||
// In general (see bug 947831), we should not be setting CSP on a principal
|
||||
// that aliases another document. For non-app code this is not a problem
|
||||
// since we only share the underlying principal with nested browsing
|
||||
// contexts for which a header cannot be set (e.g., about:blank and
|
||||
// about:srcodoc iframes) and thus won't try to set the CSP again. This
|
||||
// check ensures that we do not try to set CSP for an app.
|
||||
if (applyAppDefaultCSP || applyAppManifestCSP) {
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->GetCsp(getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (csp) {
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s",
|
||||
"This document is sharing principal with another document.",
|
||||
"Since the document is an app, CSP was already set.",
|
||||
"Skipping attempt to set CSP."));
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// create new CSP object
|
||||
csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
@ -2724,16 +2749,12 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
}
|
||||
}
|
||||
|
||||
if (csp) {
|
||||
// Copy into principal
|
||||
nsIPrincipal* principal = GetPrincipal();
|
||||
rv = principal->SetCsp(csp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = principal->SetCsp(csp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Inserted CSP into principal %p", principal));
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Inserted CSP into principal %p", principal));
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -10731,11 +10752,17 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest,
|
||||
nsIContentPermissionRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
nsPointerLockPermissionRequest::GetType(nsACString& aType)
|
||||
{
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
aType = "pointerLock";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -5,7 +5,6 @@
|
||||
#ifndef MEDIAENGINE_H_
|
||||
#define MEDIAENGINE_H_
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "DOMMediaStream.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
@ -36,7 +35,7 @@ enum {
|
||||
kAudioTrack = 2
|
||||
};
|
||||
|
||||
class MediaEngine : public RefCounted<MediaEngine>
|
||||
class MediaEngine
|
||||
{
|
||||
public:
|
||||
virtual ~MediaEngine() {}
|
||||
|
@ -101,7 +101,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSourc
|
||||
// We've already seen this device, just append.
|
||||
aVSources->AppendElement(vSource.get());
|
||||
} else {
|
||||
vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i);
|
||||
vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i, mWindowId);
|
||||
mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
|
||||
aVSources->AppendElement(vSource);
|
||||
}
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "ImageContainer.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "prprf.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#endif
|
||||
|
||||
#include "NullTransport.h"
|
||||
@ -74,7 +73,7 @@ class GetCameraNameRunnable;
|
||||
* mSources, mImageContainer, mSources, mState, mImage, mLastCapture
|
||||
*
|
||||
* MainThread:
|
||||
* mDOMCameraControl, mCaptureIndex, mCameraThread, mCameraManager,
|
||||
* mDOMCameraControl, mCaptureIndex, mCameraThread, mWindowId, mCameraManager,
|
||||
* mNativeCameraControl, mPreviewStream, mState, mLastCapture, mWidth, mHeight
|
||||
*
|
||||
* Where mWidth, mHeight, mImage are protected by mMonitor
|
||||
@ -97,10 +96,11 @@ class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource
|
||||
public:
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
MediaEngineWebRTCVideoSource(nsDOMCameraManager* aCameraManager,
|
||||
int aIndex)
|
||||
int aIndex, uint64_t aWindowId)
|
||||
: mCameraManager(aCameraManager)
|
||||
, mNativeCameraControl(nullptr)
|
||||
, mPreviewStream(nullptr)
|
||||
, mWindowId(aWindowId)
|
||||
, mCallbackMonitor("WebRTCCamera.CallbackMonitor")
|
||||
, mCaptureIndex(aIndex)
|
||||
, mMonitor("WebRTCCamera.Monitor")
|
||||
@ -223,6 +223,7 @@ private:
|
||||
nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
|
||||
nsRefPtr<nsGonkCameraControl> mNativeCameraControl;
|
||||
nsRefPtr<DOMCameraPreview> mPreviewStream;
|
||||
uint64_t mWindowId;
|
||||
mozilla::ReentrantMonitor mCallbackMonitor; // Monitor for camera callback handling
|
||||
nsRefPtr<nsIThread> mCameraThread;
|
||||
nsRefPtr<nsIDOMFile> mLastCapture;
|
||||
@ -351,14 +352,15 @@ class MediaEngineWebRTC : public MediaEngine
|
||||
{
|
||||
public:
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
MediaEngineWebRTC(nsDOMCameraManager* aCameraManager)
|
||||
MediaEngineWebRTC(nsDOMCameraManager* aCameraManager, uint64_t aWindowId)
|
||||
: mMutex("mozilla::MediaEngineWebRTC")
|
||||
, mVideoEngine(nullptr)
|
||||
, mVoiceEngine(nullptr)
|
||||
, mVideoEngineInit(false)
|
||||
, mAudioEngineInit(false)
|
||||
, mHasTabVideoSource(false)
|
||||
, mCameraManager(aCameraManager)
|
||||
, mWindowId(aWindowId)
|
||||
, mHasTabVideoSource(false)
|
||||
{
|
||||
AsyncLatencyLogger::Get(true)->AddRef();
|
||||
mLoadMonitor = new LoadMonitor();
|
||||
@ -399,8 +401,6 @@ private:
|
||||
nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
|
||||
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
// XXX Should use nsMainThreadPtrHandle/etc
|
||||
|
||||
// MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
|
||||
// Their life time is always much longer than this object. Use a raw-pointer
|
||||
// here should be safe.
|
||||
@ -409,6 +409,7 @@ private:
|
||||
// avoid any bad thing do to addref/release DOM-object on other thread, we use
|
||||
// raw-pointer for now.
|
||||
nsDOMCameraManager* mCameraManager;
|
||||
uint64_t mWindowId;
|
||||
#endif
|
||||
|
||||
nsRefPtr<LoadMonitor> mLoadMonitor;
|
||||
|
@ -312,6 +312,7 @@ nsresult
|
||||
MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
|
||||
{
|
||||
LOG((__FUNCTION__));
|
||||
int error = 0;
|
||||
if (!mInitDone || !aStream) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -340,7 +341,7 @@ MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
|
||||
}
|
||||
#else
|
||||
mState = kStarted;
|
||||
int error = mViERender->AddRenderer(mCaptureIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
|
||||
error = mViERender->AddRenderer(mCaptureIndex, webrtc::kVideoI420, (webrtc::ExternalRenderer*)this);
|
||||
if (error == -1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -491,9 +492,12 @@ void
|
||||
MediaEngineWebRTCVideoSource::AllocImpl() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ErrorResult rv;
|
||||
mDOMCameraControl = mCameraManager->GetCameraControl(mCaptureIndex,
|
||||
this, this, rv);
|
||||
mDOMCameraControl = new nsDOMCameraControl(mCaptureIndex,
|
||||
mCameraThread,
|
||||
this,
|
||||
this,
|
||||
nsGlobalWindow::GetInnerWindowWithId(mWindowId));
|
||||
mCameraManager->Register(mDOMCameraControl);
|
||||
}
|
||||
|
||||
void
|
||||
@ -502,7 +506,6 @@ MediaEngineWebRTCVideoSource::DeallocImpl() {
|
||||
|
||||
mNativeCameraControl->ReleaseHardware(this, this);
|
||||
mNativeCameraControl = nullptr;
|
||||
mDOMCameraControl = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -323,11 +323,6 @@ this.PermissionsTable = { geolocation: {
|
||||
privileged: DENY_ACTION,
|
||||
certified: ALLOW_ACTION
|
||||
},
|
||||
"video-capture": {
|
||||
app: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: PROMPT_ACTION
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -6,156 +6,20 @@
|
||||
#include "GonkPermission.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#endif // MOZ_WIDGET_GONK
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/PContentPermission.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
using mozilla::unused; // <snicker>
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ContentPermissionRequestParent : public PContentPermissionRequestParent
|
||||
{
|
||||
public:
|
||||
ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
|
||||
Element* element,
|
||||
const IPC::Principal& principal);
|
||||
virtual ~ContentPermissionRequestParent();
|
||||
|
||||
bool IsBeingDestroyed();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<Element> mElement;
|
||||
nsCOMPtr<nsContentPermissionRequestProxy> mProxy;
|
||||
nsTArray<PermissionRequest> mRequests;
|
||||
|
||||
private:
|
||||
virtual bool Recvprompt();
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
};
|
||||
|
||||
ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
|
||||
Element* aElement,
|
||||
const IPC::Principal& aPrincipal)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
|
||||
|
||||
mPrincipal = aPrincipal;
|
||||
mElement = aElement;
|
||||
mRequests = aRequests;
|
||||
}
|
||||
|
||||
ContentPermissionRequestParent::~ContentPermissionRequestParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ContentPermissionRequestParent);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentPermissionRequestParent::Recvprompt()
|
||||
{
|
||||
mProxy = new nsContentPermissionRequestProxy();
|
||||
NS_ASSERTION(mProxy, "Alloc of request proxy failed");
|
||||
if (NS_FAILED(mProxy->Init(mRequests, this))) {
|
||||
mProxy->Cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (mProxy) {
|
||||
mProxy->OnParentDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ContentPermissionRequestParent::IsBeingDestroyed()
|
||||
{
|
||||
// When TabParent::Destroy() is called, we are being destroyed. It's unsafe
|
||||
// to send out any message now.
|
||||
TabParent* tabParent = static_cast<TabParent*>(Manager());
|
||||
return tabParent->IsDestroyed();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ContentPermissionType, nsIContentPermissionType)
|
||||
|
||||
ContentPermissionType::ContentPermissionType(const nsACString& aType,
|
||||
const nsACString& aAccess)
|
||||
{
|
||||
mType = aType;
|
||||
mAccess = aAccess;
|
||||
}
|
||||
|
||||
ContentPermissionType::~ContentPermissionType()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentPermissionType::GetType(nsACString& aType)
|
||||
{
|
||||
aType = mType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentPermissionType::GetAccess(nsACString& aAccess)
|
||||
{
|
||||
aAccess = mAccess;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray,
|
||||
nsIMutableArray* aDesArray)
|
||||
{
|
||||
uint32_t len = aSrcArray.Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
nsRefPtr<ContentPermissionType> cpt =
|
||||
new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].access());
|
||||
aDesArray->AppendElement(cpt, false);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CreatePermissionArray(const nsACString& aType,
|
||||
const nsACString& aAccess,
|
||||
nsIArray** aTypesArray)
|
||||
{
|
||||
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
nsRefPtr<ContentPermissionType> permType = new ContentPermissionType(aType,
|
||||
aAccess);
|
||||
types->AppendElement(permType, false);
|
||||
types.forget(aTypesArray);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PContentPermissionRequestParent*
|
||||
CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
|
||||
Element* element,
|
||||
const IPC::Principal& principal)
|
||||
{
|
||||
return new ContentPermissionRequestParent(aRequests, element, principal);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
nsContentPermissionRequestProxy::nsContentPermissionRequestProxy()
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsContentPermissionRequestProxy);
|
||||
@ -167,12 +31,14 @@ nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests,
|
||||
nsContentPermissionRequestProxy::Init(const nsACString & type,
|
||||
const nsACString & access,
|
||||
ContentPermissionRequestParent* parent)
|
||||
{
|
||||
NS_ASSERTION(parent, "null parent");
|
||||
mParent = parent;
|
||||
mPermissionRequests = requests;
|
||||
mType = type;
|
||||
mAccess = access;
|
||||
|
||||
nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
|
||||
if (!prompt) {
|
||||
@ -192,14 +58,17 @@ nsContentPermissionRequestProxy::OnParentDestroyed()
|
||||
NS_IMPL_ISUPPORTS1(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes)
|
||||
nsContentPermissionRequestProxy::GetType(nsACString & aType)
|
||||
{
|
||||
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (ConvertPermissionRequestToArray(mPermissionRequests, types)) {
|
||||
types.forget(aTypes);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
aType = mType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = mAccess;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -267,18 +136,10 @@ nsContentPermissionRequestProxy::Allow()
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
uint32_t len = mPermissionRequests.Length();
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
if (mPermissionRequests[i].type().Equals("audio-capture")) {
|
||||
GonkPermissionService::GetInstance()->addGrantInfo(
|
||||
"android.permission.RECORD_AUDIO",
|
||||
static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
|
||||
}
|
||||
if (mPermissionRequests[i].type().Equals("video-capture")) {
|
||||
GonkPermissionService::GetInstance()->addGrantInfo(
|
||||
"android.permission.CAMERA",
|
||||
static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
|
||||
}
|
||||
if (mType.Equals("audio-capture")) {
|
||||
GonkPermissionService::GetInstance()->addGrantInfo(
|
||||
"android.permission.RECORD_AUDIO",
|
||||
static_cast<TabParent*>(mParent->Manager())->Manager()->Pid());
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -286,3 +147,55 @@ nsContentPermissionRequestProxy::Allow()
|
||||
mParent = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
ContentPermissionRequestParent::ContentPermissionRequestParent(const nsACString& aType,
|
||||
const nsACString& aAccess,
|
||||
Element* aElement,
|
||||
const IPC::Principal& aPrincipal)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
|
||||
|
||||
mPrincipal = aPrincipal;
|
||||
mElement = aElement;
|
||||
mType = aType;
|
||||
mAccess = aAccess;
|
||||
}
|
||||
|
||||
ContentPermissionRequestParent::~ContentPermissionRequestParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ContentPermissionRequestParent);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentPermissionRequestParent::Recvprompt()
|
||||
{
|
||||
mProxy = new nsContentPermissionRequestProxy();
|
||||
NS_ASSERTION(mProxy, "Alloc of request proxy failed");
|
||||
if (NS_FAILED(mProxy->Init(mType, mAccess, this))) {
|
||||
mProxy->Cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (mProxy) {
|
||||
mProxy->OnParentDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ContentPermissionRequestParent::IsBeingDestroyed()
|
||||
{
|
||||
// When TabParent::Destroy() is called, we are being destroyed. It's unsafe
|
||||
// to send out any message now.
|
||||
TabParent* tabParent = static_cast<TabParent*>(Manager());
|
||||
return tabParent->IsDestroyed();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -6,75 +6,60 @@
|
||||
#define nsContentPermissionHelper_h
|
||||
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
|
||||
class nsContentPermissionRequestProxy;
|
||||
|
||||
// Forward declare IPC::Principal here which is defined in
|
||||
// PermissionMessageUtils.h. Include this file will transitively includes
|
||||
// "windows.h" and it defines
|
||||
// #define CreateEvent CreateEventW
|
||||
// #define LoadImage LoadImageW
|
||||
// That will mess up windows build.
|
||||
namespace IPC {
|
||||
class Principal;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Element;
|
||||
class PermissionRequest;
|
||||
class ContentPermissionRequestParent;
|
||||
class PContentPermissionRequestParent;
|
||||
|
||||
class ContentPermissionType : public nsIContentPermissionType
|
||||
class ContentPermissionRequestParent : public PContentPermissionRequestParent
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONTYPE
|
||||
public:
|
||||
ContentPermissionRequestParent(const nsACString& type,
|
||||
const nsACString& access,
|
||||
Element* element,
|
||||
const IPC::Principal& principal);
|
||||
virtual ~ContentPermissionRequestParent();
|
||||
|
||||
ContentPermissionType(const nsACString& aType, const nsACString& aAccess);
|
||||
virtual ~ContentPermissionType();
|
||||
bool IsBeingDestroyed();
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<Element> mElement;
|
||||
nsCOMPtr<nsContentPermissionRequestProxy> mProxy;
|
||||
nsCString mType;
|
||||
nsCString mAccess;
|
||||
|
||||
private:
|
||||
virtual bool Recvprompt();
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
};
|
||||
|
||||
uint32_t ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray,
|
||||
nsIMutableArray* aDesArray);
|
||||
|
||||
nsresult CreatePermissionArray(const nsACString& aType,
|
||||
const nsACString& aAccess,
|
||||
nsIArray** aTypesArray);
|
||||
|
||||
PContentPermissionRequestParent*
|
||||
CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
|
||||
Element* element,
|
||||
const IPC::Principal& principal);
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsContentPermissionRequestProxy : public nsIContentPermissionRequest
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
|
||||
nsContentPermissionRequestProxy();
|
||||
virtual ~nsContentPermissionRequestProxy();
|
||||
|
||||
nsresult Init(const nsTArray<mozilla::dom::PermissionRequest>& requests,
|
||||
mozilla::dom::ContentPermissionRequestParent* parent);
|
||||
nsresult Init(const nsACString& type, const nsACString& access, mozilla::dom::ContentPermissionRequestParent* parent);
|
||||
void OnParentDestroyed();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONREQUEST
|
||||
|
||||
private:
|
||||
// Non-owning pointer to the ContentPermissionRequestParent object which owns this proxy.
|
||||
mozilla::dom::ContentPermissionRequestParent* mParent;
|
||||
nsTArray<mozilla::dom::PermissionRequest> mPermissionRequests;
|
||||
nsCString mType;
|
||||
nsCString mAccess;
|
||||
};
|
||||
|
||||
#endif // nsContentPermissionHelper_h
|
||||
|
||||
|
@ -105,31 +105,6 @@ nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow)
|
||||
return cameraManager.forget();
|
||||
}
|
||||
|
||||
nsDOMCameraControl*
|
||||
nsDOMCameraManager::GetCameraControl(uint32_t aDeviceNum,
|
||||
nsICameraGetCameraCallback* onSuccess,
|
||||
nsICameraErrorCallback* onError,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
aRv = NS_OK;
|
||||
|
||||
// reuse the same camera thread to conserve resources
|
||||
if (!mCameraThread) {
|
||||
aRv = NS_NewThread(getter_AddRefs(mCameraThread));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Creating this object will trigger the onSuccess handler
|
||||
nsDOMCameraControl* cameraControl = new nsDOMCameraControl(aDeviceNum, mCameraThread,
|
||||
onSuccess, onError, mWindow);
|
||||
if (cameraControl) {
|
||||
Register(cameraControl);
|
||||
}
|
||||
return cameraControl;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraManager::GetCamera(const CameraSelector& aOptions,
|
||||
nsICameraGetCameraCallback* onSuccess,
|
||||
@ -141,10 +116,22 @@ nsDOMCameraManager::GetCamera(const CameraSelector& aOptions,
|
||||
cameraId = 1;
|
||||
}
|
||||
|
||||
// reuse the same camera thread to conserve resources
|
||||
if (!mCameraThread) {
|
||||
aRv = NS_NewThread(getter_AddRefs(mCameraThread));
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
// Creating this object will trigger the onSuccess handler
|
||||
nsRefPtr<nsDOMCameraControl> cameraControl =
|
||||
GetCameraControl(cameraId, onSuccess, onError.WasPassed() ? onError.Value() : nullptr, aRv);
|
||||
new nsDOMCameraControl(cameraId, mCameraThread,
|
||||
onSuccess, onError.WasPassed() ? onError.Value() : nullptr, mWindow);
|
||||
|
||||
Register(cameraControl);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -49,11 +49,6 @@ public:
|
||||
CreateInstance(nsPIDOMWindow* aWindow);
|
||||
static bool IsWindowStillActive(uint64_t aWindowId);
|
||||
|
||||
// Build us an nsDOMCameraControl
|
||||
mozilla::nsDOMCameraControl* GetCameraControl(uint32_t aDeviceNum,
|
||||
nsICameraGetCameraCallback* onSuccess,
|
||||
nsICameraErrorCallback* onError,
|
||||
mozilla::ErrorResult& aRv);
|
||||
void Register(mozilla::nsDOMCameraControl* aDOMCameraControl);
|
||||
void OnNavigation(uint64_t aWindowId);
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
#define RE_LOGE(fmt, ...) DOM_CAMERA_LOGE("[%s:%d]" fmt,__FILE__,__LINE__, ## __VA_ARGS__)
|
||||
|
||||
#include <binder/IPCThreadState.h>
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
# include <media/openmax/OMX_Audio.h>
|
||||
#endif
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
@ -683,7 +683,7 @@ status_t GonkRecorder::start() {
|
||||
status = startAMRRecording();
|
||||
break;
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
case OUTPUT_FORMAT_AAC_ADIF:
|
||||
case OUTPUT_FORMAT_AAC_ADTS:
|
||||
status = startAACRecording();
|
||||
@ -735,7 +735,7 @@ sp<MediaSource> GonkRecorder::createAudioSource() {
|
||||
case AUDIO_ENCODER_AMR_WB:
|
||||
mime = MEDIA_MIMETYPE_AUDIO_AMR_WB;
|
||||
break;
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
case AUDIO_ENCODER_AAC:
|
||||
mime = MEDIA_MIMETYPE_AUDIO_AAC;
|
||||
encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectLC);
|
||||
@ -780,7 +780,7 @@ sp<MediaSource> GonkRecorder::createAudioSource() {
|
||||
return audioEncoder;
|
||||
}
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
status_t GonkRecorder::startAACRecording() {
|
||||
// FIXME:
|
||||
// Add support for OUTPUT_FORMAT_AAC_ADIF
|
||||
@ -871,7 +871,7 @@ status_t GonkRecorder::startMPEG2TSRecording() {
|
||||
sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd);
|
||||
|
||||
if (mAudioSource != AUDIO_SOURCE_CNT) {
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
if (mAudioEncoder != AUDIO_ENCODER_AAC &&
|
||||
mAudioEncoder != AUDIO_ENCODER_HE_AAC &&
|
||||
mAudioEncoder != AUDIO_ENCODER_AAC_ELD) {
|
||||
@ -1257,7 +1257,7 @@ status_t GonkRecorder::setupVideoEncoder(
|
||||
|
||||
uint32_t encoder_flags = 0;
|
||||
if (mIsMetaDataStoredInVideoBuffers) {
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
|
||||
#else
|
||||
encoder_flags |= OMXCodec::kHardwareCodecsOnly;
|
||||
@ -1293,7 +1293,7 @@ status_t GonkRecorder::setupAudioEncoder(const sp<MediaWriter>& writer) {
|
||||
switch(mAudioEncoder) {
|
||||
case AUDIO_ENCODER_AMR_NB:
|
||||
case AUDIO_ENCODER_AMR_WB:
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
case AUDIO_ENCODER_AAC:
|
||||
case AUDIO_ENCODER_HE_AAC:
|
||||
case AUDIO_ENCODER_AAC_ELD:
|
||||
|
@ -129,7 +129,7 @@ private:
|
||||
sp<MetaData> *meta);
|
||||
status_t startMPEG4Recording();
|
||||
status_t startAMRRecording();
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
|
||||
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
|
||||
status_t startAACRecording();
|
||||
#endif
|
||||
status_t startRawAudioRecording();
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIDocument.h"
|
||||
#include <algorithm>
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
#include "mozilla/dom/DeviceStorageBinding.h"
|
||||
|
||||
@ -1734,14 +1733,17 @@ nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
|
||||
nsDOMDeviceStorageCursor::GetType(nsACString & aType)
|
||||
{
|
||||
nsCString type;
|
||||
nsresult rv =
|
||||
DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
|
||||
aType);
|
||||
}
|
||||
|
||||
return CreatePermissionArray(type, NS_LITERAL_CSTRING("read"), aTypes);
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorageCursor::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = NS_LITERAL_CSTRING("read");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -2249,10 +2251,8 @@ public:
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(type, access));
|
||||
child->SendPContentPermissionRequestConstructor(
|
||||
this, permArray, IPC::Principal(mPrincipal));
|
||||
this, type, access, IPC::Principal(mPrincipal));
|
||||
|
||||
Sendprompt();
|
||||
return NS_OK;
|
||||
@ -2266,23 +2266,26 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP GetTypes(nsIArray** aTypes)
|
||||
NS_IMETHOD GetType(nsACString & aType)
|
||||
{
|
||||
nsCString type;
|
||||
nsresult rv =
|
||||
DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
|
||||
nsresult rv
|
||||
= DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
|
||||
aType);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCString access;
|
||||
rv = DeviceStorageTypeChecker::GetAccessForRequest(
|
||||
DeviceStorageRequestType(mRequestType), access);
|
||||
NS_IMETHOD GetAccess(nsACString & aAccess)
|
||||
{
|
||||
nsresult rv = DeviceStorageTypeChecker::GetAccessForRequest(
|
||||
DeviceStorageRequestType(mRequestType), aAccess);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return CreatePermissionArray(type, access, aTypes);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
|
||||
@ -3296,10 +3299,8 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(type, NS_LITERAL_CSTRING("read")));
|
||||
child->SendPContentPermissionRequestConstructor(r,
|
||||
permArray,
|
||||
child->SendPContentPermissionRequestConstructor(r, type,
|
||||
NS_LITERAL_CSTRING("read"),
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
r->Sendprompt();
|
||||
|
@ -396,7 +396,6 @@ let FormAssistant = {
|
||||
range = getSelectionRange(this.focusedElement);
|
||||
if (range[0] !== this.selectionStart ||
|
||||
range[1] !== this.selectionEnd) {
|
||||
this.sendKeyboardState(this.focusedElement);
|
||||
this.updateSelection();
|
||||
}
|
||||
break;
|
||||
|
@ -7,13 +7,15 @@
|
||||
interface nsIPrincipal;
|
||||
interface nsIDOMWindow;
|
||||
interface nsIDOMElement;
|
||||
interface nsIArray;
|
||||
|
||||
/**
|
||||
* Interface provides the request type and its access.
|
||||
* Interface allows access to a content to request
|
||||
* permission to perform a privileged operation such as
|
||||
* geolocation.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(384b6cc4-a66b-4bea-98e0-eb10562a9ba4)]
|
||||
interface nsIContentPermissionType : nsISupports {
|
||||
[scriptable, uuid(1de67000-2de8-11e2-81c1-0800200c9a66)]
|
||||
interface nsIContentPermissionRequest : nsISupports {
|
||||
|
||||
/**
|
||||
* The type of the permission request, such as
|
||||
* "geolocation".
|
||||
@ -25,22 +27,8 @@ interface nsIContentPermissionType : nsISupports {
|
||||
* "read".
|
||||
*/
|
||||
readonly attribute ACString access;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface allows access to a content to request
|
||||
* permission to perform a privileged operation such as
|
||||
* geolocation.
|
||||
*/
|
||||
[scriptable, uuid(69a39d88-d1c4-4ba9-9b19-bafc7a1bb783)]
|
||||
interface nsIContentPermissionRequest : nsISupports {
|
||||
/**
|
||||
* The array will include the request types. Elements of this array are
|
||||
* nsIContentPermissionType object.
|
||||
*/
|
||||
readonly attribute nsIArray types;
|
||||
|
||||
/*
|
||||
* The principal of the permission request.
|
||||
*/
|
||||
readonly attribute nsIPrincipal principal;
|
||||
|
@ -16,7 +16,6 @@ include protocol PIndexedDB;
|
||||
include DOMTypes;
|
||||
include JavaScriptTypes;
|
||||
include URIParams;
|
||||
include PContentPermission;
|
||||
|
||||
|
||||
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
|
||||
@ -207,8 +206,10 @@ parent:
|
||||
* Initiates an asynchronous request for permission for the
|
||||
* provided principal.
|
||||
*
|
||||
* @param aRequests
|
||||
* The array of permissions to request.
|
||||
* @param aType
|
||||
* The type of permission to request.
|
||||
* @param aAccess
|
||||
* Access type. "read" for example.
|
||||
* @param aPrincipal
|
||||
* The principal of the request.
|
||||
*
|
||||
@ -216,7 +217,7 @@ parent:
|
||||
* principals that can live in the content process should
|
||||
* provided.
|
||||
*/
|
||||
PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal);
|
||||
PContentPermissionRequest(nsCString aType, nsCString aAccess, Principal principal);
|
||||
|
||||
PContentDialog(uint32_t aType, nsCString aName, nsCString aFeatures,
|
||||
int32_t[] aIntParams, nsString[] aStringParams);
|
||||
@ -345,6 +346,13 @@ child:
|
||||
*/
|
||||
HandleLongTap(CSSIntPoint point);
|
||||
|
||||
/**
|
||||
* Notifies the child that the parent has begun or finished transforming
|
||||
* the visible child content area. Useful for showing/hiding scrollbars.
|
||||
*/
|
||||
NotifyTransformBegin(ViewID aViewId);
|
||||
NotifyTransformEnd(ViewID aViewId);
|
||||
|
||||
/**
|
||||
* Sending an activate message moves focus to the child.
|
||||
*/
|
||||
|
@ -1,14 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct PermissionRequest {
|
||||
nsCString type;
|
||||
nsCString access;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1157,11 +1157,12 @@ TabChild::ArraysToParams(const InfallibleTArray<int>& aIntParams,
|
||||
#ifdef DEBUG
|
||||
PContentPermissionRequestChild*
|
||||
TabChild:: SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor,
|
||||
const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const nsCString& aType,
|
||||
const nsCString& aAccess,
|
||||
const IPC::Principal& aPrincipal)
|
||||
{
|
||||
PCOMContentPermissionRequestChild* child = static_cast<PCOMContentPermissionRequestChild*>(aActor);
|
||||
PContentPermissionRequestChild* request = PBrowserChild::SendPContentPermissionRequestConstructor(aActor, aRequests, aPrincipal);
|
||||
PContentPermissionRequestChild* request = PBrowserChild::SendPContentPermissionRequestConstructor(aActor, aType, aAccess, aPrincipal);
|
||||
child->mIPCOpen = true;
|
||||
return request;
|
||||
}
|
||||
@ -1659,6 +1660,32 @@ TabChild::RecvHandleLongTap(const CSSIntPoint& aPoint)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvNotifyTransformBegin(const ViewID& aViewId)
|
||||
{
|
||||
nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
|
||||
if (sf) {
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sf);
|
||||
if (scrollbarOwner) {
|
||||
scrollbarOwner->ScrollbarActivityStarted();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvNotifyTransformEnd(const ViewID& aViewId)
|
||||
{
|
||||
nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aViewId);
|
||||
if (sf) {
|
||||
nsIScrollbarOwner* scrollbarOwner = do_QueryFrame(sf);
|
||||
if (scrollbarOwner) {
|
||||
scrollbarOwner->ScrollbarActivityStopped();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvActivate()
|
||||
{
|
||||
@ -2013,8 +2040,7 @@ TabChild::DeallocPContentDialogChild(PContentDialogChild* aDialog)
|
||||
}
|
||||
|
||||
PContentPermissionRequestChild*
|
||||
TabChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal)
|
||||
TabChild::AllocPContentPermissionRequestChild(const nsCString& aType, const nsCString& aAccess, const IPC::Principal&)
|
||||
{
|
||||
NS_RUNTIMEABORT("unused");
|
||||
return nullptr;
|
||||
|
@ -217,6 +217,8 @@ public:
|
||||
virtual bool RecvHandleDoubleTap(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvHandleSingleTap(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvHandleLongTap(const CSSIntPoint& aPoint);
|
||||
virtual bool RecvNotifyTransformBegin(const ViewID& aViewId);
|
||||
virtual bool RecvNotifyTransformEnd(const ViewID& aViewId);
|
||||
virtual bool RecvActivate();
|
||||
virtual bool RecvDeactivate();
|
||||
virtual bool RecvMouseEvent(const nsString& aType,
|
||||
@ -278,11 +280,13 @@ public:
|
||||
#ifdef DEBUG
|
||||
virtual PContentPermissionRequestChild*
|
||||
SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor,
|
||||
const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const nsCString& aType,
|
||||
const nsCString& aAccess,
|
||||
const IPC::Principal& aPrincipal);
|
||||
#endif /* DEBUG */
|
||||
|
||||
virtual PContentPermissionRequestChild* AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
virtual PContentPermissionRequestChild* AllocPContentPermissionRequestChild(const nsCString& aType,
|
||||
const nsCString& aAccess,
|
||||
const IPC::Principal& aPrincipal);
|
||||
virtual bool DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor);
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "mozilla/BrowserElementParent.h"
|
||||
#include "mozilla/docshell/OfflineCacheUpdateParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/ipc/DocumentRendererParent.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
@ -522,6 +521,20 @@ void TabParent::HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers)
|
||||
}
|
||||
}
|
||||
|
||||
void TabParent::NotifyTransformBegin(ViewID aViewId)
|
||||
{
|
||||
if (!mIsDestroyed) {
|
||||
unused << SendNotifyTransformBegin(aViewId);
|
||||
}
|
||||
}
|
||||
|
||||
void TabParent::NotifyTransformEnd(ViewID aViewId)
|
||||
{
|
||||
if (!mIsDestroyed) {
|
||||
unused << SendNotifyTransformEnd(aViewId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::Activate()
|
||||
{
|
||||
@ -580,10 +593,9 @@ TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor)
|
||||
}
|
||||
|
||||
PContentPermissionRequestParent*
|
||||
TabParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal)
|
||||
TabParent::AllocPContentPermissionRequestParent(const nsCString& type, const nsCString& access, const IPC::Principal& principal)
|
||||
{
|
||||
return CreateContentPermissionRequestParent(aRequests, mFrameElement, aPrincipal);
|
||||
return new ContentPermissionRequestParent(type, access, mFrameElement, principal);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -196,6 +196,8 @@ public:
|
||||
void HandleDoubleTap(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void HandleSingleTap(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void HandleLongTap(const CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
void NotifyTransformBegin(ViewID aViewId);
|
||||
void NotifyTransformEnd(ViewID aViewId);
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
@ -225,8 +227,7 @@ public:
|
||||
virtual bool DeallocPDocumentRendererParent(PDocumentRendererParent* actor);
|
||||
|
||||
virtual PContentPermissionRequestParent*
|
||||
AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal);
|
||||
AllocPContentPermissionRequestParent(const nsCString& aType, const nsCString& aAccess, const IPC::Principal& aPrincipal);
|
||||
virtual bool DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor);
|
||||
|
||||
virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdateParent(
|
||||
|
@ -68,7 +68,6 @@ IPDL_SOURCES += [
|
||||
'PBrowser.ipdl',
|
||||
'PContent.ipdl',
|
||||
'PContentDialog.ipdl',
|
||||
'PContentPermission.ipdlh',
|
||||
'PContentPermissionRequest.ipdl',
|
||||
'PCrashReporter.ipdl',
|
||||
'PDocumentRenderer.ipdl',
|
||||
|
@ -41,7 +41,7 @@
|
||||
#include "MediaEngineWebRTC.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "MediaPermissionGonk.h"
|
||||
#endif
|
||||
|
||||
@ -756,7 +756,7 @@ public:
|
||||
, mListener(aListener)
|
||||
, mPrefs(aPrefs)
|
||||
, mDeviceChosen(false)
|
||||
, mBackend(nullptr)
|
||||
, mBackendChosen(false)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
{}
|
||||
|
||||
@ -778,11 +778,15 @@ public:
|
||||
, mListener(aListener)
|
||||
, mPrefs(aPrefs)
|
||||
, mDeviceChosen(false)
|
||||
, mBackendChosen(true)
|
||||
, mBackend(aBackend)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
{}
|
||||
|
||||
~GetUserMediaRunnable() {
|
||||
if (mBackendChosen) {
|
||||
delete mBackend;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -790,15 +794,14 @@ public:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
MediaEngine* backend = mBackend;
|
||||
// Was a backend provided?
|
||||
if (!backend) {
|
||||
backend = mManager->GetBackend(mWindowID);
|
||||
if (!mBackendChosen) {
|
||||
mBackend = mManager->GetBackend(mWindowID);
|
||||
}
|
||||
|
||||
// Was a device provided?
|
||||
if (!mDeviceChosen) {
|
||||
nsresult rv = SelectDevice(backend);
|
||||
nsresult rv = SelectDevice();
|
||||
if (rv != NS_OK) {
|
||||
return rv;
|
||||
}
|
||||
@ -870,10 +873,10 @@ public:
|
||||
}
|
||||
|
||||
nsresult
|
||||
SelectDevice(MediaEngine* backend)
|
||||
SelectDevice()
|
||||
{
|
||||
if (mConstraints.mPicture || mConstraints.mVideo) {
|
||||
ScopedDeletePtr<SourceSet> sources (GetSources(backend,
|
||||
ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
|
||||
mConstraints.mVideom, &MediaEngine::EnumerateVideoDevices));
|
||||
|
||||
if (!sources->Length()) {
|
||||
@ -887,7 +890,7 @@ public:
|
||||
}
|
||||
|
||||
if (mConstraints.mAudio) {
|
||||
ScopedDeletePtr<SourceSet> sources (GetSources(backend,
|
||||
ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
|
||||
mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices));
|
||||
|
||||
if (!sources->Length()) {
|
||||
@ -981,8 +984,9 @@ private:
|
||||
MediaEnginePrefs mPrefs;
|
||||
|
||||
bool mDeviceChosen;
|
||||
bool mBackendChosen;
|
||||
|
||||
RefPtr<MediaEngine> mBackend;
|
||||
MediaEngine* mBackend;
|
||||
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
|
||||
};
|
||||
|
||||
@ -1262,10 +1266,10 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
|
||||
// Force MediaManager to startup before we try to access it from other threads
|
||||
// Hack: should init singleton earlier unless it's expensive (mem or CPU)
|
||||
(void) MediaManager::Get();
|
||||
#ifdef MOZ_B2G
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// Initialize MediaPermissionManager before send out any permission request.
|
||||
(void) MediaPermissionManager::GetInstance();
|
||||
#endif //MOZ_B2G
|
||||
#endif //MOZ_WIDGET_GONK
|
||||
}
|
||||
|
||||
// Store the WindowID in a hash table and mark as active. The entry is removed
|
||||
@ -1411,11 +1415,11 @@ MediaManager::GetBackend(uint64_t aWindowId)
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mBackend) {
|
||||
#if defined(MOZ_WEBRTC)
|
||||
#ifndef MOZ_B2G_CAMERA
|
||||
#ifndef MOZ_B2G_CAMERA
|
||||
mBackend = new MediaEngineWebRTC(mPrefs);
|
||||
#else
|
||||
mBackend = new MediaEngineWebRTC(mCameraManager);
|
||||
#endif
|
||||
#else
|
||||
mBackend = new MediaEngineWebRTC(mCameraManager, aWindowId);
|
||||
#endif
|
||||
#else
|
||||
mBackend = new MediaEngineDefault();
|
||||
#endif
|
||||
|
@ -513,7 +513,9 @@ private:
|
||||
// Make private because we want only one instance of this class
|
||||
MediaManager();
|
||||
|
||||
~MediaManager() {}
|
||||
~MediaManager() {
|
||||
delete mBackend;
|
||||
}
|
||||
|
||||
nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
|
||||
bool* aAudio);
|
||||
@ -528,11 +530,11 @@ private:
|
||||
|
||||
Mutex mMutex;
|
||||
// protected with mMutex:
|
||||
RefPtr<MediaEngine> mBackend;
|
||||
MediaEngine* mBackend;
|
||||
|
||||
static StaticRefPtr<MediaManager> sSingleton;
|
||||
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsRefPtr<nsDOMCameraManager> mCameraManager;
|
||||
#endif
|
||||
};
|
||||
|
@ -20,36 +20,14 @@
|
||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
|
||||
#define AUDIO_PERMISSION_NAME "audio-capture"
|
||||
#define VIDEO_PERMISSION_NAME "video-capture"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static MediaPermissionManager *gMediaPermMgr = nullptr;
|
||||
|
||||
static uint32_t
|
||||
ConvertArrayToPermissionRequest(nsIArray* aSrcArray,
|
||||
nsTArray<PermissionRequest>& aDesArray)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
aSrcArray->GetLength(&len);
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
|
||||
nsAutoCString type;
|
||||
nsAutoCString access;
|
||||
cpt->GetType(type);
|
||||
cpt->GetAccess(access);
|
||||
aDesArray.AppendElement(PermissionRequest(type, access));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
// Helper function for notifying permission granted
|
||||
static nsresult
|
||||
NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
|
||||
@ -115,7 +93,6 @@ public:
|
||||
|
||||
private:
|
||||
bool mAudio; // Request for audio permission
|
||||
bool mVideo; // Request for video permission
|
||||
nsRefPtr<dom::GetUserMediaRequest> mRequest;
|
||||
nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices; // candiate device list
|
||||
};
|
||||
@ -131,7 +108,6 @@ MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest
|
||||
mRequest->GetConstraints(constraints);
|
||||
|
||||
mAudio = constraints.mAudio;
|
||||
mVideo = constraints.mVideo;
|
||||
|
||||
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
|
||||
nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
|
||||
@ -140,34 +116,10 @@ MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest
|
||||
if (mAudio && deviceType.EqualsLiteral("audio")) {
|
||||
mDevices.AppendElement(device);
|
||||
}
|
||||
if (mVideo && deviceType.EqualsLiteral("video")) {
|
||||
mDevices.AppendElement(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nsIContentPermissionRequest methods
|
||||
NS_IMETHODIMP
|
||||
MediaPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (mAudio) {
|
||||
nsCOMPtr<ContentPermissionType> AudioType =
|
||||
new ContentPermissionType(NS_LITERAL_CSTRING(AUDIO_PERMISSION_NAME),
|
||||
NS_LITERAL_CSTRING("unused"));
|
||||
types->AppendElement(AudioType, false);
|
||||
}
|
||||
if (mVideo) {
|
||||
nsCOMPtr<ContentPermissionType> VideoType =
|
||||
new ContentPermissionType(NS_LITERAL_CSTRING(VIDEO_PERMISSION_NAME),
|
||||
NS_LITERAL_CSTRING("unused"));
|
||||
types->AppendElement(VideoType, false);
|
||||
}
|
||||
NS_IF_ADDREF(*aTypes = types);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
|
||||
{
|
||||
@ -183,6 +135,24 @@ MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaPermissionRequest::GetType(nsACString &aType)
|
||||
{
|
||||
if (mAudio) {
|
||||
aType = AUDIO_PERMISSION_NAME;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaPermissionRequest::GetAccess(nsACString &aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MediaPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
|
||||
{
|
||||
@ -308,12 +278,13 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
|
||||
dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
|
||||
NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIArray> typeArray;
|
||||
rv = req->GetTypes(getter_AddRefs(typeArray));
|
||||
nsAutoCString type;
|
||||
rv = req->GetType(type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
ConvertArrayToPermissionRequest(typeArray, permArray);
|
||||
nsAutoCString access;
|
||||
rv = req->GetAccess(access);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = req->GetPrincipal(getter_AddRefs(principal));
|
||||
@ -321,7 +292,8 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
|
||||
|
||||
req->AddRef();
|
||||
child->SendPContentPermissionRequestConstructor(req,
|
||||
permArray,
|
||||
type,
|
||||
access,
|
||||
IPC::Principal(principal));
|
||||
|
||||
req->Sendprompt();
|
||||
|
@ -110,7 +110,6 @@ this.SystemMessagePermissionsTable = {
|
||||
"nfc-powerlevel-change": {
|
||||
"settings": ["read", "write"]
|
||||
},
|
||||
"rtsp-open-video": {},
|
||||
};
|
||||
|
||||
this.SystemMessagePermissionsChecker = {
|
||||
|
@ -256,11 +256,13 @@ MmsConnection.prototype = {
|
||||
let networkManager =
|
||||
Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
|
||||
let activeNetwork = networkManager.active;
|
||||
if (activeNetwork.serviceId != this.serviceId) {
|
||||
|
||||
let rilNetwork = activeNetwork.QueryInterface(Ci.nsIRilNetworkInterface);
|
||||
if (rilNetwork.serviceId != this.serviceId) {
|
||||
if (DEBUG) debug("Sevice ID between active/MMS network doesn't match.");
|
||||
return;
|
||||
}
|
||||
|
||||
let rilNetwork = activeNetwork.QueryInterface(Ci.nsIRilNetworkInterface);
|
||||
// Set up the MMS APN setting based on the connected MMS network,
|
||||
// which is going to be used for the HTTP requests later.
|
||||
this.setApnSetting(rilNetwork);
|
||||
|
@ -386,11 +386,17 @@ nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationRequest::GetTypes(nsIArray** aTypes)
|
||||
nsGeolocationRequest::GetType(nsACString & aType)
|
||||
{
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
aType = "geolocation";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationRequest::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1447,15 +1453,12 @@ Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(NS_LITERAL_CSTRING("geolocation"),
|
||||
NS_LITERAL_CSTRING("unused")));
|
||||
|
||||
// Retain a reference so the object isn't deleted without IPDL's knowledge.
|
||||
// Corresponding release occurs in DeallocPContentPermissionRequest.
|
||||
request->AddRef();
|
||||
child->SendPContentPermissionRequestConstructor(request,
|
||||
permArray,
|
||||
NS_LITERAL_CSTRING("geolocation"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
request->Sendprompt();
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "PCOMContentPermissionRequestChild.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PermissionMessageUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -180,12 +179,9 @@ DesktopNotification::Init()
|
||||
// Corresponding release occurs in DeallocPContentPermissionRequest.
|
||||
nsRefPtr<DesktopNotificationRequest> copy = request;
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(
|
||||
NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused")));
|
||||
child->SendPContentPermissionRequestConstructor(copy.forget().get(),
|
||||
permArray,
|
||||
NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
request->Sendprompt();
|
||||
@ -357,11 +353,17 @@ DesktopNotificationRequest::Allow()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
|
||||
DesktopNotificationRequest::GetType(nsACString & aType)
|
||||
{
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
aType = "desktop-notification";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DesktopNotificationRequest::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#ifdef MOZ_B2G
|
||||
#include "nsIDOMDesktopNotification.h"
|
||||
#endif
|
||||
@ -268,11 +267,9 @@ NotificationPermissionRequest::Run()
|
||||
// Corresponding release occurs in DeallocPContentPermissionRequest.
|
||||
AddRef();
|
||||
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(
|
||||
NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused")));
|
||||
child->SendPContentPermissionRequestConstructor(this, permArray,
|
||||
NS_NAMED_LITERAL_CSTRING(type, "desktop-notification");
|
||||
NS_NAMED_LITERAL_CSTRING(access, "unused");
|
||||
child->SendPContentPermissionRequestConstructor(this, type, access,
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
Sendprompt();
|
||||
@ -345,11 +342,17 @@ NotificationPermissionRequest::CallCallback()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
NotificationPermissionRequest::GetAccess(nsACString& aAccess)
|
||||
{
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
aAccess.AssignLiteral("unused");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NotificationPermissionRequest::GetType(nsACString& aType)
|
||||
{
|
||||
aType.AssignLiteral("desktop-notification");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -2761,9 +2761,10 @@ this.PDU_CDMA_MSG_TYPE_BROADCAST = 0x01; // Broadcast
|
||||
this.PDU_CDMA_MSG_TYPE_ACK = 0x02; // Acknowledge
|
||||
|
||||
// SMS Teleservice Identitifier, as defined in 3GPP2 N.S0005, Table 175
|
||||
this.PDU_CDMA_MSG_TELESERIVCIE_ID_SMS = 0x1002; // SMS
|
||||
this.PDU_CDMA_MSG_TELESERIVCIE_ID_WEMT = 0x1005; // Wireless Enhanced Messaging Teleservice
|
||||
// required for fragmented SMS
|
||||
this.PDU_CDMA_MSG_TELESERIVCIE_ID_SMS = 0x1002; // SMS
|
||||
this.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP = 0x1004; // WAP
|
||||
this.PDU_CDMA_MSG_TELESERIVCIE_ID_WEMT = 0x1005; // Wireless Enhanced Messaging Teleservice
|
||||
// required for fragmented SMS
|
||||
|
||||
// SMS Service Category, as defined in 3GPP2 C.R1001-D, Table 9.3.1-1
|
||||
this.PDU_CDMA_MSG_CATEGORY_UNSPEC = 0x00; // Unknown/Unspecified
|
||||
|
@ -4312,6 +4312,61 @@ let RIL = {
|
||||
return PDU_FCS_OK;
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for processing CDMA SMS WAP Push Message
|
||||
*
|
||||
* @param message
|
||||
* decoded WAP message from CdmaPDUHelper.
|
||||
*
|
||||
* @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
|
||||
*/
|
||||
_processCdmaSmsWapPush: function _processCdmaSmsWapPush(message) {
|
||||
if (!message.data) {
|
||||
if (DEBUG) debug("no data inside WAP Push message.");
|
||||
return PDU_FCS_OK;
|
||||
}
|
||||
|
||||
// See 6.5. MAPPING OF WDP TO CDMA SMS in WAP-295-WDP.
|
||||
//
|
||||
// Field | Length (bits)
|
||||
// -----------------------------------------
|
||||
// MSG_TYPE | 8
|
||||
// TOTAL_SEGMENTS | 8
|
||||
// SEGMENT_NUMBER | 8
|
||||
// DATAGRAM | (NUM_FIELDS – 3) * 8
|
||||
let index = 0;
|
||||
if (message.data[index++] !== 0) {
|
||||
if (DEBUG) debug("Ignore a WAP Message which is not WDP.");
|
||||
return PDU_FCS_OK;
|
||||
}
|
||||
|
||||
// 1. Originator Address in SMS-TL + Message_Id in SMS-TS are used to identify a unique WDP datagram.
|
||||
// 2. TOTAL_SEGMENTS, SEGMENT_NUMBER are used to verify that a complete
|
||||
// datagram has been received and is ready to be passed to a higher layer.
|
||||
message.header = {
|
||||
segmentRef: message.msgId,
|
||||
segmentMaxSeq: message.data[index++],
|
||||
segmentSeq: message.data[index++] + 1 // It's zero-based in CDMA WAP Push.
|
||||
};
|
||||
|
||||
if (message.header.segmentSeq > message.header.segmentMaxSeq) {
|
||||
if (DEBUG) debug("Wrong WDP segment info.");
|
||||
return PDU_FCS_OK;
|
||||
}
|
||||
|
||||
// Ports are only specified in 1st segment.
|
||||
if (message.header.segmentSeq == 1) {
|
||||
message.header.originatorPort = message.data[index++] << 8;
|
||||
message.header.originatorPort |= message.data[index++];
|
||||
message.header.destinationPort = message.data[index++] << 8;
|
||||
message.header.destinationPort |= message.data[index++];
|
||||
}
|
||||
|
||||
message.data = message.data.subarray(index);
|
||||
|
||||
return this._processSmsMultipart(message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper for processing received multipart SMS.
|
||||
*
|
||||
@ -4347,6 +4402,22 @@ let RIL = {
|
||||
delete original.body;
|
||||
}
|
||||
options.receivedSegments++;
|
||||
|
||||
// The port information is only available in 1st segment for CDMA WAP Push.
|
||||
// If the segments of a WAP Push are not received in sequence
|
||||
// (e.g., SMS with seq == 1 is not the 1st segment received by the device),
|
||||
// we have to retrieve the port information from 1st segment and
|
||||
// save it into the cached options.header.
|
||||
if (original.teleservice === PDU_CDMA_MSG_TELESERIVCIE_ID_WAP && seq === 1) {
|
||||
if (!options.header.originatorPort && original.header.originatorPort) {
|
||||
options.header.originatorPort = original.header.originatorPort;
|
||||
}
|
||||
|
||||
if (!options.header.destinationPort && original.header.destinationPort) {
|
||||
options.header.destinationPort = original.header.destinationPort;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.receivedSegments < options.segmentMaxSeq) {
|
||||
if (DEBUG) {
|
||||
debug("Got segment no." + seq + " of a multipart SMS: "
|
||||
@ -6215,7 +6286,9 @@ RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] = function UNSOLICITED_RESPONSE_CDMA_NEW_
|
||||
let [message, result] = CdmaPDUHelper.processReceivedSms(length);
|
||||
|
||||
if (message) {
|
||||
if (message.subMsgType === PDU_CDMA_MSG_TYPE_DELIVER_ACK) {
|
||||
if (message.teleservice === PDU_CDMA_MSG_TELESERIVCIE_ID_WAP) {
|
||||
result = this._processCdmaSmsWapPush(message);
|
||||
} else if (message.subMsgType === PDU_CDMA_MSG_TYPE_DELIVER_ACK) {
|
||||
result = this._processCdmaSmsStatusReport(message);
|
||||
} else {
|
||||
result = this._processSmsMultipart(message);
|
||||
@ -7997,6 +8070,25 @@ let BitBufferHelper = {
|
||||
return result;
|
||||
},
|
||||
|
||||
backwardReadPilot: function backwardReadPilot(length) {
|
||||
if (length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Zero-based position.
|
||||
let bitIndexToRead = this.readIndex * 8 - this.readCacheSize - length;
|
||||
|
||||
if (bitIndexToRead < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update readIndex, readCache, readCacheSize accordingly.
|
||||
let readBits = bitIndexToRead % 8;
|
||||
this.readIndex = Math.floor(bitIndexToRead / 8) + ((readBits) ? 1 : 0);
|
||||
this.readCache = (readBits) ? this.readBuffer[this.readIndex - 1] : 0;
|
||||
this.readCacheSize = (readBits) ? (8 - readBits) : 0;
|
||||
},
|
||||
|
||||
writeBits: function writeBits(value, length) {
|
||||
if (length <= 0 || length > 32) {
|
||||
return;
|
||||
@ -8463,17 +8555,17 @@ let CdmaPDUHelper = {
|
||||
|
||||
// Bearer Data Sub-Parameter: User Data
|
||||
let userData = message[PDU_CDMA_MSG_USERDATA_BODY];
|
||||
[message.header, message.body, message.encoding] =
|
||||
(userData)? [userData.header, userData.body, userData.encoding]
|
||||
: [null, null, null];
|
||||
[message.header, message.body, message.encoding, message.data] =
|
||||
(userData) ? [userData.header, userData.body, userData.encoding, userData.data]
|
||||
: [null, null, null, null];
|
||||
|
||||
// Bearer Data Sub-Parameter: Message Status
|
||||
// Success Delivery (0) if both Message Status and User Data are absent.
|
||||
// Message Status absent (-1) if only User Data is available.
|
||||
let msgStatus = message[PDU_CDMA_MSG_USER_DATA_MSG_STATUS];
|
||||
[message.errorClass, message.msgStatus] =
|
||||
(msgStatus)? [msgStatus.errorClass, msgStatus.msgStatus]
|
||||
: ((message.body)? [-1, -1]: [0, 0]);
|
||||
(msgStatus) ? [msgStatus.errorClass, msgStatus.msgStatus]
|
||||
: ((message.body) ? [-1, -1] : [0, 0]);
|
||||
|
||||
// Transform message to GSM msg
|
||||
let msg = {
|
||||
@ -8489,7 +8581,7 @@ let CdmaPDUHelper = {
|
||||
replace: false,
|
||||
header: message.header,
|
||||
body: message.body,
|
||||
data: null,
|
||||
data: message.data,
|
||||
timestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
|
||||
language: message[PDU_CDMA_LANGUAGE_INDICATOR],
|
||||
status: null,
|
||||
@ -8502,7 +8594,8 @@ let CdmaPDUHelper = {
|
||||
subMsgType: message[PDU_CDMA_MSG_USERDATA_MSG_ID].msgType,
|
||||
msgId: message[PDU_CDMA_MSG_USERDATA_MSG_ID].msgId,
|
||||
errorClass: message.errorClass,
|
||||
msgStatus: message.msgStatus
|
||||
msgStatus: message.msgStatus,
|
||||
teleservice: message.teleservice
|
||||
};
|
||||
|
||||
return msg;
|
||||
@ -8939,6 +9032,15 @@ let CdmaPDUHelper = {
|
||||
msgBodySize -= result.header.length;
|
||||
}
|
||||
|
||||
// Store original payload if enconding is OCTET for further handling of WAP Push, etc.
|
||||
if (encoding === PDU_CDMA_MSG_CODING_OCTET && msgBodySize > 0) {
|
||||
result.data = new Uint8Array(msgBodySize);
|
||||
for (let i = 0; i < msgBodySize; i++) {
|
||||
result.data[i] = BitBufferHelper.readBits(8);
|
||||
}
|
||||
BitBufferHelper.backwardReadPilot(8 * msgBodySize);
|
||||
}
|
||||
|
||||
// Decode sms content
|
||||
result.body = this.decodeCdmaPDUMsg(encoding, msgType, msgBodySize);
|
||||
|
||||
|
@ -26,6 +26,164 @@ function _getWorker() {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to covert a HEX string to a byte array.
|
||||
*
|
||||
* @param hexString
|
||||
* A hexadecimal string of which the length is even.
|
||||
*/
|
||||
function hexStringToBytes(hexString) {
|
||||
let bytes = [];
|
||||
|
||||
let length = hexString.length;
|
||||
|
||||
for (let i = 0; i < length; i += 2) {
|
||||
bytes.push(Number.parseInt(hexString.substr(i, 2), 16));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to covert a byte array to a HEX string.
|
||||
*
|
||||
* @param bytes
|
||||
* Could be a regular byte array or Uint8Array.
|
||||
*/
|
||||
function bytesToHexString(bytes) {
|
||||
let hexString = "";
|
||||
let hex;
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
hex = bytes[i].toString(16).toUpperCase();
|
||||
if (hex.length === 1) {
|
||||
hexString += "0";
|
||||
}
|
||||
hexString += hex;
|
||||
}
|
||||
|
||||
return hexString;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to ecode Opaque UserData
|
||||
*
|
||||
* @param msg_type
|
||||
* PDU_CDMA_MSG_TYPE_SUBMIT or PDU_CDMA_MSG_TYPE_DELIVER
|
||||
* @param data
|
||||
* The byte array of opaque data to be encoded.
|
||||
*/
|
||||
function encodeOpaqueUserData(bitBufferHelper, options) {
|
||||
let bearerDataBuffer = [];
|
||||
bitBufferHelper.startWrite(bearerDataBuffer);
|
||||
|
||||
// Msg-Id
|
||||
bitBufferHelper.writeBits(PDU_CDMA_MSG_USERDATA_MSG_ID, 8);
|
||||
bitBufferHelper.writeBits(3, 8);
|
||||
bitBufferHelper.writeBits(options.msg_type, 4); // MSG_TYPE
|
||||
bitBufferHelper.writeBits(1, 16); // MSG_ID
|
||||
bitBufferHelper.flushWithPadding(); // HEADER_IND (1) + RESERVED (3)
|
||||
|
||||
// User Data
|
||||
bitBufferHelper.writeBits(PDU_CDMA_MSG_USERDATA_BODY, 8);
|
||||
let dataLength = options.data.length;
|
||||
bitBufferHelper.writeBits(2 + dataLength, 8); // 2 bytes for MSG_ENCODING, NUM_FIELDS
|
||||
bitBufferHelper.writeBits(PDU_CDMA_MSG_CODING_OCTET, 5); //MSG_ENCODING
|
||||
// MSG_TYPE is omitted if MSG_ENCODING is CODING_OCTET
|
||||
bitBufferHelper.writeBits(dataLength, 8); // NUM_FIELDS
|
||||
for (let i = 0; i < dataLength; i++) { // CHARi
|
||||
bitBufferHelper.writeBits(options.data[i], 8);
|
||||
}
|
||||
bitBufferHelper.flushWithPadding(); // RESERVED (3 filling bits)
|
||||
|
||||
return bearerDataBuffer;
|
||||
}
|
||||
|
||||
function newSmsParcel(cdmaPduHelper, pdu) {
|
||||
return newIncomingParcel(-1,
|
||||
RESPONSE_TYPE_UNSOLICITED,
|
||||
UNSOLICITED_RESPONSE_CDMA_NEW_SMS,
|
||||
pduToParcelData(cdmaPduHelper, pdu));
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to encode PDU into Parcel.
|
||||
* See ril_cdma_sms.h for the structure definition of RIL_CDMA_SMS_Message
|
||||
*
|
||||
* @param teleservice
|
||||
* The Teleservice-Id of this PDU.
|
||||
* See PDU_CDMA_MSG_TELESERIVCIE_ID_XXX in ril_const.js.
|
||||
* @param address
|
||||
* The Orginating or Destinating address.
|
||||
* @param bearerData
|
||||
* The byte array of the encoded bearer data.
|
||||
*/
|
||||
function pduToParcelData(cdmaPduHelper, pdu) {
|
||||
|
||||
let addrInfo = cdmaPduHelper.encodeAddr(pdu.address);
|
||||
// Teleservice, isServicePresent, ServiceCategory,
|
||||
// addrInfo {digitMode, numberMode, numberType, numberPlan, address.length, address}
|
||||
// Sub Address
|
||||
// bearerData length, bearerData.
|
||||
let dataLength = 4 + 4 + 4
|
||||
+ (5 + addrInfo.address.length) * 4
|
||||
+ 3 * 4
|
||||
+ 4 + pdu.bearerData.length * 4;
|
||||
|
||||
let data = new Uint8Array(dataLength);
|
||||
let offset = 0;
|
||||
|
||||
function writeInt(value) {
|
||||
data[offset++] = value & 0xFF;
|
||||
data[offset++] = (value >> 8) & 0xFF;
|
||||
data[offset++] = (value >> 16) & 0xFF;
|
||||
data[offset++] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
function writeByte(value) {
|
||||
data[offset++] = value & 0xFF;
|
||||
data[offset++] = 0;
|
||||
data[offset++] = 0;
|
||||
data[offset++] = 0;
|
||||
}
|
||||
|
||||
// Teleservice
|
||||
writeInt(pdu.teleservice);
|
||||
|
||||
// isServicePresent
|
||||
writeByte(0);
|
||||
|
||||
// ServiceCategory
|
||||
writeInt(PDU_CDMA_MSG_CATEGORY_UNSPEC);
|
||||
|
||||
// AddrInfo
|
||||
writeByte(addrInfo.digitMode);
|
||||
writeByte(addrInfo.numberMode);
|
||||
writeByte(addrInfo.numberType);
|
||||
writeByte(addrInfo.numberPlan);
|
||||
let addressLength = addrInfo.address.length;
|
||||
writeByte(addressLength);
|
||||
for (let i = 0; i < addressLength; i++) {
|
||||
writeByte(addrInfo.address[i]);
|
||||
}
|
||||
|
||||
// Subaddress
|
||||
writeByte(0);
|
||||
writeByte(0);
|
||||
writeByte(0);
|
||||
|
||||
// Bearer Data Length
|
||||
let dataLength = pdu.bearerData.length;
|
||||
writeByte(dataLength);
|
||||
|
||||
// Bearer Data
|
||||
for (let i = 0; i < dataLength; i++) {
|
||||
writeByte(pdu.bearerData[i]);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify CDMA SMS Delivery ACK Message.
|
||||
*/
|
||||
@ -73,7 +231,7 @@ add_test(function test_processCdmaSmsStatusReport() {
|
||||
let postedMessage = workerHelper.postedMessage;
|
||||
|
||||
// Check if pending token is removed.
|
||||
do_check_true((errorClass === 2)? !!sentSmsMap[msgId]: !sentSmsMap[msgId]);
|
||||
do_check_true((errorClass === 2) ? !!sentSmsMap[msgId] : !sentSmsMap[msgId]);
|
||||
|
||||
// Check the response message accordingly.
|
||||
if (errorClass === -1) {
|
||||
@ -98,3 +256,67 @@ add_test(function test_processCdmaSmsStatusReport() {
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify WAP Push over CDMA SMS Message.
|
||||
*/
|
||||
add_test(function test_processCdmaSmsWapPush() {
|
||||
let workerHelper = _getWorker(),
|
||||
worker = workerHelper.worker,
|
||||
bitBufferHelper = worker.BitBufferHelper,
|
||||
cdmaPduHelper = worker.CdmaPDUHelper;
|
||||
|
||||
function test_CdmaSmsWapPdu(wdpData, reversed) {
|
||||
let orig_address = "0987654321",
|
||||
hexString,
|
||||
fullDataHexString = "";
|
||||
|
||||
for (let i = 0; i < wdpData.length; i++) {
|
||||
let dataIndex = (reversed) ? (wdpData.length - i - 1) : i;
|
||||
hexString = "00"; // MSG_TYPE
|
||||
hexString += bytesToHexString([wdpData.length]); // TOTAL_SEG
|
||||
hexString += bytesToHexString([dataIndex]); // SEG_NUM (zero-based)
|
||||
if ((dataIndex === 0)) {
|
||||
hexString += "23F00B84"; // SOURCE_PORT, DEST_PORT for 1st segment
|
||||
}
|
||||
hexString += wdpData[dataIndex]; // WDP DATA
|
||||
|
||||
do_print("hexString: " + hexString);
|
||||
|
||||
fullDataHexString += wdpData[i];
|
||||
|
||||
let pdu = {
|
||||
teleservice: PDU_CDMA_MSG_TELESERIVCIE_ID_WAP,
|
||||
address: orig_address,
|
||||
bearerData: encodeOpaqueUserData(bitBufferHelper,
|
||||
{ msg_type: PDU_CDMA_MSG_TYPE_DELIVER,
|
||||
data: hexStringToBytes(hexString) })
|
||||
};
|
||||
|
||||
worker.onRILMessage(newSmsParcel(cdmaPduHelper, pdu));
|
||||
}
|
||||
|
||||
let postedMessage = workerHelper.postedMessage;
|
||||
|
||||
do_print("fullDataHexString: " + fullDataHexString);
|
||||
|
||||
do_check_eq("sms-received", postedMessage.rilMessageType);
|
||||
do_check_eq(PDU_CDMA_MSG_TELESERIVCIE_ID_WAP, postedMessage.teleservice);
|
||||
do_check_eq(orig_address, postedMessage.sender);
|
||||
do_check_eq(0x23F0, postedMessage.header.originatorPort);
|
||||
do_check_eq(0x0B84, postedMessage.header.destinationPort);
|
||||
do_check_eq(fullDataHexString, bytesToHexString(postedMessage.fullData));
|
||||
}
|
||||
|
||||
// Verify Single WAP PDU
|
||||
test_CdmaSmsWapPdu(["000102030405060708090A0B0C0D0E0F"]);
|
||||
|
||||
// Verify Concatenated WAP PDUs
|
||||
test_CdmaSmsWapPdu(["000102030405060708090A0B0C0D0E0F", "0F0E0D0C0B0A09080706050403020100"]);
|
||||
|
||||
// Verify Concatenated WAP PDUs received in reversed order.
|
||||
// Note: the port information is only available in 1st segment in CDMA WAP Push.
|
||||
test_CdmaSmsWapPdu(["000102030405060708090A0B0C0D0E0F", "0F0E0D0C0B0A09080706050403020100"], true);
|
||||
|
||||
run_next_test();
|
||||
});
|
@ -175,6 +175,7 @@ Layer::Layer(LayerManager* aManager, void* aImplData) :
|
||||
mIsFixedPosition(false),
|
||||
mMargins(0, 0, 0, 0),
|
||||
mStickyPositionData(nullptr),
|
||||
mIsScrollbar(false),
|
||||
mDebugColorIndex(0),
|
||||
mAnimationGeneration(0)
|
||||
{}
|
||||
@ -1269,6 +1270,13 @@ Layer::PrintInfo(nsACString& aTo, const char* aPrefix)
|
||||
if (GetContentFlags() & CONTENT_COMPONENT_ALPHA) {
|
||||
aTo += " [componentAlpha]";
|
||||
}
|
||||
if (GetIsScrollbar()) {
|
||||
if (GetScrollbarDirection() == VERTICAL) {
|
||||
aTo.AppendPrintf(" [vscrollbar=%lld]", GetScrollbarTargetContainerId());
|
||||
} else {
|
||||
aTo.AppendPrintf(" [hscrollbar=%lld]", GetScrollbarTargetContainerId());
|
||||
}
|
||||
}
|
||||
if (GetIsFixedPosition()) {
|
||||
aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f]", mAnchor.x, mAnchor.y);
|
||||
}
|
||||
|
@ -966,6 +966,29 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
enum ScrollDirection {
|
||||
VERTICAL,
|
||||
HORIZONTAL
|
||||
};
|
||||
|
||||
/**
|
||||
* CONSTRUCTION PHASE ONLY
|
||||
* If a layer is a scrollbar layer, |aScrollId| holds the scroll identifier
|
||||
* of the scrollable content that the scrollbar is for.
|
||||
*/
|
||||
void SetScrollbarData(FrameMetrics::ViewID aScrollId, ScrollDirection aDir)
|
||||
{
|
||||
if (mIsScrollbar ||
|
||||
mScrollbarTargetId != aScrollId ||
|
||||
mScrollbarDirection != aDir) {
|
||||
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ScrollbarData", this));
|
||||
mIsScrollbar = true;
|
||||
mScrollbarTargetId = aScrollId;
|
||||
mScrollbarDirection = aDir;
|
||||
Mutated();
|
||||
}
|
||||
}
|
||||
|
||||
// These getters can be used anytime.
|
||||
float GetOpacity() { return mOpacity; }
|
||||
gfxContext::GraphicsOperator GetMixBlendMode() const { return mMixBlendMode; }
|
||||
@ -990,6 +1013,9 @@ public:
|
||||
FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; }
|
||||
const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
|
||||
const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
|
||||
bool GetIsScrollbar() { return mIsScrollbar; }
|
||||
FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
|
||||
ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
|
||||
Layer* GetMaskLayer() const { return mMaskLayer; }
|
||||
|
||||
// Note that all lengths in animation data are either in CSS pixels or app
|
||||
@ -1361,6 +1387,9 @@ protected:
|
||||
LayerRect mInner;
|
||||
};
|
||||
nsAutoPtr<StickyPositionData> mStickyPositionData;
|
||||
bool mIsScrollbar;
|
||||
FrameMetrics::ViewID mScrollbarTargetId;
|
||||
ScrollDirection mScrollbarDirection;
|
||||
DebugOnly<uint32_t> mDebugColorIndex;
|
||||
// If this layer is used for OMTA, then this counter is used to ensure we
|
||||
// stay in sync with the animation manager
|
||||
|
@ -501,9 +501,9 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
|
||||
mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
|
||||
|
||||
gfx3DMatrix transform(gfx3DMatrix(treeTransform) * aLayer->GetTransform());
|
||||
// The transform already takes the resolution scale into account. Since we
|
||||
// will apply the resolution scale again when computing the effective
|
||||
// transform, we must apply the inverse resolution scale here.
|
||||
// GetTransform already takes the pre- and post-scale into account. Since we
|
||||
// will apply the pre- and post-scale again when computing the effective
|
||||
// transform, we must apply the inverses here.
|
||||
transform.Scale(1.0f/container->GetPreXScale(),
|
||||
1.0f/container->GetPreYScale(),
|
||||
1);
|
||||
@ -524,9 +524,69 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
|
||||
appliedTransform = true;
|
||||
}
|
||||
|
||||
if (container->GetIsScrollbar()) {
|
||||
ApplyAsyncTransformToScrollbar(container);
|
||||
}
|
||||
return appliedTransform;
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCompositionManager::ApplyAsyncTransformToScrollbar(ContainerLayer* aLayer)
|
||||
{
|
||||
// If this layer corresponds to a scrollbar, then search backwards through the
|
||||
// siblings until we find the container layer with the right ViewID; this is
|
||||
// the content that this scrollbar is for. Pick up the transient async transform
|
||||
// from that layer and use it to update the scrollbar position.
|
||||
// Note that it is possible that the content layer is no longer there; in
|
||||
// this case we don't need to do anything because there can't be an async
|
||||
// transform on the content.
|
||||
for (Layer* scrollTarget = aLayer->GetPrevSibling();
|
||||
scrollTarget;
|
||||
scrollTarget = scrollTarget->GetPrevSibling()) {
|
||||
if (!scrollTarget->AsContainerLayer()) {
|
||||
continue;
|
||||
}
|
||||
AsyncPanZoomController* apzc = scrollTarget->AsContainerLayer()->GetAsyncPanZoomController();
|
||||
if (!apzc) {
|
||||
continue;
|
||||
}
|
||||
const FrameMetrics& metrics = scrollTarget->AsContainerLayer()->GetFrameMetrics();
|
||||
if (metrics.mScrollId != aLayer->GetScrollbarTargetContainerId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gfx3DMatrix asyncTransform = gfx3DMatrix(apzc->GetCurrentAsyncTransform());
|
||||
gfx3DMatrix nontransientTransform = apzc->GetNontransientAsyncTransform();
|
||||
gfx3DMatrix transientTransform = asyncTransform * nontransientTransform.Inverse();
|
||||
|
||||
gfx3DMatrix scrollbarTransform;
|
||||
if (aLayer->GetScrollbarDirection() == Layer::VERTICAL) {
|
||||
float scale = metrics.CalculateCompositedRectInCssPixels().height / metrics.mScrollableRect.height;
|
||||
scrollbarTransform.ScalePost(1.f, 1.f / transientTransform.GetYScale(), 1.f);
|
||||
scrollbarTransform.TranslatePost(gfxPoint3D(0, -transientTransform._42 * scale, 0));
|
||||
}
|
||||
if (aLayer->GetScrollbarDirection() == Layer::HORIZONTAL) {
|
||||
float scale = metrics.CalculateCompositedRectInCssPixels().width / metrics.mScrollableRect.width;
|
||||
scrollbarTransform.ScalePost(1.f / transientTransform.GetXScale(), 1.f, 1.f);
|
||||
scrollbarTransform.TranslatePost(gfxPoint3D(-transientTransform._41 * scale, 0, 0));
|
||||
}
|
||||
|
||||
gfx3DMatrix transform = scrollbarTransform * aLayer->GetTransform();
|
||||
// GetTransform already takes the pre- and post-scale into account. Since we
|
||||
// will apply the pre- and post-scale again when computing the effective
|
||||
// transform, we must apply the inverses here.
|
||||
transform.Scale(1.0f/aLayer->GetPreXScale(),
|
||||
1.0f/aLayer->GetPreYScale(),
|
||||
1);
|
||||
transform.ScalePost(1.0f/aLayer->GetPostXScale(),
|
||||
1.0f/aLayer->GetPostYScale(),
|
||||
1);
|
||||
aLayer->AsLayerComposite()->SetShadowTransform(transform);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
|
||||
{
|
||||
|
@ -123,6 +123,11 @@ private:
|
||||
// controller wants another animation frame.
|
||||
bool ApplyAsyncContentTransformToTree(TimeStamp aCurrentFrame, Layer* aLayer,
|
||||
bool* aWantNextFrame);
|
||||
/**
|
||||
* Update the shadow trasnform for aLayer assuming that is a scrollbar,
|
||||
* so that it stays in sync with the content that is being scrolled by APZ.
|
||||
*/
|
||||
void ApplyAsyncTransformToScrollbar(ContainerLayer* aLayer);
|
||||
|
||||
void SetFirstPaintViewport(const LayerIntPoint& aOffset,
|
||||
const CSSToLayerScale& aZoom,
|
||||
|
@ -1647,9 +1647,11 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) {
|
||||
|
||||
if (mGeckoContentController) {
|
||||
if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
|
||||
mGeckoContentController->NotifyTransformBegin();
|
||||
mGeckoContentController->NotifyTransformBegin(
|
||||
ScrollableLayerGuid(mLayersId, mFrameMetrics.mPresShellId, mFrameMetrics.mScrollId));
|
||||
} else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
|
||||
mGeckoContentController->NotifyTransformEnd();
|
||||
mGeckoContentController->NotifyTransformEnd(
|
||||
ScrollableLayerGuid(mLayersId, mFrameMetrics.mPresShellId, mFrameMetrics.mScrollId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ public:
|
||||
* the apzc is modifying the view, including panning, zooming, and
|
||||
* fling.
|
||||
*/
|
||||
virtual void NotifyTransformBegin() {}
|
||||
virtual void NotifyTransformEnd() {}
|
||||
virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid) {}
|
||||
virtual void NotifyTransformEnd(const ScrollableLayerGuid& aGuid) {}
|
||||
|
||||
GeckoContentController() {}
|
||||
virtual ~GeckoContentController() {}
|
||||
|
@ -279,6 +279,10 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
|
||||
common.stickyScrollRangeOuter(),
|
||||
common.stickyScrollRangeInner());
|
||||
}
|
||||
if (common.isScrollbar()) {
|
||||
layer->SetScrollbarData(common.scrollbarTargetContainerId(),
|
||||
static_cast<Layer::ScrollDirection>(common.scrollbarDirection()));
|
||||
}
|
||||
if (PLayerParent* maskLayer = common.maskLayerParent()) {
|
||||
layer->SetMaskLayer(cast(maskLayer)->AsLayer());
|
||||
} else {
|
||||
|
@ -197,6 +197,9 @@ struct CommonLayerAttributes {
|
||||
uint64_t stickyScrollContainerId;
|
||||
LayerRect stickyScrollRangeOuter;
|
||||
LayerRect stickyScrollRangeInner;
|
||||
bool isScrollbar;
|
||||
uint64_t scrollbarTargetContainerId;
|
||||
uint32_t scrollbarDirection;
|
||||
nullable PLayer maskLayer;
|
||||
// Animated colors will only honored for ColorLayers.
|
||||
Animation[] animations;
|
||||
|
@ -528,6 +528,11 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies, bool
|
||||
common.stickyScrollRangeOuter() = mutant->GetStickyScrollRangeOuter();
|
||||
common.stickyScrollRangeInner() = mutant->GetStickyScrollRangeInner();
|
||||
}
|
||||
common.isScrollbar() = mutant->GetIsScrollbar();
|
||||
if (mutant->GetIsScrollbar()) {
|
||||
common.scrollbarTargetContainerId() = mutant->GetScrollbarTargetContainerId();
|
||||
common.scrollbarDirection() = mutant->GetScrollbarDirection();
|
||||
}
|
||||
if (Layer* maskLayer = mutant->GetMaskLayer()) {
|
||||
common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
|
||||
} else {
|
||||
|
@ -3259,9 +3259,10 @@ bool nsDisplayBlendContainer::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplay
|
||||
|
||||
nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
|
||||
nsIFrame* aFrame, nsDisplayList* aList,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags, ViewID aScrollTarget)
|
||||
: nsDisplayWrapList(aBuilder, aFrame, aList)
|
||||
, mFlags(aFlags) {
|
||||
, mFlags(aFlags)
|
||||
, mScrollTarget(aScrollTarget) {
|
||||
MOZ_COUNT_CTOR(nsDisplayOwnLayer);
|
||||
}
|
||||
|
||||
@ -3279,6 +3280,12 @@ nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||
nsRefPtr<ContainerLayer> layer = aManager->GetLayerBuilder()->
|
||||
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
|
||||
aContainerParameters, nullptr);
|
||||
if (mFlags & VERTICAL_SCROLLBAR) {
|
||||
layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::VERTICAL);
|
||||
}
|
||||
if (mFlags & HORIZONTAL_SCROLLBAR) {
|
||||
layer->SetScrollbarData(mScrollTarget, Layer::ScrollDirection::HORIZONTAL);
|
||||
}
|
||||
|
||||
if (mFlags & GENERATE_SUBDOC_INVALIDATIONS) {
|
||||
mFrame->PresContext()->SetNotifySubDocInvalidationData(layer);
|
||||
|
@ -2575,16 +2575,22 @@ public:
|
||||
* nsDisplayOwnLayer constructor flags
|
||||
*/
|
||||
enum {
|
||||
GENERATE_SUBDOC_INVALIDATIONS = 0x01
|
||||
GENERATE_SUBDOC_INVALIDATIONS = 0x01,
|
||||
VERTICAL_SCROLLBAR = 0x02,
|
||||
HORIZONTAL_SCROLLBAR = 0x04
|
||||
};
|
||||
|
||||
/**
|
||||
* @param aFlags GENERATE_SUBDOC_INVALIDATIONS :
|
||||
* Add UserData to the created ContainerLayer, so that invalidations
|
||||
* for this layer are send to our nsPresContext.
|
||||
* @param aScrollTarget when VERTICAL_SCROLLBAR or HORIZONTAL_SCROLLBAR
|
||||
* is set in the flags, this parameter should be the ViewID of the
|
||||
* scrollable content this scrollbar is for.
|
||||
*/
|
||||
nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
|
||||
nsDisplayList* aList, uint32_t aFlags = 0);
|
||||
nsDisplayList* aList, uint32_t aFlags = 0,
|
||||
ViewID aScrollTarget = mozilla::layers::FrameMetrics::NULL_SCROLL_ID);
|
||||
#ifdef NS_BUILD_REFCNT_LOGGING
|
||||
virtual ~nsDisplayOwnLayer();
|
||||
#endif
|
||||
@ -2606,6 +2612,7 @@ public:
|
||||
NS_DISPLAY_DECL_NAME("OwnLayer", TYPE_OWN_LAYER)
|
||||
private:
|
||||
uint32_t mFlags;
|
||||
ViewID mScrollTarget;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2058,13 +2058,15 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange)
|
||||
|
||||
static void
|
||||
AppendToTop(nsDisplayListBuilder* aBuilder, nsDisplayList* aDest,
|
||||
nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer)
|
||||
nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
|
||||
uint32_t aFlags, mozilla::layers::FrameMetrics::ViewID aScrollTargetId)
|
||||
{
|
||||
if (aSource->IsEmpty())
|
||||
return;
|
||||
if (aOwnLayer) {
|
||||
aDest->AppendNewToTop(
|
||||
new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource));
|
||||
new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource,
|
||||
aFlags, aScrollTargetId));
|
||||
} else {
|
||||
aDest->AppendToTop(aSource);
|
||||
}
|
||||
@ -2113,6 +2115,10 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
|
||||
scrollParts.AppendElement(kid);
|
||||
}
|
||||
|
||||
mozilla::layers::FrameMetrics::ViewID scrollTargetId = aCreateLayer
|
||||
? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
|
||||
: mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
|
||||
|
||||
scrollParts.Sort(HoveredStateComparator());
|
||||
|
||||
for (uint32_t i = 0; i < scrollParts.Length(); ++i) {
|
||||
@ -2129,11 +2135,19 @@ ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder* aBuilder,
|
||||
nsDisplayList* dest = appendToPositioned ?
|
||||
aLists.PositionedDescendants() : aLists.BorderBackground();
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (scrollParts[i] == mVScrollbarBox) {
|
||||
flags |= nsDisplayOwnLayer::VERTICAL_SCROLLBAR;
|
||||
}
|
||||
if (scrollParts[i] == mHScrollbarBox) {
|
||||
flags |= nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR;
|
||||
}
|
||||
|
||||
// DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into
|
||||
// partList.PositionedDescendants().
|
||||
::AppendToTop(aBuilder, dest,
|
||||
partList.PositionedDescendants(), scrollParts[i],
|
||||
aCreateLayer);
|
||||
aCreateLayer, flags, scrollTargetId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2425,6 +2439,12 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
scrolledContent.MoveTo(aLists);
|
||||
|
||||
// Now display overlay scrollbars and the resizer, if we have one.
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// TODO: only layerize the overlay scrollbars if this scrollframe can be
|
||||
// panned asynchronously. For now just always layerize on B2G because.
|
||||
// that's where we want the layerized scrollbars
|
||||
createLayersForScrollbars = true;
|
||||
#endif
|
||||
AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, createLayersForScrollbars,
|
||||
true);
|
||||
}
|
||||
|
@ -615,6 +615,36 @@ public:
|
||||
return mHaveZoomConstraints;
|
||||
}
|
||||
|
||||
virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (MessageLoop::current() != mUILoop) {
|
||||
mUILoop->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &RemoteContentController::NotifyTransformBegin,
|
||||
aGuid));
|
||||
return;
|
||||
}
|
||||
if (mRenderFrame) {
|
||||
TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
|
||||
browser->NotifyTransformBegin(aGuid.mScrollId);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void NotifyTransformEnd(const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (MessageLoop::current() != mUILoop) {
|
||||
mUILoop->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &RemoteContentController::NotifyTransformEnd,
|
||||
aGuid));
|
||||
return;
|
||||
}
|
||||
if (mRenderFrame) {
|
||||
TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
|
||||
browser->NotifyTransformEnd(aGuid.mScrollId);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void DoRequestContentRepaint(const FrameMetrics& aFrameMetrics)
|
||||
{
|
||||
|
@ -429,7 +429,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
mAboutHomeStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_ABOUTHOME");
|
||||
mAboutHomeStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_ABOUTHOME");
|
||||
|
||||
final Intent intent = getIntent();
|
||||
|
||||
|
@ -1153,8 +1153,8 @@ public abstract class GeckoApp
|
||||
}
|
||||
|
||||
// The clock starts...now. Better hurry!
|
||||
mJavaUiStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_JAVAUI");
|
||||
mGeckoReadyStartupTimer = new Telemetry.Timer("FENNEC_STARTUP_TIME_GECKOREADY");
|
||||
mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI");
|
||||
mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY");
|
||||
|
||||
Intent intent = getIntent();
|
||||
String args = intent.getStringExtra("args");
|
||||
|
@ -43,7 +43,7 @@ public class GeckoEvent {
|
||||
private static final String LOGTAG = "GeckoEvent";
|
||||
|
||||
// Make sure to keep these values in sync with the enum in
|
||||
// AndroidGeckoEvent in widget/android/AndroidJavaWrapper.h
|
||||
// AndroidGeckoEvent in widget/android/AndroidJavaWrappers.h
|
||||
@JNITarget
|
||||
private enum NativeGeckoEvent {
|
||||
NATIVE_POKE(0),
|
||||
@ -76,13 +76,16 @@ public class GeckoEvent {
|
||||
TELEMETRY_HISTOGRAM_ADD(37),
|
||||
PREFERENCES_OBSERVE(39),
|
||||
PREFERENCES_GET(40),
|
||||
PREFERENCES_REMOVE_OBSERVERS(41);
|
||||
PREFERENCES_REMOVE_OBSERVERS(41),
|
||||
TELEMETRY_UI_SESSION_START(42),
|
||||
TELEMETRY_UI_SESSION_STOP(43),
|
||||
TELEMETRY_UI_EVENT(44);
|
||||
|
||||
public final int value;
|
||||
|
||||
private NativeGeckoEvent(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -759,6 +762,30 @@ public class GeckoEvent {
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createTelemetryUISessionStartEvent(String session, long timestamp) {
|
||||
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.TELEMETRY_UI_SESSION_START);
|
||||
event.mCharacters = session;
|
||||
event.mTime = timestamp;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createTelemetryUISessionStopEvent(String session, String reason, long timestamp) {
|
||||
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.TELEMETRY_UI_SESSION_STOP);
|
||||
event.mCharacters = session;
|
||||
event.mCharactersExtra = reason;
|
||||
event.mTime = timestamp;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createTelemetryUIEvent(String action, String method, long timestamp, String extras) {
|
||||
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.TELEMETRY_UI_EVENT);
|
||||
event.mData = action;
|
||||
event.mCharacters = method;
|
||||
event.mCharactersExtra = extras;
|
||||
event.mTime = timestamp;
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setAckNeeded(boolean ackNeeded) {
|
||||
mAckNeeded = ackNeeded;
|
||||
}
|
||||
|
@ -411,10 +411,13 @@ public class Tabs implements GeckoEventListener {
|
||||
message.getInt("parentId"),
|
||||
message.getString("title"),
|
||||
message.getBoolean("isPrivate"));
|
||||
|
||||
// If we added the tab as a stub, we should have already
|
||||
// selected it, so ignore this flag for stubbed tabs.
|
||||
if (message.getBoolean("selected"))
|
||||
selectTab(id);
|
||||
}
|
||||
|
||||
if (message.getBoolean("selected"))
|
||||
selectTab(id);
|
||||
if (message.getBoolean("delayLoad"))
|
||||
tab.setState(Tab.STATE_DELAYED);
|
||||
if (message.getBoolean("desktopMode"))
|
||||
|
@ -5,31 +5,52 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* All telemetry times are relative to one of two clocks:
|
||||
*
|
||||
* * Real time since the device was booted, including deep sleep. Use this
|
||||
* as a substitute for wall clock.
|
||||
* * Uptime since the device was booted, excluding deep sleep. Use this to
|
||||
* avoid timing a user activity when their phone is in their pocket!
|
||||
*
|
||||
* The majority of methods in this class are defined in terms of real time.
|
||||
*/
|
||||
@RobocopTarget
|
||||
public class Telemetry {
|
||||
private static final String LOGTAG = "Telemetry";
|
||||
|
||||
public static long uptime() {
|
||||
return SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
public static long realtime() {
|
||||
return SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
// Define new histograms in:
|
||||
// toolkit/components/telemetry/Histograms.json
|
||||
public static void HistogramAdd(String name,
|
||||
int value) {
|
||||
GeckoEvent event =
|
||||
GeckoEvent.createTelemetryHistogramAddEvent(name, value);
|
||||
public static void HistogramAdd(String name, int value) {
|
||||
GeckoEvent event = GeckoEvent.createTelemetryHistogramAddEvent(name, value);
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
public static class Timer {
|
||||
private long mStartTime;
|
||||
private String mName;
|
||||
private boolean mHasFinished;
|
||||
public abstract static class Timer {
|
||||
private final long mStartTime;
|
||||
private final String mName;
|
||||
|
||||
private volatile boolean mHasFinished = false;
|
||||
private volatile long mElapsed = -1;
|
||||
|
||||
protected abstract long now();
|
||||
|
||||
public Timer(String name) {
|
||||
mName = name;
|
||||
mStartTime = SystemClock.uptimeMillis();
|
||||
mHasFinished = false;
|
||||
mStartTime = now();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
@ -44,17 +65,76 @@ public class Telemetry {
|
||||
// Only the first stop counts.
|
||||
if (mHasFinished) {
|
||||
return;
|
||||
} else {
|
||||
mHasFinished = true;
|
||||
}
|
||||
|
||||
final long elapsed = SystemClock.uptimeMillis() - mStartTime;
|
||||
mElapsed = elapsed;
|
||||
if (elapsed < Integer.MAX_VALUE) {
|
||||
HistogramAdd(mName, (int)(elapsed));
|
||||
} else {
|
||||
Log.e(LOGTAG, "Duration of " + elapsed + " ms is too long to add to histogram.");
|
||||
mHasFinished = true;
|
||||
|
||||
final long elapsed = now() - mStartTime;
|
||||
if (elapsed < 0) {
|
||||
Log.e(LOGTAG, "Current time less than start time -- clock shenanigans?");
|
||||
return;
|
||||
}
|
||||
|
||||
mElapsed = elapsed;
|
||||
if (elapsed > Integer.MAX_VALUE) {
|
||||
Log.e(LOGTAG, "Duration of " + elapsed + "ms is too great to add to histogram.");
|
||||
return;
|
||||
}
|
||||
|
||||
HistogramAdd(mName, (int)(elapsed));
|
||||
}
|
||||
}
|
||||
|
||||
public static class RealtimeTimer extends Timer {
|
||||
public RealtimeTimer(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long now() {
|
||||
return Telemetry.realtime();
|
||||
}
|
||||
}
|
||||
|
||||
public static class UptimeTimer extends Timer {
|
||||
public UptimeTimer(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long now() {
|
||||
return Telemetry.uptime();
|
||||
}
|
||||
}
|
||||
|
||||
public static void startUISession(String sessionName) {
|
||||
GeckoEvent event = GeckoEvent.createTelemetryUISessionStartEvent(sessionName, realtime());
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
public static void stopUISession(String sessionName, String reason) {
|
||||
GeckoEvent event = GeckoEvent.createTelemetryUISessionStopEvent(sessionName, reason, realtime());
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
public static void sendUIEvent(String action, String method, long timestamp, String extras) {
|
||||
GeckoEvent event = GeckoEvent.createTelemetryUIEvent(action, method, timestamp, extras);
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
public static void sendUIEvent(String action, String method, long timestamp) {
|
||||
sendUIEvent(action, method, timestamp, null);
|
||||
}
|
||||
|
||||
public static void sendUIEvent(String action, String method, String extras) {
|
||||
sendUIEvent(action, method, realtime(), extras);
|
||||
}
|
||||
|
||||
public static void sendUIEvent(String action, String method) {
|
||||
sendUIEvent(action, method, realtime(), null);
|
||||
}
|
||||
|
||||
public static void sendUIEvent(String action) {
|
||||
sendUIEvent(action, null, realtime(), null);
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ public class StringHelper {
|
||||
// Privacy
|
||||
public static final String TRACKING_LABEL = "Tracking";
|
||||
public static final String COOKIES_LABEL = "Cookies";
|
||||
public static final String REMEMBER_PASSWORDS_LABEL = "Remeber passwords";
|
||||
public static final String REMEMBER_PASSWORDS_LABEL = "Remember passwords";
|
||||
public static final String MASTER_PASWSWORD_LABEL = "Use master password";
|
||||
public static final String CLEAR_PRIVATE_DATA_LABEL = "Clear private data";
|
||||
|
||||
|
@ -23,6 +23,24 @@ import android.view.View;
|
||||
* A class representing any interactions that take place on the Awesomescreen.
|
||||
*/
|
||||
public class AboutHomeComponent extends BaseComponent {
|
||||
// TODO: Having a specific ordering of pages is prone to fail and thus temporary.
|
||||
// Hopefully the work in bug 940565 will alleviate the need for these enums.
|
||||
// Explicit ordering of HomePager pages on a phone.
|
||||
private enum PhonePage {
|
||||
HISTORY,
|
||||
TOP_SITES,
|
||||
BOOKMARKS,
|
||||
READING_LIST
|
||||
}
|
||||
|
||||
// Explicit ordering of HomePager pages on a tablet.
|
||||
private enum TabletPage {
|
||||
TOP_SITES,
|
||||
BOOKMARKS,
|
||||
READING_LIST,
|
||||
HISTORY
|
||||
}
|
||||
|
||||
public AboutHomeComponent(final UITestContext testContext) {
|
||||
super(testContext);
|
||||
}
|
||||
@ -34,17 +52,7 @@ public class AboutHomeComponent extends BaseComponent {
|
||||
public AboutHomeComponent assertCurrentPage(final Page expectedPage) {
|
||||
assertVisible();
|
||||
|
||||
// TODO: A "PhonePage" and "TabletPage" enum should be set explicitly or decided
|
||||
// dynamically, likely with the work done in bug 940565. The current solution should only
|
||||
// be temporary.
|
||||
int expectedPageIndex = expectedPage.ordinal();
|
||||
if (DeviceHelper.isTablet()) {
|
||||
// Left circular shift Page enum since the History tab is moved to the rightmost.
|
||||
expectedPageIndex -= 1;
|
||||
expectedPageIndex =
|
||||
(expectedPageIndex >= 0) ? expectedPageIndex : Page.values().length - 1;
|
||||
}
|
||||
|
||||
final int expectedPageIndex = getPageIndexForDevice(expectedPage.ordinal());
|
||||
assertEquals("The current HomePager page is " + expectedPage,
|
||||
expectedPageIndex, getHomePagerView().getCurrentItem());
|
||||
return this;
|
||||
@ -64,11 +72,13 @@ public class AboutHomeComponent extends BaseComponent {
|
||||
|
||||
// TODO: Take specific page as parameter rather than swipe in a direction?
|
||||
public AboutHomeComponent swipeToPageOnRight() {
|
||||
mTestContext.dumpLog("Swiping to the page on the right.");
|
||||
swipe(Solo.LEFT);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AboutHomeComponent swipeToPageOnLeft() {
|
||||
mTestContext.dumpLog("Swiping to the page on the left.");
|
||||
swipe(Solo.RIGHT);
|
||||
return this;
|
||||
}
|
||||
@ -94,7 +104,13 @@ public class AboutHomeComponent extends BaseComponent {
|
||||
}
|
||||
|
||||
private void waitForPageIndex(final int expectedIndex) {
|
||||
final String pageName = Page.values()[expectedIndex].toString();
|
||||
final String pageName;
|
||||
if (DeviceHelper.isTablet()) {
|
||||
pageName = TabletPage.values()[expectedIndex].name();
|
||||
} else {
|
||||
pageName = PhonePage.values()[expectedIndex].name();
|
||||
}
|
||||
|
||||
WaitHelper.waitFor("HomePager " + pageName + " page", new Condition() {
|
||||
@Override
|
||||
public boolean isSatisfied() {
|
||||
@ -102,4 +118,15 @@ public class AboutHomeComponent extends BaseComponent {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page index in the device specific Page enum for the given index in the
|
||||
* HomePager.Page enum.
|
||||
*/
|
||||
private int getPageIndexForDevice(final int pageIndex) {
|
||||
final String pageName = Page.values()[pageIndex].name();
|
||||
final Class devicePageEnum =
|
||||
DeviceHelper.isTablet() ? TabletPage.class : PhonePage.class;
|
||||
return Enum.valueOf(devicePageEnum, pageName).ordinal();
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import android.app.Activity;
|
||||
* (e.g. clicking the toolbar, entering a url, and waiting for page load).
|
||||
*/
|
||||
public abstract class BaseComponent {
|
||||
private final UITestContext mTestContext;
|
||||
protected final UITestContext mTestContext;
|
||||
protected final Activity mActivity;
|
||||
protected final Solo mSolo;
|
||||
protected final Actions mActions;
|
||||
@ -30,8 +30,4 @@ public abstract class BaseComponent {
|
||||
mSolo = mTestContext.getSolo();
|
||||
mActions = mTestContext.getActions();
|
||||
}
|
||||
|
||||
protected UITestContext getTestContext() {
|
||||
return mTestContext;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ skip-if = processor == "x86"
|
||||
[testBrowserProvider]
|
||||
[testBrowserSearchVisibility]
|
||||
[testClearPrivateData]
|
||||
[testDeviceSearchEngine]
|
||||
[testDistribution]
|
||||
[testDoorHanger]
|
||||
[testFindInPage]
|
||||
@ -32,14 +31,11 @@ skip-if = processor == "x86"
|
||||
skip-if = processor == "x86"
|
||||
[testInputUrlBar]
|
||||
[testJarReader]
|
||||
[testJNI]
|
||||
[testLinkContextMenu]
|
||||
[testLoad]
|
||||
[testMailToContextMenu]
|
||||
[testMasterPassword]
|
||||
# [testMozPay] # see bug 945675
|
||||
[testNewTab]
|
||||
[testOrderedBroadcast]
|
||||
[testOverscroll]
|
||||
[testPanCorrectness]
|
||||
# disabled on x86 only; bug 927476
|
||||
@ -58,7 +54,6 @@ skip-if = processor == "x86"
|
||||
[testSessionOOMSave]
|
||||
[testSessionOOMRestore]
|
||||
[testSettingsMenuItems]
|
||||
[testSharedPreferences]
|
||||
# [testShareLink] # see bug 915897
|
||||
[testSystemPages]
|
||||
# disabled on x86 only; bug 907383
|
||||
@ -66,6 +61,13 @@ skip-if = processor == "x86"
|
||||
# [testThumbnails] # see bug 813107
|
||||
# [testVkbOverlap] # see bug 907274
|
||||
|
||||
# Using JavascriptTest
|
||||
[testDeviceSearchEngine]
|
||||
[testJNI]
|
||||
# [testMozPay] # see bug 945675
|
||||
[testOrderedBroadcast]
|
||||
[testSharedPreferences]
|
||||
[testUITelemetry]
|
||||
|
||||
# Used for Talos, please don't use in mochitest
|
||||
#[testPan]
|
||||
|
38
mobile/android/base/tests/testUITelemetry.java
Normal file
38
mobile/android/base/tests/testUITelemetry.java
Normal file
@ -0,0 +1,38 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class testUITelemetry extends JavascriptTest {
|
||||
public testUITelemetry() {
|
||||
super("testUITelemetry.js");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testJavascript() throws Exception {
|
||||
blockForGeckoReady();
|
||||
Log.i("GeckoTest", "Adding telemetry events.");
|
||||
|
||||
try {
|
||||
Telemetry.sendUIEvent("enone", "method0");
|
||||
Telemetry.startUISession("foo");
|
||||
Telemetry.sendUIEvent("efoo", "method1");
|
||||
Telemetry.startUISession("foo");
|
||||
Telemetry.sendUIEvent("efoo", "method2");
|
||||
Telemetry.startUISession("bar");
|
||||
Telemetry.sendUIEvent("efoobar", "method3", "foobarextras");
|
||||
Telemetry.stopUISession("foo", "reasonfoo");
|
||||
Telemetry.sendUIEvent("ebar", "method4", "barextras");
|
||||
Telemetry.stopUISession("bar", "reasonbar");
|
||||
Telemetry.stopUISession("bar", "reasonbar2");
|
||||
Telemetry.sendUIEvent("enone", "method5");
|
||||
} catch (Exception e) {
|
||||
Log.e("GeckoTest", "Oops.", e);
|
||||
}
|
||||
|
||||
Log.i("GeckoTest", "Running remaining JS test code.");
|
||||
super.testJavascript();
|
||||
}
|
||||
}
|
||||
|
63
mobile/android/base/tests/testUITelemetry.js
Normal file
63
mobile/android/base/tests/testUITelemetry.js
Normal file
@ -0,0 +1,63 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function do_check_array_eq(a1, a2) {
|
||||
do_check_eq(a1.length, a2.length);
|
||||
for (let i = 0; i < a1.length; ++i) {
|
||||
do_check_eq(a1[i], a2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
add_test(function test_telemetry_events() {
|
||||
let bridge = Components.classes["@mozilla.org/android/bridge;1"]
|
||||
.getService(Components.interfaces.nsIAndroidBridge);
|
||||
let obsXPCOM = bridge.browserApp.getUITelemetryObserver();
|
||||
do_check_true(!!obsXPCOM);
|
||||
|
||||
let obs = obsXPCOM.wrappedJSObject;
|
||||
do_check_true(!!obs);
|
||||
|
||||
let measurements = obs.getUIMeasurements();
|
||||
|
||||
let expected = [
|
||||
["event", "enone", "method0", [], null],
|
||||
["event", "efoo", "method1", ["foo"], null],
|
||||
["event", "efoo", "method2", ["foo"], null],
|
||||
["event", "efoobar", "method3", ["foo", "bar"], "foobarextras"],
|
||||
["session", "foo", "reasonfoo"],
|
||||
["event", "ebar", "method4", ["bar"], "barextras"],
|
||||
["session", "bar", "reasonbar"],
|
||||
["event", "enone", "method5", [], null],
|
||||
];
|
||||
|
||||
do_check_eq(expected.length, measurements.length);
|
||||
|
||||
for (let i = 0; i < measurements.length; ++i) {
|
||||
let m = measurements[i];
|
||||
|
||||
let type = m[0];
|
||||
if (type == "event") {
|
||||
let [type, action, method, sessions, extras] = expected[i];
|
||||
do_check_eq(m.action, action);
|
||||
do_check_eq(m.method, method);
|
||||
do_check_array_eq(m.sessions, sessions);
|
||||
do_check_eq(m.extras, extras);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == "session") {
|
||||
let [type, name, reason] = expected[i];
|
||||
do_check_eq(m.name, name);
|
||||
do_check_eq(m.reason, method);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
run_next_test();
|
@ -338,7 +338,6 @@ var BrowserApp = {
|
||||
DesktopUserAgent.init();
|
||||
Distribution.init();
|
||||
Tabs.init();
|
||||
UITelemetry.init();
|
||||
#ifdef ACCESSIBILITY
|
||||
AccessFu.attach(window);
|
||||
#endif
|
||||
@ -1530,6 +1529,10 @@ var BrowserApp = {
|
||||
return this.getTabForId(tabId);
|
||||
},
|
||||
|
||||
getUITelemetryObserver: function() {
|
||||
return UITelemetry;
|
||||
},
|
||||
|
||||
getPreferences: function getPreferences(requestId, prefNames, count) {
|
||||
this.handlePreferencesRequest(requestId, prefNames, false);
|
||||
},
|
||||
@ -2671,8 +2674,9 @@ Tab.prototype = {
|
||||
|
||||
// When the tab is stubbed from Java, there's a window between the stub
|
||||
// creation and the tab creation in Gecko where the stub could be removed
|
||||
// (which is easiest to hit during startup). We need to differentiate
|
||||
// between tab stubs from Java and new tabs from Gecko to prevent breakage.
|
||||
// or the selected tab can change (which is easiest to hit during startup).
|
||||
// To prevent these races, we need to differentiate between tab stubs from
|
||||
// Java and new tabs from Gecko.
|
||||
let stub = false;
|
||||
|
||||
if (!aParams.zombifying) {
|
||||
|
@ -21,8 +21,8 @@ ContentPermissionPrompt.prototype = {
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
|
||||
|
||||
handleExistingPermission: function handleExistingPermission(request, type, isApp) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
|
||||
handleExistingPermission: function handleExistingPermission(request, isApp) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type);
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
return true;
|
||||
@ -32,7 +32,7 @@ ContentPermissionPrompt.prototype = {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[type])) {
|
||||
if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[request.type])) {
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
@ -62,16 +62,8 @@ ContentPermissionPrompt.prototype = {
|
||||
prompt: function(request) {
|
||||
let isApp = request.principal.appId !== Ci.nsIScriptSecurityManager.NO_APP_ID && request.principal.appId !== Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID;
|
||||
|
||||
// Only allow exactly one permission rquest here.
|
||||
let types = request.types.QueryInterface(Ci.nsIArray);
|
||||
if (types.length != 1) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
|
||||
|
||||
// Returns true if the request was handled
|
||||
if (this.handleExistingPermission(request, perm.type, isApp))
|
||||
if (this.handleExistingPermission(request, isApp))
|
||||
return;
|
||||
|
||||
let chromeWin = this.getChromeForRequest(request);
|
||||
@ -80,17 +72,17 @@ ContentPermissionPrompt.prototype = {
|
||||
return;
|
||||
|
||||
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
let entityName = kEntities[perm.type];
|
||||
let entityName = kEntities[request.type];
|
||||
|
||||
let buttons = [{
|
||||
label: browserBundle.GetStringFromName(entityName + ".allow"),
|
||||
callback: function(aChecked) {
|
||||
// If the user checked "Don't ask again", make a permanent exception
|
||||
if (aChecked) {
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
} else if (isApp || entityName == "desktopNotification") {
|
||||
// Otherwise allow the permission for the current session (if the request comes from an app or if it's a desktop-notification request)
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
}
|
||||
|
||||
request.allow();
|
||||
@ -101,7 +93,7 @@ ContentPermissionPrompt.prototype = {
|
||||
callback: function(aChecked) {
|
||||
// If the user checked "Don't ask again", make a permanent exception
|
||||
if (aChecked)
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
|
||||
request.cancel();
|
||||
}
|
||||
|
@ -229,8 +229,9 @@ void RTSPSource::performPause() {
|
||||
for (size_t i = 0; i < mTracks.size(); ++i) {
|
||||
TrackInfo *info = &mTracks.editItemAt(i);
|
||||
info->mLatestPausedUnit = 0;
|
||||
mLatestPausedUnit = 0;
|
||||
}
|
||||
mLatestPausedUnit = 0;
|
||||
|
||||
mState = PAUSING;
|
||||
mHandler->pause();
|
||||
}
|
||||
@ -259,8 +260,8 @@ void RTSPSource::performSeek(int64_t seekTimeUs) {
|
||||
for (size_t i = 0; i < mTracks.size(); ++i) {
|
||||
TrackInfo *info = &mTracks.editItemAt(i);
|
||||
info->mLatestPausedUnit = 0;
|
||||
mLatestPausedUnit = 0;
|
||||
}
|
||||
mLatestPausedUnit = 0;
|
||||
|
||||
LOGI("performSeek: %llu", seekTimeUs);
|
||||
mState = SEEKING;
|
||||
@ -328,6 +329,14 @@ void RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
|
||||
case RtspConnectionHandler::kWhatSeekDone:
|
||||
{
|
||||
mState = PLAYING;
|
||||
// Even if we have reset mLatestPausedUnit in performSeek(),
|
||||
// it's still possible that kWhatPausedDone event may arrive
|
||||
// because of previous performPause() command.
|
||||
for (size_t i = 0; i < mTracks.size(); ++i) {
|
||||
TrackInfo *info = &mTracks.editItemAt(i);
|
||||
info->mLatestPausedUnit = 0;
|
||||
}
|
||||
mLatestPausedUnit = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -616,8 +616,9 @@ class MarionetteTestRunner(object):
|
||||
manifest = TestManifest()
|
||||
manifest.read(filepath)
|
||||
|
||||
all_tests = manifest.active_tests(disabled=False)
|
||||
manifest_tests = manifest.active_tests(disabled=False,
|
||||
all_tests = manifest.active_tests(exists=False, disabled=False)
|
||||
manifest_tests = manifest.active_tests(exists=False,
|
||||
disabled=False,
|
||||
device=self.device,
|
||||
app=self.appName)
|
||||
skip_tests = list(set([x['path'] for x in all_tests]) -
|
||||
|
@ -34,18 +34,9 @@ this.MockPermissionPrompt = {
|
||||
init: function() {
|
||||
this.reset();
|
||||
if (!registrar.isCIDRegistered(newClassID)) {
|
||||
try {
|
||||
oldClassID = registrar.contractIDToCID(CONTRACT_ID);
|
||||
oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
|
||||
} catch (ex) {
|
||||
oldClassID = "";
|
||||
oldFactory = null;
|
||||
dump("TEST-INFO | can't get permission prompt registered component, " +
|
||||
"assuming there is none");
|
||||
}
|
||||
if (oldFactory) {
|
||||
registrar.unregisterFactory(oldClassID, oldFactory);
|
||||
}
|
||||
oldClassID = registrar.contractIDToCID(CONTRACT_ID);
|
||||
oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
|
||||
registrar.unregisterFactory(oldClassID, oldFactory);
|
||||
registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory);
|
||||
}
|
||||
},
|
||||
@ -70,17 +61,14 @@ MockPermissionPromptInstance.prototype = {
|
||||
|
||||
prompt: function(request) {
|
||||
|
||||
let perms = request.types.QueryInterface(Ci.nsIArray);
|
||||
for (let idx = 0; idx < perms.length; idx++) {
|
||||
let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
|
||||
if (Services.perms.testExactPermissionFromPrincipal(
|
||||
request.principal, perm.type) != Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
this.promptResult = Services.perms.testExactPermissionFromPrincipal(request.principal,
|
||||
request.type);
|
||||
if (this.promptResult == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
}
|
||||
else {
|
||||
request.cancel();
|
||||
}
|
||||
|
||||
request.allow();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
[include:dom/apps/tests/unit/xpcshell.ini]
|
||||
[include:dom/mobilemessage/tests/xpcshell.ini]
|
||||
[include:dom/network/tests/unit_stats/xpcshell.ini]
|
||||
[include:dom/system/gonk/tests/xpcshell.ini]
|
||||
[include:toolkit/devtools/apps/tests/unit/xpcshell.ini]
|
||||
[include:toolkit/devtools/debugger/tests/unit/xpcshell.ini]
|
||||
|
@ -7,80 +7,78 @@
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"UITelemetry"
|
||||
"UITelemetry",
|
||||
];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
/**
|
||||
* UITelemetry is a helper JSM used to record UI specific telemetry events.
|
||||
*
|
||||
* It implements nsIUITelemetryObserver, defined in nsIAndroidBridge.idl.
|
||||
*/
|
||||
this.UITelemetry = {
|
||||
this.UITelemetry = Object.freeze({
|
||||
_activeSessions: {},
|
||||
_measurements: [],
|
||||
|
||||
measurements: [],
|
||||
|
||||
init: function init() {
|
||||
Services.obs.addObserver(this, "UITelemetry:Event", false);
|
||||
Services.obs.addObserver(this, "UITelemetry:Session", false);
|
||||
},
|
||||
|
||||
observe: function observe(aMessage, aTopic, aData) {
|
||||
switch(aTopic) {
|
||||
case "UITelemetry:Event":
|
||||
let args = JSON.parse(aData);
|
||||
this.addEvent(args.action, args.method, args.extras, args.timestamp);
|
||||
break;
|
||||
case "UITelemetry:Session":
|
||||
args = JSON.parse(aData);
|
||||
let sessionName = args.name;
|
||||
let timestamp = args.timestamp;
|
||||
if (args.state == "start") {
|
||||
this.startSession(sessionName, timestamp);
|
||||
} else if (args.state == "stop") {
|
||||
this.stopSession(sessionName, timestamp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* This exists exclusively for testing -- our events are not intended to
|
||||
* be retrieved via an XPCOM interface.
|
||||
*/
|
||||
get wrappedJSObject() {
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a single event described by an action, and the calling method. Optional
|
||||
* paramaters are extras and timestamp. The timestamp will be set here if it is
|
||||
* not passed in by the caller.
|
||||
* Holds the functions that provide UITelemetry's simple
|
||||
* measurements. Those functions are mapped to unique names,
|
||||
* and should be registered with addSimpleMeasureFunction.
|
||||
*/
|
||||
addEvent: function addEvent(aAction, aMethod, aExtras, aTimestamp) {
|
||||
let timestamp = aTimestamp || Date.now();
|
||||
_simpleMeasureFunctions: {},
|
||||
|
||||
/**
|
||||
* Adds a single event described by a timestamp, an action, and the calling
|
||||
* method.
|
||||
*
|
||||
* Optionally provide a string 'extras', which will be recorded as part of
|
||||
* the event.
|
||||
*
|
||||
* All extant sessions will be recorded by name for each event.
|
||||
*/
|
||||
addEvent: function(aAction, aMethod, aTimestamp, aExtras) {
|
||||
let sessions = Object.keys(this._activeSessions);
|
||||
let aEvent = {
|
||||
type: "event",
|
||||
action: aAction,
|
||||
method: aMethod,
|
||||
timestamp: timestamp
|
||||
sessions: sessions,
|
||||
timestamp: aTimestamp,
|
||||
};
|
||||
|
||||
if (aExtras) aEvent.extras = aExtras;
|
||||
this._logEvent(aEvent);
|
||||
},
|
||||
if (aExtras) {
|
||||
aEvent.extras = aExtras;
|
||||
}
|
||||
|
||||
activeSessions: {},
|
||||
this._recordEvent(aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Begins tracking a session by storing a timestamp for session start.
|
||||
*/
|
||||
startSession: function startSession(aName, aTimestamp) {
|
||||
let timestamp = aTimestamp || Date.now();
|
||||
if (this.activeSessions[aName]) {
|
||||
// Do not overwrite a previous event start if it already exsts.
|
||||
return;
|
||||
}
|
||||
this.activeSessions[aName] = timestamp;
|
||||
startSession: function(aName, aTimestamp) {
|
||||
if (this._activeSessions[aName]) {
|
||||
// Do not overwrite a previous event start if it already exists.
|
||||
return;
|
||||
}
|
||||
this._activeSessions[aName] = aTimestamp;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tracks the end of a session with a timestamp.
|
||||
*/
|
||||
stopSession: function stopSession(aName, aTimestamp) {
|
||||
let timestamp = aTimestamp || Date.now();
|
||||
let sessionStart = this.activeSessions[aName];
|
||||
stopSession: function(aName, aReason, aTimestamp) {
|
||||
let sessionStart = this._activeSessions[aName];
|
||||
delete this._activeSessions[aName];
|
||||
|
||||
if (!sessionStart) {
|
||||
Services.console.logStringMessage("UITelemetry error: no session [" + aName + "] to stop!");
|
||||
@ -90,24 +88,18 @@ this.UITelemetry = {
|
||||
let aEvent = {
|
||||
type: "session",
|
||||
name: aName,
|
||||
reason: aReason,
|
||||
start: sessionStart,
|
||||
end: timestamp
|
||||
end: aTimestamp,
|
||||
};
|
||||
|
||||
this._logEvent(aEvent);
|
||||
this._recordEvent(aEvent);
|
||||
},
|
||||
|
||||
_logEvent: function sendEvent(aEvent) {
|
||||
this.measurements.push(aEvent);
|
||||
_recordEvent: function(aEvent) {
|
||||
this._measurements.push(aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Holds the functions that provide UITelemety's simple
|
||||
* measurements. Those functions are mapped to unique names,
|
||||
* and should be registered with addSimpleMeasureFunction.
|
||||
*/
|
||||
_simpleMeasureFuncs: {},
|
||||
|
||||
/**
|
||||
* Called by TelemetryPing to populate the simple measurement
|
||||
* blob. This function will iterate over all functions added
|
||||
@ -116,8 +108,8 @@ this.UITelemetry = {
|
||||
*/
|
||||
getSimpleMeasures: function() {
|
||||
let result = {};
|
||||
for (let name in this._simpleMeasureFuncs) {
|
||||
result[name] = this._simpleMeasureFuncs[name]();
|
||||
for (let name in this._simpleMeasureFunctions) {
|
||||
result[name] = this._simpleMeasureFunctions[name]();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
@ -132,22 +124,22 @@ this.UITelemetry = {
|
||||
* registered for it.
|
||||
*/
|
||||
addSimpleMeasureFunction: function(aName, aFunction) {
|
||||
if (aName in this._simpleMeasureFuncs) {
|
||||
throw new Error("A simple measurement function is already registered for "
|
||||
+ aName);
|
||||
}
|
||||
if (!aFunction || typeof aFunction !== 'function') {
|
||||
throw new Error("A function must be passed as the second argument.");
|
||||
if (aName in this._simpleMeasureFunctions) {
|
||||
throw new Error("A simple measurement function is already registered for " + aName);
|
||||
}
|
||||
|
||||
this._simpleMeasureFuncs[aName] = aFunction;
|
||||
if (!aFunction || typeof aFunction !== 'function') {
|
||||
throw new Error("addSimpleMeasureFunction called with non-function argument.");
|
||||
}
|
||||
|
||||
this._simpleMeasureFunctions[aName] = aFunction;
|
||||
},
|
||||
|
||||
removeSimpleMeasureFunction: function(aName) {
|
||||
delete this._simpleMeasureFuncs[aName];
|
||||
delete this._simpleMeasureFunctions[aName];
|
||||
},
|
||||
|
||||
getUIMeasurements: function getUIMeasurements() {
|
||||
return this.measurements.slice();
|
||||
return this._measurements.slice();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -9,7 +9,8 @@ support-files =
|
||||
thumbnails_crash_content_helper.js
|
||||
thumbnails_update.sjs
|
||||
|
||||
[browser_thumbnails_background_crash.js]
|
||||
[browser_thumbnails_bg_crash_during_capture.js]
|
||||
[browser_thumbnails_bg_crash_while_idle.js]
|
||||
[browser_thumbnails_bg_basic.js]
|
||||
[browser_thumbnails_bg_queueing.js]
|
||||
[browser_thumbnails_bg_timeout.js]
|
||||
|
@ -1,162 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TEST_PAGE_URL = "http://mochi.test:8888/browser/toolkit/components/thumbnails/test/thumbnails_background.sjs";
|
||||
const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
|
||||
|
||||
const imports = {};
|
||||
Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", imports);
|
||||
Cu.import("resource://gre/modules/PageThumbs.jsm", imports);
|
||||
Cu.import("resource://gre/modules/Task.jsm", imports);
|
||||
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", imports);
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
Services.obs.addObserver(crashObserver, 'ipc:content-shutdown', false);
|
||||
|
||||
spawnNextTest();
|
||||
}
|
||||
|
||||
function spawnNextTest() {
|
||||
if (!tests.length) {
|
||||
Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
|
||||
is(numCrashes, 2, "saw 2 crashes from this test");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
let test = tests.shift();
|
||||
info("Running sub-test " + test.name);
|
||||
imports.Task.spawn(test).then(spawnNextTest, function onError(err) {
|
||||
ok(false, err);
|
||||
spawnNextTest();
|
||||
});
|
||||
}
|
||||
|
||||
let tests = [
|
||||
|
||||
function crashDuringCapture() {
|
||||
// make a good capture first - this ensures we have the <browser>
|
||||
let goodUrl = testPageURL();
|
||||
yield capture(goodUrl);
|
||||
let goodFile = fileForURL(goodUrl);
|
||||
ok(goodFile.exists(), "Thumbnail should be cached after capture: " + goodFile.path);
|
||||
goodFile.remove(false);
|
||||
// inject our content script.
|
||||
let mm = injectContentScript();
|
||||
// queue up 2 captures - the first has a wait, so this is the one that
|
||||
// will die. The second one should immediately capture after the crash.
|
||||
let waitUrl = testPageURL({ wait: 30000 });
|
||||
let deferred1 = capture(waitUrl);
|
||||
let deferred2 = capture(goodUrl);
|
||||
info("Crashing the thumbnail content process.");
|
||||
mm.sendAsyncMessage("thumbnails-test:crash");
|
||||
yield deferred1;
|
||||
let waitFile = fileForURL(waitUrl);
|
||||
ok(!waitFile.exists(), "Thumbnail should not have been saved due to the crash: " + waitFile.path);
|
||||
yield deferred2;
|
||||
ok(goodFile.exists(), "We should have recovered and completed the 2nd capture after the crash: " + goodFile.path);
|
||||
goodFile.remove(false);
|
||||
},
|
||||
|
||||
function crashWhileIdle() {
|
||||
// make a good capture first - this ensures we have the <browser>
|
||||
let goodUrl = testPageURL();
|
||||
yield capture(goodUrl);
|
||||
let goodFile = fileForURL(goodUrl);
|
||||
ok(goodFile.exists(), "Thumbnail should be cached after capture: " + goodFile.path);
|
||||
goodFile.remove(false);
|
||||
// inject our content script.
|
||||
let mm = injectContentScript();
|
||||
// the observer for the crashing process is basically async, so it's hard
|
||||
// to know when the <browser> has actually seen it. Easist is to just add
|
||||
// our own observer.
|
||||
let deferred = imports.Promise.defer();
|
||||
Services.obs.addObserver(function crashObserver() {
|
||||
Services.obs.removeObserver(crashObserver, "oop-frameloader-crashed");
|
||||
// spin the event loop to ensure the BPT observer was called.
|
||||
executeSoon(function() {
|
||||
// Now queue another capture and ensure it recovers.
|
||||
capture(goodUrl).then(() => {
|
||||
ok(goodFile.exists(), "We should have recovered and handled new capture requests: " + goodFile.path);
|
||||
goodFile.remove(false);
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
} , "oop-frameloader-crashed", false);
|
||||
|
||||
// Nothing is pending - crash the process.
|
||||
info("Crashing the thumbnail content process.");
|
||||
mm.sendAsyncMessage("thumbnails-test:crash");
|
||||
yield deferred.promise;
|
||||
},
|
||||
];
|
||||
|
||||
function injectContentScript() {
|
||||
let thumbnailBrowser = imports.BackgroundPageThumbs._thumbBrowser;
|
||||
let mm = thumbnailBrowser.messageManager;
|
||||
mm.loadFrameScript(TEST_CONTENT_HELPER, false);
|
||||
return mm;
|
||||
}
|
||||
|
||||
function capture(url, options) {
|
||||
let deferred = imports.Promise.defer();
|
||||
options = options || {};
|
||||
options.onDone = function onDone(capturedURL) {
|
||||
deferred.resolve(capturedURL);
|
||||
};
|
||||
imports.BackgroundPageThumbs.capture(url, options);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function fileForURL(url) {
|
||||
let path = imports.PageThumbsStorage.getFilePathForURL(url);
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
file.initWithPath(path);
|
||||
return file;
|
||||
}
|
||||
|
||||
function testPageURL(opts) {
|
||||
return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(opts || {}));
|
||||
}
|
||||
|
||||
// This observer is needed so we can clean up all evidence of the crash so
|
||||
// the testrunner thinks things are peachy.
|
||||
let numCrashes = 0;
|
||||
function crashObserver(subject, topic, data) {
|
||||
is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
|
||||
ok(subject instanceof Components.interfaces.nsIPropertyBag2,
|
||||
'Subject implements nsIPropertyBag2.');
|
||||
// we might see this called as the process terminates due to previous tests.
|
||||
// We are only looking for "abnormal" exits...
|
||||
if (!subject.hasKey("abnormal")) {
|
||||
info("This is a normal termination and isn't the one we are looking for...");
|
||||
return;
|
||||
}
|
||||
numCrashes++;
|
||||
|
||||
var dumpID;
|
||||
if ('nsICrashReporter' in Components.interfaces) {
|
||||
dumpID = subject.getPropertyAsAString('dumpID');
|
||||
ok(dumpID, "dumpID is present and not an empty string");
|
||||
}
|
||||
|
||||
if (dumpID) {
|
||||
var minidumpDirectory = getMinidumpDirectory();
|
||||
removeFile(minidumpDirectory, dumpID + '.dmp');
|
||||
removeFile(minidumpDirectory, dumpID + '.extra');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getMinidumpDirectory() {
|
||||
var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
|
||||
dir.append("minidumps");
|
||||
return dir;
|
||||
}
|
||||
function removeFile(directory, filename) {
|
||||
var file = directory.clone();
|
||||
file.append(filename);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function runTests() {
|
||||
let crashObserver = bgAddCrashObserver();
|
||||
|
||||
// make a good capture first - this ensures we have the <browser>
|
||||
let goodUrl = bgTestPageURL();
|
||||
yield bgCapture(goodUrl);
|
||||
ok(thumbnailExists(goodUrl), "Thumbnail should be cached after capture");
|
||||
removeThumbnail(goodUrl);
|
||||
|
||||
// inject our content script.
|
||||
let mm = bgInjectCrashContentScript();
|
||||
|
||||
// queue up 2 captures - the first has a wait, so this is the one that
|
||||
// will die. The second one should immediately capture after the crash.
|
||||
let waitUrl = bgTestPageURL({ wait: 30000 });
|
||||
let sawWaitUrlCapture = false;
|
||||
bgCapture(waitUrl, { onDone: () => {
|
||||
sawWaitUrlCapture = true;
|
||||
ok(!thumbnailExists(waitUrl), "Thumbnail should not have been saved due to the crash");
|
||||
}});
|
||||
bgCapture(goodUrl, { onDone: () => {
|
||||
ok(sawWaitUrlCapture, "waitUrl capture should have finished first");
|
||||
ok(thumbnailExists(goodUrl), "We should have recovered and completed the 2nd capture after the crash");
|
||||
removeThumbnail(goodUrl);
|
||||
// Test done.
|
||||
ok(crashObserver.crashed, "Saw a crash from this test");
|
||||
next();
|
||||
}});
|
||||
|
||||
info("Crashing the thumbnail content process.");
|
||||
mm.sendAsyncMessage("thumbnails-test:crash");
|
||||
yield true;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function runTests() {
|
||||
let crashObserver = bgAddCrashObserver();
|
||||
|
||||
// make a good capture first - this ensures we have the <browser>
|
||||
let goodUrl = bgTestPageURL();
|
||||
yield bgCapture(goodUrl);
|
||||
ok(thumbnailExists(goodUrl), "Thumbnail should be cached after capture");
|
||||
removeThumbnail(goodUrl);
|
||||
|
||||
// inject our content script.
|
||||
let mm = bgInjectCrashContentScript();
|
||||
|
||||
// the observer for the crashing process is basically async, so it's hard
|
||||
// to know when the <browser> has actually seen it. Easist is to just add
|
||||
// our own observer.
|
||||
Services.obs.addObserver(function onCrash() {
|
||||
Services.obs.removeObserver(onCrash, "oop-frameloader-crashed");
|
||||
// spin the event loop to ensure the BPT observer was called.
|
||||
executeSoon(function() {
|
||||
// Now queue another capture and ensure it recovers.
|
||||
bgCapture(goodUrl, { onDone: () => {
|
||||
ok(thumbnailExists(goodUrl), "We should have recovered and handled new capture requests");
|
||||
removeThumbnail(goodUrl);
|
||||
// Test done.
|
||||
ok(crashObserver.crashed, "Saw a crash from this test");
|
||||
next();
|
||||
}});
|
||||
});
|
||||
} , "oop-frameloader-crashed", false);
|
||||
|
||||
// Nothing is pending - crash the process.
|
||||
info("Crashing the thumbnail content process.");
|
||||
mm.sendAsyncMessage("thumbnails-test:crash");
|
||||
yield true;
|
||||
}
|
@ -312,7 +312,8 @@ function bgCaptureIfMissing(aURL, aOptions) {
|
||||
}
|
||||
|
||||
function bgCaptureWithMethod(aMethodName, aURL, aOptions = {}) {
|
||||
aOptions.onDone = next;
|
||||
if (!aOptions.onDone)
|
||||
aOptions.onDone = next;
|
||||
BackgroundPageThumbs[aMethodName](aURL, aOptions);
|
||||
}
|
||||
|
||||
@ -320,3 +321,57 @@ function bgTestPageURL(aOpts = {}) {
|
||||
let TEST_PAGE_URL = "http://mochi.test:8888/browser/toolkit/components/thumbnails/test/thumbnails_background.sjs";
|
||||
return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(aOpts));
|
||||
}
|
||||
|
||||
function bgAddCrashObserver() {
|
||||
let crashed = false;
|
||||
Services.obs.addObserver(function crashObserver(subject, topic, data) {
|
||||
is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
|
||||
ok(subject instanceof Components.interfaces.nsIPropertyBag2,
|
||||
'Subject implements nsIPropertyBag2.');
|
||||
// we might see this called as the process terminates due to previous tests.
|
||||
// We are only looking for "abnormal" exits...
|
||||
if (!subject.hasKey("abnormal")) {
|
||||
info("This is a normal termination and isn't the one we are looking for...");
|
||||
return;
|
||||
}
|
||||
Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
|
||||
crashed = true;
|
||||
|
||||
var dumpID;
|
||||
if ('nsICrashReporter' in Components.interfaces) {
|
||||
dumpID = subject.getPropertyAsAString('dumpID');
|
||||
ok(dumpID, "dumpID is present and not an empty string");
|
||||
}
|
||||
|
||||
if (dumpID) {
|
||||
var minidumpDirectory = getMinidumpDirectory();
|
||||
removeFile(minidumpDirectory, dumpID + '.dmp');
|
||||
removeFile(minidumpDirectory, dumpID + '.extra');
|
||||
}
|
||||
}, 'ipc:content-shutdown', false);
|
||||
return {
|
||||
get crashed() crashed
|
||||
};
|
||||
}
|
||||
|
||||
function bgInjectCrashContentScript() {
|
||||
const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
|
||||
let thumbnailBrowser = BackgroundPageThumbs._thumbBrowser;
|
||||
let mm = thumbnailBrowser.messageManager;
|
||||
mm.loadFrameScript(TEST_CONTENT_HELPER, false);
|
||||
return mm;
|
||||
}
|
||||
|
||||
function getMinidumpDirectory() {
|
||||
var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
|
||||
dir.append("minidumps");
|
||||
return dir;
|
||||
}
|
||||
|
||||
function removeFile(directory, filename) {
|
||||
var file = directory.clone();
|
||||
file.append(filename);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
}
|
||||
}
|
||||
|
@ -405,6 +405,8 @@ INNER_MAKE_GECKOVIEW_LIBRARY= \
|
||||
else
|
||||
INNER_MAKE_GECKOVIEW_LIBRARY=echo 'GeckoView library packaging is disabled'
|
||||
endif
|
||||
else
|
||||
INNER_MAKE_GECKOVIEW_LIBRARY=echo 'GeckoView library packaging is only enabled on Nightly'
|
||||
endif
|
||||
|
||||
ifdef MOZ_OMX_PLUGIN
|
||||
|
@ -121,7 +121,8 @@
|
||||
#endif
|
||||
|
||||
#ifdef NECKO_PROTOCOL_rtsp
|
||||
#include "nsISystemMessagesInternal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIMessageManager.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
@ -605,41 +606,29 @@ private:
|
||||
} // anonymous namespace
|
||||
|
||||
/**
|
||||
* This function broadcasts a system message in order to launch video app for
|
||||
* rtsp scheme. This is Gonk-specific behavior.
|
||||
* This function sends a message. This 'content-handler' message is handled in
|
||||
* b2g/chrome/content/shell.js where it starts an activity request that will
|
||||
* open the video app.
|
||||
*/
|
||||
void nsExternalHelperAppService::LaunchVideoAppForRtsp(nsIURI* aURI)
|
||||
{
|
||||
NS_NAMED_LITERAL_STRING(msgType, "rtsp-open-video");
|
||||
bool rv;
|
||||
|
||||
// Make the url is rtsp.
|
||||
bool isRTSP = false;
|
||||
aURI->SchemeIs("rtsp", &isRTSP);
|
||||
NS_ASSERTION(isRTSP, "Not rtsp protocol! Something goes wrong here");
|
||||
// Get a system principal.
|
||||
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(securityManager);
|
||||
|
||||
// Construct jsval for system message.
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
securityManager->GetSystemPrincipal(getter_AddRefs(principal));
|
||||
NS_ENSURE_TRUE_VOID(principal);
|
||||
|
||||
// Construct the message in jsVal format.
|
||||
AutoSafeJSContext cx;
|
||||
AutoClearPendingException helper(cx);
|
||||
JS::Rooted<JSObject*> msgObj(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
|
||||
NS_ENSURE_TRUE_VOID(msgObj);
|
||||
JS::Rooted<JS::Value> jsVal(cx);
|
||||
bool rv;
|
||||
|
||||
// Set the "url" and "title" properties of the message.
|
||||
// In the case of RTSP streaming, the title is the same as the url.
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aURI->GetAsciiSpec(spec);
|
||||
JSString *urlStr = JS_NewStringCopyN(cx, spec.get(), spec.Length());
|
||||
NS_ENSURE_TRUE_VOID(urlStr);
|
||||
jsVal.setString(urlStr);
|
||||
|
||||
rv = JS_SetProperty(cx, msgObj, "url", jsVal);
|
||||
NS_ENSURE_TRUE_VOID(rv);
|
||||
|
||||
rv = JS_SetProperty(cx, msgObj, "title", jsVal);
|
||||
NS_ENSURE_TRUE_VOID(rv);
|
||||
}
|
||||
|
||||
// Set the "type" property of the message. This is a fake MIME type.
|
||||
{
|
||||
@ -647,18 +636,29 @@ void nsExternalHelperAppService::LaunchVideoAppForRtsp(nsIURI* aURI)
|
||||
JSString *typeStr = JS_NewStringCopyN(cx, mimeType.get(), mimeType.Length());
|
||||
NS_ENSURE_TRUE_VOID(typeStr);
|
||||
jsVal.setString(typeStr);
|
||||
rv = JS_SetProperty(cx, msgObj, "type", jsVal);
|
||||
NS_ENSURE_TRUE_VOID(rv);
|
||||
}
|
||||
// Set the "url" and "title" properties of the message.
|
||||
// They are the same in the case of RTSP streaming.
|
||||
{
|
||||
nsAutoCString spec;
|
||||
aURI->GetSpec(spec);
|
||||
JSString *urlStr = JS_NewStringCopyN(cx, spec.get(), spec.Length());
|
||||
NS_ENSURE_TRUE_VOID(urlStr);
|
||||
jsVal.setString(urlStr);
|
||||
rv = JS_SetProperty(cx, msgObj, "url", jsVal);
|
||||
NS_ENSURE_TRUE_VOID(rv);
|
||||
rv = JS_SetProperty(cx, msgObj, "title", jsVal);
|
||||
}
|
||||
rv = JS_SetProperty(cx, msgObj, "type", jsVal);
|
||||
NS_ENSURE_TRUE_VOID(rv);
|
||||
|
||||
// Broadcast system message.
|
||||
nsCOMPtr<nsISystemMessagesInternal> systemMessenger =
|
||||
do_GetService("@mozilla.org/system-message-internal;1");
|
||||
NS_ENSURE_TRUE_VOID(systemMessenger);
|
||||
jsVal.setObject(*msgObj);
|
||||
systemMessenger->BroadcastMessage(msgType, jsVal, JS::UndefinedValue());
|
||||
|
||||
return;
|
||||
// Send the message.
|
||||
nsCOMPtr<nsIMessageSender> cpmm =
|
||||
do_GetService("@mozilla.org/childprocessmessagemanager;1");
|
||||
NS_ENSURE_TRUE_VOID(cpmm);
|
||||
cpmm->SendAsyncMessage(NS_LITERAL_STRING("content-handler"),
|
||||
jsVal, JSVAL_NULL, principal, cx, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -30,25 +30,17 @@ ContentPermission.prototype = {
|
||||
},
|
||||
|
||||
prompt: function(request) {
|
||||
// Only allow exactly one permission rquest here.
|
||||
let types = request.types.QueryInterface(Ci.nsIArray);
|
||||
if (types.length != 1) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
let perm = types.queryElementAt(0, Ci.nsIContentPermissionType);
|
||||
|
||||
// Reuse any remembered permission preferences
|
||||
let result =
|
||||
Services.perms.testExactPermissionFromPrincipal(request.principal,
|
||||
perm.type);
|
||||
request.type);
|
||||
|
||||
// We used to use the name "geo" for the geolocation permission, now we're
|
||||
// using "geolocation". We need to check both to support existing
|
||||
// installations.
|
||||
if ((result == Ci.nsIPermissionManager.UNKNOWN_ACTION ||
|
||||
result == Ci.nsIPermissionManager.PROMPT_ACTION) &&
|
||||
perm.type == "geolocation") {
|
||||
request.type == "geolocation") {
|
||||
let geoResult = Services.perms.testExactPermission(request.principal.URI,
|
||||
"geo");
|
||||
// We override the result only if the "geo" permission was allowed or
|
||||
@ -64,7 +56,7 @@ ContentPermission.prototype = {
|
||||
return;
|
||||
} else if (result == Ci.nsIPermissionManager.DENY_ACTION ||
|
||||
(result == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
UNKNOWN_FAIL.indexOf(perm.type) >= 0)) {
|
||||
UNKNOWN_FAIL.indexOf(request.type) >= 0)) {
|
||||
request.cancel();
|
||||
return;
|
||||
}
|
||||
@ -79,16 +71,16 @@ ContentPermission.prototype = {
|
||||
let remember = {value: false};
|
||||
let choice = Services.prompt.confirmEx(
|
||||
chromeWin,
|
||||
bundle.formatStringFromName(perm.type + ".title", [name], 1),
|
||||
bundle.GetStringFromName(perm.type + ".description"),
|
||||
bundle.formatStringFromName(request.type + ".title", [name], 1),
|
||||
bundle.GetStringFromName(request.type + ".description"),
|
||||
// Set both buttons to strings with the cancel button being default
|
||||
Ci.nsIPromptService.BUTTON_POS_1_DEFAULT |
|
||||
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 |
|
||||
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1,
|
||||
bundle.GetStringFromName(perm.type + ".allow"),
|
||||
bundle.GetStringFromName(perm.type + ".deny"),
|
||||
bundle.GetStringFromName(request.type + ".allow"),
|
||||
bundle.GetStringFromName(request.type + ".deny"),
|
||||
null,
|
||||
bundle.GetStringFromName(perm.type + ".remember"),
|
||||
bundle.GetStringFromName(request.type + ".remember"),
|
||||
remember);
|
||||
|
||||
let action = Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
@ -98,10 +90,10 @@ ContentPermission.prototype = {
|
||||
|
||||
if (remember.value) {
|
||||
// Persist the choice if the user wants to remember
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, action);
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, action);
|
||||
} else {
|
||||
// Otherwise allow the permission for the current session
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, action,
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, action,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION);
|
||||
}
|
||||
|
||||
|
@ -568,6 +568,27 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
||||
break;
|
||||
}
|
||||
|
||||
case TELEMETRY_UI_SESSION_START: {
|
||||
ReadCharactersField(jenv);
|
||||
mTime = jenv->GetLongField(jobj, jTimeField);
|
||||
break;
|
||||
}
|
||||
|
||||
case TELEMETRY_UI_SESSION_STOP: {
|
||||
ReadCharactersField(jenv);
|
||||
ReadCharactersExtraField(jenv);
|
||||
mTime = jenv->GetLongField(jobj, jTimeField);
|
||||
break;
|
||||
}
|
||||
|
||||
case TELEMETRY_UI_EVENT: {
|
||||
ReadCharactersField(jenv);
|
||||
ReadCharactersExtraField(jenv);
|
||||
ReadDataField(jenv);
|
||||
mTime = jenv->GetLongField(jobj, jTimeField);
|
||||
break;
|
||||
}
|
||||
|
||||
case PREFERENCES_OBSERVE:
|
||||
case PREFERENCES_GET: {
|
||||
ReadStringArray(mPrefNames, jenv, jPrefNamesField);
|
||||
|
@ -688,11 +688,14 @@ public:
|
||||
PREFERENCES_OBSERVE = 39,
|
||||
PREFERENCES_GET = 40,
|
||||
PREFERENCES_REMOVE_OBSERVERS = 41,
|
||||
TELEMETRY_UI_SESSION_START = 42,
|
||||
TELEMETRY_UI_SESSION_STOP = 43,
|
||||
TELEMETRY_UI_EVENT = 44,
|
||||
dummy_java_enum_list_end
|
||||
};
|
||||
|
||||
enum {
|
||||
// Memory pressue levels, keep in sync with those in MemoryMonitor.java
|
||||
// Memory pressure levels. Keep these in sync with those in MemoryMonitor.java.
|
||||
MEMORY_PRESSURE_NONE = 0,
|
||||
MEMORY_PRESSURE_CLEANUP = 1,
|
||||
MEMORY_PRESSURE_LOW = 2,
|
||||
|
@ -259,8 +259,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
||||
NativeEventCallback();
|
||||
break;
|
||||
|
||||
case AndroidGeckoEvent::SENSOR_EVENT:
|
||||
{
|
||||
case AndroidGeckoEvent::SENSOR_EVENT: {
|
||||
InfallibleTArray<float> values;
|
||||
mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags();
|
||||
|
||||
@ -371,7 +370,6 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
||||
|
||||
case AndroidGeckoEvent::VIEWPORT:
|
||||
case AndroidGeckoEvent::BROADCAST: {
|
||||
|
||||
if (curEvent->Characters().Length() == 0)
|
||||
break;
|
||||
|
||||
@ -385,6 +383,57 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
||||
break;
|
||||
}
|
||||
|
||||
case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: {
|
||||
if (curEvent->Characters().Length() == 0)
|
||||
break;
|
||||
|
||||
nsCOMPtr<nsIUITelemetryObserver> obs;
|
||||
mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
|
||||
if (!obs)
|
||||
break;
|
||||
|
||||
obs->StopSession(
|
||||
nsString(curEvent->Characters()).get(),
|
||||
nsString(curEvent->CharactersExtra()).get(),
|
||||
curEvent->Time()
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: {
|
||||
if (curEvent->Characters().Length() == 0)
|
||||
break;
|
||||
|
||||
nsCOMPtr<nsIUITelemetryObserver> obs;
|
||||
mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
|
||||
if (!obs)
|
||||
break;
|
||||
|
||||
obs->StartSession(
|
||||
nsString(curEvent->Characters()).get(),
|
||||
curEvent->Time()
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case AndroidGeckoEvent::TELEMETRY_UI_EVENT: {
|
||||
if (curEvent->Characters().Length() == 0)
|
||||
break;
|
||||
|
||||
nsCOMPtr<nsIUITelemetryObserver> obs;
|
||||
mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs));
|
||||
if (!obs)
|
||||
break;
|
||||
|
||||
obs->AddEvent(
|
||||
nsString(curEvent->Data()).get(),
|
||||
nsString(curEvent->Characters()).get(),
|
||||
curEvent->Time(),
|
||||
nsString(curEvent->CharactersExtra()).get()
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case AndroidGeckoEvent::LOAD_URI: {
|
||||
nsCOMPtr<nsICommandLineRunner> cmdline
|
||||
(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
|
||||
|
@ -11,7 +11,20 @@ interface nsIBrowserTab : nsISupports {
|
||||
readonly attribute float scale;
|
||||
};
|
||||
|
||||
[scriptable, uuid(7508b826-4129-40a0-91da-2a6bba33681f)]
|
||||
[scriptable, uuid(08426a73-e70b-4680-9282-630932e2b2bb)]
|
||||
interface nsIUITelemetryObserver : nsISupports {
|
||||
void startSession(in wstring name,
|
||||
in unsigned long timestamp);
|
||||
void stopSession(in wstring name,
|
||||
in wstring reason,
|
||||
in unsigned long timestamp);
|
||||
void addEvent(in wstring action,
|
||||
in wstring method,
|
||||
in unsigned long timestamp,
|
||||
in wstring extras);
|
||||
};
|
||||
|
||||
[scriptable, uuid(c31331d2-afad-460f-9c66-728b8c838cec)]
|
||||
interface nsIAndroidBrowserApp : nsISupports {
|
||||
nsIBrowserTab getBrowserTab(in int32_t tabId);
|
||||
void getPreferences(in int32_t requestId,
|
||||
@ -21,7 +34,9 @@ interface nsIAndroidBrowserApp : nsISupports {
|
||||
[array, size_is(count)] in wstring prefNames,
|
||||
in unsigned long count);
|
||||
void removePreferenceObservers(in int32_t requestId);
|
||||
nsIUITelemetryObserver getUITelemetryObserver();
|
||||
};
|
||||
|
||||
[scriptable, uuid(59cfcb35-69b7-47b2-8155-32b193272666)]
|
||||
interface nsIAndroidViewport : nsISupports {
|
||||
readonly attribute float x;
|
||||
|
@ -334,7 +334,7 @@ class TransformedEndEvent : public nsRunnable
|
||||
};
|
||||
|
||||
void
|
||||
APZController::NotifyTransformBegin()
|
||||
APZController::NotifyTransformBegin(const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
MetroUtils::FireObserver("apzc-transform-begin", L"");
|
||||
@ -345,7 +345,7 @@ APZController::NotifyTransformBegin()
|
||||
}
|
||||
|
||||
void
|
||||
APZController::NotifyTransformEnd()
|
||||
APZController::NotifyTransformEnd(const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
MetroUtils::FireObserver("apzc-transform-end", L"");
|
||||
|
@ -37,8 +37,8 @@ public:
|
||||
virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint, int32_t aModifiers);
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRoot, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
|
||||
virtual void PostDelayedTask(Task* aTask, int aDelayMs);
|
||||
virtual void NotifyTransformBegin();
|
||||
virtual void NotifyTransformEnd();
|
||||
virtual void NotifyTransformBegin(const ScrollableLayerGuid& aGuid);
|
||||
virtual void NotifyTransformEnd(const ScrollableLayerGuid& aGuid);
|
||||
|
||||
void SetWidgetListener(nsIWidgetListener* aWidgetListener);
|
||||
void UpdateScrollOffset(const mozilla::layers::ScrollableLayerGuid& aScrollLayerId, CSSIntPoint& aScrollOffset);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user