Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2013-12-10 15:48:16 -05:00
commit 7a6613cc88
101 changed files with 2055 additions and 1195 deletions

View File

@ -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]);

View File

@ -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,

View File

@ -1,4 +1,4 @@
{
"revision": "bb23044d4a97be1381be924064817dddbd2ea47b",
"revision": "9f1117fde1d221d998c065a87e0614af6239b585",
"repo_path": "/integration/gaia-central"
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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();
}
},

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

@ -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"));

View File

@ -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

View File

@ -4,3 +4,5 @@
[browser_SignInToWebsite.js]
[browser_UITour.js]
support-files = uitour.*
[browser_taskbar_preview.js]
run-if = os == "win"

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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();
}

View File

@ -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

View File

@ -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/

View File

@ -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

View File

@ -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() {}

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -323,11 +323,6 @@ this.PermissionsTable = { geolocation: {
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"video-capture": {
app: PROMPT_ACTION,
privileged: PROMPT_ACTION,
certified: PROMPT_ACTION
},
};
/**

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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.
*/

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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(

View File

@ -68,7 +68,6 @@ IPDL_SOURCES += [
'PBrowser.ipdl',
'PContent.ipdl',
'PContentDialog.ipdl',
'PContentPermission.ipdlh',
'PContentPermissionRequest.ipdl',
'PCrashReporter.ipdl',
'PDocumentRenderer.ipdl',

View File

@ -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

View File

@ -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
};

View File

@ -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();

View File

@ -110,7 +110,6 @@ this.SystemMessagePermissionsTable = {
"nfc-powerlevel-change": {
"settings": ["read", "write"]
},
"rtsp-open-video": {},
};
this.SystemMessagePermissionsChecker = {

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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();
});

View File

@ -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);
}

View File

@ -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

View File

@ -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)
{

View File

@ -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,

View File

@ -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));
}
}
}

View File

@ -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() {}

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -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;
};
/**

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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();

View File

@ -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");

View File

@ -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;
}

View File

@ -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"))

View File

@ -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);
}
}

View File

@ -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";

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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]

View 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();
}
}

View 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();

View File

@ -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) {

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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]) -

View File

@ -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();
}
};

View File

@ -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]

View File

@ -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();
}
};
});

View File

@ -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]

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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"));

View File

@ -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;

View File

@ -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"");

View File

@ -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