mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 20:35:50 +00:00
Merge m-c to inbound.
This commit is contained in:
commit
43c0e6a230
@ -5,7 +5,7 @@
|
||||
"use strict"
|
||||
|
||||
function debug(str) {
|
||||
//dump("-*- ContentPermissionPrompt: " + s + "\n");
|
||||
//dump("-*- ContentPermissionPrompt: " + str + "\n");
|
||||
}
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
@ -13,11 +13,14 @@ const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
|
||||
const PROMPT_FOR_UNKNOWN = ["geolocation", "desktop-notification",
|
||||
"audio-capture"];
|
||||
const PROMPT_FOR_UNKNOWN = ["audio-capture",
|
||||
"desktop-notification",
|
||||
"geolocation",
|
||||
"video-capture"];
|
||||
// Due to privary issue, permission requests like GetUserMedia should prompt
|
||||
// every time instead of providing session persistence.
|
||||
const PERMISSION_NO_SESSION = ["audio-capture"];
|
||||
const PERMISSION_NO_SESSION = ["audio-capture", "video-capture"];
|
||||
const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -41,7 +44,21 @@ XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"@mozilla.org/telephony/audiomanager;1",
|
||||
"nsIAudioManager");
|
||||
|
||||
function rememberPermission(aPermission, aPrincipal, aSession)
|
||||
/**
|
||||
* 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 convertPermToAllow(aPerm, aPrincipal)
|
||||
{
|
||||
@ -49,12 +66,13 @@ function rememberPermission(aPermission, aPrincipal, aSession)
|
||||
permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm);
|
||||
if (type == Ci.nsIPermissionManager.PROMPT_ACTION ||
|
||||
(type == Ci.nsIPermissionManager.UNKNOWN_ACTION &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(aPermission) >= 0)) {
|
||||
PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)) {
|
||||
debug("add " + aPerm + " to permission manager with ALLOW_ACTION");
|
||||
if (!aSession) {
|
||||
permissionManager.addFromPrincipal(aPrincipal,
|
||||
aPerm,
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
} else if (PERMISSION_NO_SESSION.indexOf(aPermission) < 0) {
|
||||
} else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) {
|
||||
permissionManager.addFromPrincipal(aPrincipal,
|
||||
aPerm,
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
@ -63,14 +81,18 @@ function rememberPermission(aPermission, aPrincipal, aSession)
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
convertPermToAllow(aPermission, aPrincipal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,23 +100,63 @@ function ContentPermissionPrompt() {}
|
||||
|
||||
ContentPermissionPrompt.prototype = {
|
||||
|
||||
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) {
|
||||
handleExistingPermission: function handleExistingPermission(request,
|
||||
typesInfo) {
|
||||
typesInfo.forEach(function(type) {
|
||||
type.action =
|
||||
Services.perms.testExactPermissionFromPrincipal(request.principal,
|
||||
type.access);
|
||||
});
|
||||
|
||||
// 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");
|
||||
request.allow();
|
||||
return true;
|
||||
}
|
||||
if (result == Ci.nsIPermissionManager.DENY_ACTION ||
|
||||
result == Ci.nsIPermissionManager.UNKNOWN_ACTION && PROMPT_FOR_UNKNOWN.indexOf(access) < 0) {
|
||||
|
||||
// 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 &&
|
||||
PROMPT_FOR_UNKNOWN.indexOf(type.access) < 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (typesInfo.every(checkDenyPermission)) {
|
||||
debug("all permission requests are denied");
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
handledByApp: function handledByApp(request) {
|
||||
// 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) {
|
||||
if (request.principal.appId == Ci.nsIScriptSecurityManager.NO_APP_ID ||
|
||||
request.principal.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) {
|
||||
// This should not really happen
|
||||
@ -106,49 +168,94 @@ ContentPermissionPrompt.prototype = {
|
||||
.getService(Ci.nsIAppsService);
|
||||
let app = appsService.getAppByLocalId(request.principal.appId);
|
||||
|
||||
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);
|
||||
// Check each permission if it's denied by permission manager with app's
|
||||
// URL.
|
||||
let checkIfDenyAppPrincipal = 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);
|
||||
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
|
||||
result == Ci.nsIPermissionManager.PROMPT_ACTION) {
|
||||
return false;
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION ||
|
||||
result == Ci.nsIPermissionManager.PROMPT_ACTION) {
|
||||
type.deny = false;
|
||||
}
|
||||
return type.deny;
|
||||
}
|
||||
if (typesInfo.every(checkIfDenyAppPrincipal)) {
|
||||
request.cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
request.cancel();
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
handledByPermissionType: function handledByPermissionType(request) {
|
||||
return permissionSpecificChecker.hasOwnProperty(request.type)
|
||||
? permissionSpecificChecker[request.type](request)
|
||||
: false;
|
||||
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;
|
||||
},
|
||||
|
||||
_id: 0,
|
||||
prompt: function(request) {
|
||||
if (secMan.isSystemPrincipal(request.principal)) {
|
||||
request.allow();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.handledByApp(request) ||
|
||||
this.handledByPermissionType(request)) {
|
||||
// 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)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// returns true if the request was handled
|
||||
if (this.handleExistingPermission(request))
|
||||
if (this.handleExistingPermission(request, typesInfo)) {
|
||||
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);
|
||||
this.delegatePrompt(request, requestId, typesInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -163,7 +270,7 @@ ContentPermissionPrompt.prototype = {
|
||||
if (evt.detail.visible === true)
|
||||
return;
|
||||
|
||||
self.cancelPrompt(request, requestId);
|
||||
self.cancelPrompt(request, requestId, typesInfo);
|
||||
cancelRequest();
|
||||
}
|
||||
|
||||
@ -180,7 +287,7 @@ ContentPermissionPrompt.prototype = {
|
||||
// away but the request is still here.
|
||||
frame.addEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
|
||||
self.delegatePrompt(request, requestId, function onCallback() {
|
||||
self.delegatePrompt(request, requestId, typesInfo, function onCallback() {
|
||||
frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange);
|
||||
});
|
||||
};
|
||||
@ -191,22 +298,17 @@ ContentPermissionPrompt.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
cancelPrompt: function(request, requestId) {
|
||||
this.sendToBrowserWindow("cancel-permission-prompt", request, requestId);
|
||||
cancelPrompt: function(request, requestId, typesInfo) {
|
||||
this.sendToBrowserWindow("cancel-permission-prompt", request, requestId,
|
||||
typesInfo);
|
||||
},
|
||||
|
||||
delegatePrompt: function(request, requestId, callback) {
|
||||
let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access :
|
||||
request.type;
|
||||
let principal = request.principal;
|
||||
delegatePrompt: function(request, requestId, typesInfo, callback) {
|
||||
|
||||
this._permission = access;
|
||||
this._uri = principal.URI.spec;
|
||||
this._origin = principal.origin;
|
||||
|
||||
this.sendToBrowserWindow("permission-prompt", request, requestId, function(type, remember) {
|
||||
this.sendToBrowserWindow("permission-prompt", request, requestId, typesInfo,
|
||||
function(type, remember) {
|
||||
if (type == "permission-allow") {
|
||||
rememberPermission(request.type, principal, !remember);
|
||||
rememberPermission(typesInfo, request.principal, !remember);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
@ -214,14 +316,20 @@ ContentPermissionPrompt.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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 {
|
||||
Services.perms.addFromPrincipal(request.principal, type.access,
|
||||
Ci.nsIPermissionManager.DENY_ACTION,
|
||||
Ci.nsIPermissionManager.EXPIRE_SESSION,
|
||||
0);
|
||||
}
|
||||
}
|
||||
typesInfo.forEach(addDenyPermission);
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
@ -230,7 +338,7 @@ ContentPermissionPrompt.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
sendToBrowserWindow: function(type, request, requestId, callback) {
|
||||
sendToBrowserWindow: function(type, request, requestId, typesInfo, callback) {
|
||||
let browser = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let content = browser.getContentWindow();
|
||||
if (!content)
|
||||
@ -253,10 +361,15 @@ 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.push(typesInfo[i].permission);
|
||||
}
|
||||
|
||||
let details = {
|
||||
type: type,
|
||||
permission: request.type,
|
||||
permissions: permissions,
|
||||
id: requestId,
|
||||
origin: principal.origin,
|
||||
isApp: isApp,
|
||||
@ -289,6 +402,5 @@ ContentPermissionPrompt.prototype = {
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
//module initialization
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "9c89bc252dca005ec14f54d6d8524b44917322b2",
|
||||
"revision": "53c6a2b0bf4c238d39ee0096b79d911aec2de0fc",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ pref("lightweightThemes.update.enabled", true);
|
||||
|
||||
// UI tour experience.
|
||||
pref("browser.uitour.enabled", true);
|
||||
pref("browser.uitour.requireSecure", true);
|
||||
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
|
||||
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
|
||||
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
|
||||
|
@ -146,7 +146,8 @@ let gPage = {
|
||||
handleEvent: function Page_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "unload":
|
||||
this._mutationObserver.disconnect();
|
||||
if (this._mutationObserver)
|
||||
this._mutationObserver.disconnect();
|
||||
gAllPages.unregister(this);
|
||||
break;
|
||||
case "click":
|
||||
|
@ -15,24 +15,20 @@ function test() {
|
||||
// Verify that about:preferences tab is displayed when
|
||||
// browser.preferences.inContent is set to true
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", true);
|
||||
|
||||
gBrowser.tabContainer.addEventListener("TabOpen", function(aEvent) {
|
||||
|
||||
gBrowser.tabContainer.removeEventListener("TabOpen", arguments.callee, true);
|
||||
let browser = aEvent.originalTarget.linkedBrowser;
|
||||
browser.addEventListener("load", function(aEvent) {
|
||||
browser.removeEventListener("load", arguments.callee, true);
|
||||
|
||||
is(Services.prefs.getBoolPref("browser.preferences.inContent"), true, "In-content prefs are enabled");
|
||||
is(browser.contentWindow.location.href, "about:preferences", "Checking if the preferences tab was opened");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", false);
|
||||
openPreferences();
|
||||
|
||||
}, true);
|
||||
}, true);
|
||||
|
||||
|
||||
// Open a new tab.
|
||||
whenNewTabLoaded(window, testPreferences);
|
||||
}
|
||||
|
||||
function testPreferences() {
|
||||
whenTabLoaded(gBrowser.selectedTab, function () {
|
||||
is(Services.prefs.getBoolPref("browser.preferences.inContent"), true, "In-content prefs are enabled");
|
||||
is(content.location.href, "about:preferences", "Checking if the preferences tab was opened");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", false);
|
||||
openPreferences();
|
||||
});
|
||||
|
||||
let observer = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
|
@ -237,9 +237,14 @@ function whenNewTabLoaded(aWindow, aCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
whenTabLoaded(aWindow.gBrowser.selectedTab, aCallback);
|
||||
}
|
||||
|
||||
function whenTabLoaded(aTab, aCallback) {
|
||||
let browser = aTab.linkedBrowser;
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
aCallback();
|
||||
executeSoon(aCallback);
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
@ -473,8 +473,37 @@ function openAboutDialog() {
|
||||
|
||||
function openPreferences(paneID, extraArgs)
|
||||
{
|
||||
if (Services.prefs.getBoolPref("browser.preferences.inContent")) {
|
||||
openUILinkIn("about:preferences", "tab");
|
||||
function switchToAdvancedSubPane(doc) {
|
||||
if (extraArgs && extraArgs["advancedTab"]) {
|
||||
let advancedPaneTabs = doc.getElementById("advancedPrefs");
|
||||
advancedPaneTabs.selectedTab = doc.getElementById(extraArgs["advancedTab"]);
|
||||
}
|
||||
}
|
||||
|
||||
if (getBoolPref("browser.preferences.inContent")) {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newLoad = !win.switchToTabHavingURI("about:preferences", true);
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
function switchToPane() {
|
||||
if (paneID) {
|
||||
browser.contentWindow.selectCategory(paneID);
|
||||
}
|
||||
switchToAdvancedSubPane(browser.contentDocument);
|
||||
}
|
||||
|
||||
if (newLoad) {
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
switchToPane();
|
||||
}, true);
|
||||
} else {
|
||||
switchToPane();
|
||||
}
|
||||
} else {
|
||||
var instantApply = getBoolPref("browser.preferences.instantApply", false);
|
||||
var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
|
||||
@ -487,16 +516,11 @@ function openPreferences(paneID, extraArgs)
|
||||
win.document.documentElement.showPane(pane);
|
||||
}
|
||||
|
||||
if (extraArgs && extraArgs["advancedTab"]) {
|
||||
var advancedPaneTabs = win.document.getElementById("advancedPrefs");
|
||||
advancedPaneTabs.selectedTab = win.document.getElementById(extraArgs["advancedTab"]);
|
||||
}
|
||||
|
||||
return;
|
||||
switchToAdvancedSubPane(win.document);
|
||||
} else {
|
||||
openDialog("chrome://browser/content/preferences/preferences.xul",
|
||||
"Preferences", features, paneID, extraArgs);
|
||||
}
|
||||
|
||||
openDialog("chrome://browser/content/preferences/preferences.xul",
|
||||
"Preferences", features, paneID, extraArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,8 +452,10 @@ nsBrowserContentHandler.prototype = {
|
||||
var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
|
||||
if (chromeParam) {
|
||||
|
||||
// Handle the old preference dialog URL separately (bug 285416)
|
||||
if (chromeParam == "chrome://browser/content/pref/pref.xul") {
|
||||
// Handle old preference dialog URLs.
|
||||
if (chromeParam == "chrome://browser/content/pref/pref.xul" ||
|
||||
(Services.prefs.getBoolPref("browser.preferences.inContent") &&
|
||||
chromeParam == "chrome://browser/content/preferences/preferences.xul")) {
|
||||
openPreferences();
|
||||
cmdLine.preventDefault = true;
|
||||
} else try {
|
||||
|
@ -1995,13 +1995,21 @@ 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 (!(request.type in kFeatureKeys)) {
|
||||
if (!(perm.type in kFeatureKeys)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2013,7 +2021,7 @@ ContentPermissionPrompt.prototype = {
|
||||
return;
|
||||
|
||||
var autoAllow = false;
|
||||
var permissionKey = kFeatureKeys[request.type];
|
||||
var permissionKey = kFeatureKeys[perm.type];
|
||||
var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey);
|
||||
|
||||
if (result == Ci.nsIPermissionManager.DENY_ACTION) {
|
||||
@ -2024,14 +2032,14 @@ ContentPermissionPrompt.prototype = {
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
autoAllow = true;
|
||||
// For pointerLock, we still want to show a warning prompt.
|
||||
if (request.type != "pointerLock") {
|
||||
if (perm.type != "pointerLock") {
|
||||
request.allow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Show the prompt.
|
||||
switch (request.type) {
|
||||
switch (perm.type) {
|
||||
case "geolocation":
|
||||
this._promptGeo(request);
|
||||
break;
|
||||
|
@ -2,11 +2,11 @@
|
||||
* 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/. */
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
-moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler");
|
||||
}
|
||||
|
||||
richlistitem[selected="true"] {
|
||||
#handlersView > richlistitem[selected="true"] {
|
||||
-moz-binding: url("chrome://browser/content/preferences/handlers.xml#handler-selected");
|
||||
}
|
||||
|
||||
|
@ -128,11 +128,6 @@
|
||||
#endif
|
||||
<stringbundle id="bundlePreferences" src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
|
||||
<hbox class="heading" data-category="paneAdvanced" hidden="true">
|
||||
<image class="preference-icon" type="advanced"/>
|
||||
<html:h1>&paneAdvanced.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<tabbox id="advancedPrefs" flex="1"
|
||||
data-category="paneAdvanced" hidden="true"
|
||||
onselect="gAdvancedPane.tabSelectionChanged();">
|
||||
|
@ -55,11 +55,6 @@
|
||||
<key key="&focusSearch2.key;" modifiers="accel" oncommand="gApplicationsPane.focusFilterBox();"/>
|
||||
</keyset>
|
||||
|
||||
<hbox class="heading" data-category="paneApplications" hidden="true">
|
||||
<image class="preference-icon" type="applications"/>
|
||||
<html:h1>&paneApplications.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<vbox data-category="paneApplications" hidden="true" flex="1">
|
||||
<hbox>
|
||||
<textbox id="filter" flex="1"
|
||||
|
@ -21,12 +21,9 @@
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/preferences/in-content/content.js"/>
|
||||
|
||||
<hbox class="heading" data-category="paneContent" hidden="true">
|
||||
<image class="preference-icon" type="content"/>
|
||||
<html:h1>&paneContent.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<groupbox id="miscGroup" data-category="paneContent" hidden="true">
|
||||
<caption label="&popups.label;"/>
|
||||
|
||||
<grid id="contentGrid">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
browser.jar:
|
||||
content/browser/preferences/in-content/preferences.js
|
||||
content/browser/preferences/in-content/landing.xul
|
||||
* content/browser/preferences/in-content/preferences.xul
|
||||
* content/browser/preferences/in-content/main.xul
|
||||
* content/browser/preferences/in-content/main.js
|
||||
|
@ -1,55 +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/. -->
|
||||
|
||||
<vbox data-category="landing">
|
||||
<html:h1 class="indent-small">&brandShortName;</html:h1>
|
||||
|
||||
<hbox id="preferences-home" flex="1">
|
||||
|
||||
<button label="&paneGeneral.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneGeneral');">
|
||||
<image class="landingButton-icon" type="general"/>
|
||||
<label class="landingButton-label">&paneGeneral.title;</label>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
<button label="&paneContent.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneContent');">
|
||||
<image class="landingButton-icon" type="content"/>
|
||||
<label class="landingButton-label">&paneContent.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneApplications.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneApplications');">
|
||||
<image class="landingButton-icon" type="applications"/>
|
||||
<label class="landingButton-label">&paneApplications.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&panePrivacy.title;" class="landingButton"
|
||||
oncommand="gotoPref('panePrivacy');">
|
||||
<image class="landingButton-icon" type="privacy"/>
|
||||
<label class="landingButton-label">&panePrivacy.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneSecurity.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneSecurity');">
|
||||
<image class="landingButton-icon" type="security"/>
|
||||
<label class="landingButton-label">&paneSecurity.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneSync.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneSync');">
|
||||
<image class="landingButton-icon" type="sync"/>
|
||||
<label class="landingButton-label">&paneSync.title;</label>
|
||||
</button>
|
||||
|
||||
<button label="&paneAdvanced.title;" class="landingButton"
|
||||
oncommand="gotoPref('paneAdvanced');">
|
||||
<image class="landingButton-icon" type="advanced"/>
|
||||
<label class="landingButton-label">&paneAdvanced.title;</label>
|
||||
</button>
|
||||
|
||||
</hbox>
|
||||
</vbox>
|
@ -85,13 +85,8 @@
|
||||
#endif
|
||||
</preferences>
|
||||
|
||||
<hbox class="heading" data-category="paneGeneral" hidden="true">
|
||||
<image class="preference-icon" type="general"/>
|
||||
<html:h1>&paneGeneral.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<!-- Startup -->
|
||||
<groupbox id="startupGroup" data-category="paneGeneral" hidden="true">
|
||||
<groupbox id="startupGroup" data-category="paneGeneral">
|
||||
<caption label="&startup.label;"/>
|
||||
|
||||
<hbox align="center">
|
||||
@ -150,7 +145,7 @@
|
||||
</groupbox>
|
||||
|
||||
<!-- Downloads -->
|
||||
<groupbox id="downloadsGroup" data-category="paneGeneral" hidden="true">
|
||||
<groupbox id="downloadsGroup" data-category="paneGeneral">
|
||||
<caption label="&downloads.label;"/>
|
||||
|
||||
<radiogroup id="saveWhere"
|
||||
@ -189,7 +184,7 @@
|
||||
</groupbox>
|
||||
|
||||
<!-- Tab preferences -->
|
||||
<groupbox data-category="paneGeneral" hidden="true">
|
||||
<groupbox data-category="paneGeneral">
|
||||
<caption label="&tabsGroup.label;"/>
|
||||
<checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
|
||||
accesskey="&newWindowsAsTabs.accesskey;"
|
||||
|
@ -12,11 +12,13 @@ const Cr = Components.results;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
addEventListener("DOMContentLoaded", function onLoad() {
|
||||
removeEventListener("DOMContentLoaded", onLoad);
|
||||
init_all();
|
||||
});
|
||||
|
||||
function init_all() {
|
||||
document.documentElement.instantApply = true;
|
||||
window.history.replaceState("landing", document.title);
|
||||
window.addEventListener("popstate", onStatePopped, true);
|
||||
updateCommands();
|
||||
gMainPane.init();
|
||||
gPrivacyPane.init();
|
||||
gAdvancedPane.init();
|
||||
@ -27,12 +29,32 @@ function init_all() {
|
||||
var initFinished = document.createEvent("Event");
|
||||
initFinished.initEvent("Initialized", true, true);
|
||||
document.dispatchEvent(initFinished);
|
||||
|
||||
let categories = document.getElementById("categories");
|
||||
categories.addEventListener("select", event => gotoPref(event.target.value));
|
||||
window.addEventListener("popstate", event => selectCategory(event.state));
|
||||
|
||||
if (history.length > 1 && history.state) {
|
||||
updateCommands();
|
||||
selectCategory(history.state);
|
||||
} else {
|
||||
history.replaceState("paneGeneral", document.title);
|
||||
}
|
||||
}
|
||||
|
||||
function selectCategory(name) {
|
||||
let categories = document.getElementById("categories");
|
||||
let item = categories.querySelector(".category[value=" + name + "]");
|
||||
categories.selectedItem = item;
|
||||
}
|
||||
|
||||
function gotoPref(page) {
|
||||
search(page, "data-category");
|
||||
window.history.pushState(page, document.title);
|
||||
if (history.state != page) {
|
||||
window.history.pushState(page, document.title);
|
||||
}
|
||||
|
||||
updateCommands();
|
||||
search(page, "data-category");
|
||||
}
|
||||
|
||||
function cmd_back() {
|
||||
@ -43,11 +65,6 @@ function cmd_forward() {
|
||||
window.history.forward();
|
||||
}
|
||||
|
||||
function onStatePopped(aEvent) {
|
||||
updateCommands();
|
||||
search(aEvent.state, "data-category");
|
||||
}
|
||||
|
||||
function updateCommands() {
|
||||
document.getElementById("back-btn").disabled = !canGoBack();
|
||||
document.getElementById("forward-btn").disabled = !canGoForward();
|
||||
|
@ -54,8 +54,7 @@
|
||||
#define USE_WIN_TITLE_STYLE
|
||||
#endif
|
||||
|
||||
<page onload="init_all();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
#ifdef USE_WIN_TITLE_STYLE
|
||||
title="&prefWindow.titleWin;">
|
||||
@ -86,10 +85,58 @@
|
||||
oncommand="cmd_forward()" tooltiptext="&buttonForward.tooltip;"
|
||||
disabled="true"/>
|
||||
</hbox>
|
||||
|
||||
<hbox class="main-content" flex="1">
|
||||
<prefpane flex="1" id="mainPrefPane">
|
||||
#include landing.xul
|
||||
|
||||
<hbox flex="1">
|
||||
|
||||
<!-- category list -->
|
||||
<richlistbox id="categories">
|
||||
<richlistitem id="category-general" class="category" align="center"
|
||||
value="paneGeneral" tooltiptext="&paneGeneral.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneGeneral.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-content" class="category" align="center"
|
||||
value="paneContent" tooltiptext="&paneContent.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneContent.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-application" class="category" align="center"
|
||||
value="paneApplications" tooltiptext="&paneApplications.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneApplications.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-privacy" class="category" align="center"
|
||||
value="panePrivacy" tooltiptext="&panePrivacy.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&panePrivacy.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
<richlistitem id="category-security" class="category" align="center"
|
||||
value="paneSecurity" tooltiptext="&paneSecurity.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneSecurity.title;"/>
|
||||
</richlistitem>
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
<richlistitem id="category-sync" class="category" align="center"
|
||||
value="paneSync" tooltiptext="&paneSync.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneSync.title;"/>
|
||||
</richlistitem>
|
||||
#endif
|
||||
|
||||
<richlistitem id="category-advanced" class="category" align="center"
|
||||
value="paneAdvanced" tooltiptext="&paneAdvanced.title;">
|
||||
<image class="category-icon"/>
|
||||
<label class="category-name" flex="1" value="&paneAdvanced.title;"/>
|
||||
</richlistitem>
|
||||
</richlistbox>
|
||||
|
||||
<box class="main-content" flex="1">
|
||||
<prefpane flex="1" id="mainPrefPane">
|
||||
#include main.xul
|
||||
#include privacy.xul
|
||||
#include advanced.xul
|
||||
@ -99,7 +146,8 @@
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
#include sync.xul
|
||||
#endif
|
||||
</prefpane>
|
||||
</prefpane>
|
||||
</box>
|
||||
|
||||
</hbox>
|
||||
|
||||
</page>
|
||||
|
@ -65,11 +65,6 @@
|
||||
|
||||
</preferences>
|
||||
|
||||
<hbox class="heading" data-category="panePrivacy" hidden="true">
|
||||
<image class="preference-icon" type="privacy"/>
|
||||
<html:h1>&panePrivacy.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<!-- Tracking -->
|
||||
<groupbox id="trackingGroup" data-category="panePrivacy" hidden="true" align="start">
|
||||
<caption label="&tracking.label;"/>
|
||||
|
@ -30,13 +30,10 @@
|
||||
|
||||
</preferences>
|
||||
|
||||
<hbox class="heading" data-category="paneSecurity" hidden="true">
|
||||
<image class="preference-icon" type="security"/>
|
||||
<html:h1>&paneSecurity.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<!-- addons, forgery (phishing) UI -->
|
||||
<groupbox id="addonsPhishingGroup" data-category="paneSecurity" hidden="true">
|
||||
<caption label="&general.label;"/>
|
||||
|
||||
<hbox id="addonInstallBox">
|
||||
<checkbox id="warnAddonInstall" flex="1"
|
||||
label="&warnAddonInstall.label;"
|
||||
|
@ -28,11 +28,6 @@
|
||||
<script type="application/javascript"
|
||||
src="chrome://browser/content/sync/utils.js"/>
|
||||
|
||||
<hbox class="heading" data-category="paneSync" hidden="true">
|
||||
<image class="preference-icon" type="sync"/>
|
||||
<html:h1>&paneSync.title;</html:h1>
|
||||
</hbox>
|
||||
|
||||
<deck id="weavePrefsDeck" data-category="paneSync" hidden="true">
|
||||
<vbox id="noAccount" align="center">
|
||||
<spacer flex="1"/>
|
||||
|
@ -149,7 +149,7 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
|
||||
try {
|
||||
indexURL = Services.io.newURI(path, null, Services.io.newURI(origin, null, null)).spec;
|
||||
} catch(e) {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [origin + path], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1));
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
@ -158,25 +158,25 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
|
||||
try {
|
||||
req.open("HEAD", indexURL, true);
|
||||
} catch(e) {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [indexURL], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
|
||||
req.onload = () => {
|
||||
if (req.status >= 400)
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPathBadHttpCode", [indexURL, req.status], 2));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
|
||||
deferred.resolve();
|
||||
};
|
||||
req.onerror = () => {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [indexURL], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
|
||||
deferred.resolve();
|
||||
};
|
||||
|
||||
try {
|
||||
req.send(null);
|
||||
} catch(e) {
|
||||
this.error(strings.formatStringFromName("validator.invalidLaunchPath", [indexURL], 1));
|
||||
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@
|
||||
let validator = createHosted("wrong-launch-path");
|
||||
validator.validate().then(() => {
|
||||
is(validator.errors.length, 1, "app with non-existant launch path got an error");
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.invalidLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2),
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [origin + "wrong-path.html", 404], 2),
|
||||
"with the right error message");
|
||||
is(validator.warnings.length, 0, "but no warning");
|
||||
next();
|
||||
@ -112,7 +112,7 @@
|
||||
let file = nsFile(validator.project.location);
|
||||
file.append("wrong-path.html");
|
||||
let url = Services.io.newFileURI(file);
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.invalidLaunchPath", [url.spec], 1),
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),
|
||||
"with the expected message");
|
||||
is(validator.warnings.length, 0, "but no warning");
|
||||
|
||||
|
@ -358,6 +358,9 @@ InspectorPanel.prototype = {
|
||||
|
||||
this._initMarkup();
|
||||
this.once("markuploaded", () => {
|
||||
if (this._destroyPromise) {
|
||||
return;
|
||||
}
|
||||
this.markup.expandNode(this.selection.nodeFront);
|
||||
this.setupSearchBox();
|
||||
this.emit("new-root");
|
||||
|
@ -14,6 +14,7 @@ const COLLAPSE_ATTRIBUTE_LENGTH = 120;
|
||||
const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
|
||||
const COLLAPSE_DATA_URL_LENGTH = 60;
|
||||
const CONTAINER_FLASHING_DURATION = 500;
|
||||
const IMAGE_PREVIEW_MAX_DIM = 400;
|
||||
|
||||
const {UndoStack} = require("devtools/shared/undo");
|
||||
const {editableField, InplaceEditor} = require("devtools/shared/inplace-editor");
|
||||
@ -99,6 +100,10 @@ function MarkupView(aInspector, aFrame, aControllerWindow) {
|
||||
gDevTools.on("pref-changed", this._handlePrefChange);
|
||||
|
||||
this._initPreview();
|
||||
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
this.tooltip.startTogglingOnHover(this._elt,
|
||||
this._buildTooltipContent.bind(this));
|
||||
}
|
||||
|
||||
exports.MarkupView = MarkupView;
|
||||
@ -148,6 +153,25 @@ MarkupView.prototype = {
|
||||
updateChildren(documentElement);
|
||||
},
|
||||
|
||||
_buildTooltipContent: function(target) {
|
||||
// From the target passed here, let's find the parent MarkupContainer
|
||||
// and ask it if the tooltip should be shown
|
||||
let parent = target, container;
|
||||
while (parent !== this.doc.body) {
|
||||
if (parent.container) {
|
||||
container = parent.container;
|
||||
break;
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
if (container) {
|
||||
// With the newly found container, delegate the tooltip content creation
|
||||
// and decision to show or not the tooltip
|
||||
return container._buildTooltipContent(target, this.tooltip);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Highlight the inspector selected node.
|
||||
*/
|
||||
@ -954,6 +978,9 @@ MarkupView.prototype = {
|
||||
container.destroy();
|
||||
}
|
||||
delete this._containers;
|
||||
|
||||
this.tooltip.destroy();
|
||||
delete this.tooltip;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1099,8 +1126,8 @@ function MarkupContainer(aMarkupView, aNode, aInspector) {
|
||||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this.elt.addEventListener("mousedown", this._onMouseDown, false);
|
||||
|
||||
this.tooltip = null;
|
||||
this._attachTooltipIfNeeded();
|
||||
// Prepare the image preview tooltip data if any
|
||||
this._prepareImagePreview();
|
||||
}
|
||||
|
||||
MarkupContainer.prototype = {
|
||||
@ -1108,36 +1135,43 @@ MarkupContainer.prototype = {
|
||||
return "[MarkupContainer for " + this.node + "]";
|
||||
},
|
||||
|
||||
_attachTooltipIfNeeded: function() {
|
||||
_prepareImagePreview: function() {
|
||||
if (this.node.tagName) {
|
||||
let tagName = this.node.tagName.toLowerCase();
|
||||
let isImage = tagName === "img" &&
|
||||
this.editor.getAttributeElement("src");
|
||||
let isCanvas = tagName && tagName === "canvas";
|
||||
let srcAttr = this.editor.getAttributeElement("src");
|
||||
let isImage = tagName === "img" && srcAttr;
|
||||
let isCanvas = tagName === "canvas";
|
||||
|
||||
// Get the image data for later so that when the user actually hovers over
|
||||
// the element, the tooltip does contain the image
|
||||
if (isImage || isCanvas) {
|
||||
this.tooltip = new Tooltip(this._inspector.panelDoc);
|
||||
let def = promise.defer();
|
||||
|
||||
this.node.getImageData().then(data => {
|
||||
this.tooltipData = {
|
||||
target: isImage ? srcAttr : this.editor.tag,
|
||||
data: def.promise
|
||||
};
|
||||
|
||||
this.node.getImageData(IMAGE_PREVIEW_MAX_DIM).then(data => {
|
||||
if (data) {
|
||||
data.string().then(str => {
|
||||
this.tooltip.setImageContent(str);
|
||||
data.data.string().then(str => {
|
||||
// Resolving the data promise and, to always keep tooltipData.data
|
||||
// as a promise, create a new one that resolves immediately
|
||||
def.resolve(str, data.size);
|
||||
this.tooltipData.data = promise.resolve(str, data.size);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// If it's an image, show the tooltip on the src attribute
|
||||
if (isImage) {
|
||||
this.tooltip.startTogglingOnHover(this.editor.getAttributeElement("src"));
|
||||
}
|
||||
|
||||
// If it's a canvas, show it on the tag
|
||||
if (isCanvas) {
|
||||
this.tooltip.startTogglingOnHover(this.editor.tag);
|
||||
}
|
||||
_buildTooltipContent: function(target, tooltip) {
|
||||
if (this.tooltipData && target === this.tooltipData.target) {
|
||||
this.tooltipData.data.then((data, size) => {
|
||||
tooltip.setImageContent(data, size);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1375,12 +1409,6 @@ MarkupContainer.prototype = {
|
||||
|
||||
// Destroy my editor
|
||||
this.editor.destroy();
|
||||
|
||||
// Destroy the tooltip if any
|
||||
if (this.tooltip) {
|
||||
this.tooltip.destroy();
|
||||
this.tooltip = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -88,14 +88,14 @@ function testImageTooltip(index) {
|
||||
target = container.editor.getAttributeElement("src");
|
||||
}
|
||||
|
||||
assertTooltipShownOn(container.tooltip, target, () => {
|
||||
let images = container.tooltip.panel.getElementsByTagName("image");
|
||||
assertTooltipShownOn(target, () => {
|
||||
let images = markup.tooltip.panel.getElementsByTagName("image");
|
||||
is(images.length, 1, "Tooltip for [" + TEST_NODES[index] + "] contains an image");
|
||||
if (isImg) {
|
||||
compareImageData(node, images[0].src);
|
||||
}
|
||||
|
||||
container.tooltip.hide();
|
||||
markup.tooltip.hide();
|
||||
|
||||
testImageTooltip(index + 1);
|
||||
});
|
||||
@ -115,17 +115,17 @@ function compareImageData(img, imgData) {
|
||||
is(data, imgData, "Tooltip image has the right content");
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
function assertTooltipShownOn(element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
markup.tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
markup.tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
|
||||
// Poll until the image gets loaded in the tooltip. This is required because
|
||||
// markup containers only load images in their associated tooltips when
|
||||
// the image data comes back from the server. However, this test is executed
|
||||
// synchronously as soon as "inspector-updated" is fired, which is before
|
||||
// the data for images is known.
|
||||
let hasImage = () => tooltip.panel.getElementsByTagName("image").length;
|
||||
let hasImage = () => markup.tooltip.panel.getElementsByTagName("image").length;
|
||||
let poll = setInterval(() => {
|
||||
if (hasImage()) {
|
||||
clearInterval(poll);
|
||||
@ -133,5 +133,5 @@ function assertTooltipShownOn(tooltip, element, cb) {
|
||||
}
|
||||
}, 200);
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
markup.tooltip._showOnHover(element);
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ var Scratchpad = {
|
||||
{
|
||||
this._dirty = aValue;
|
||||
if (!aValue && this.editor)
|
||||
this.editor.markClean();
|
||||
this.editor.setClean();
|
||||
this._updateTitle();
|
||||
},
|
||||
|
||||
|
@ -15,6 +15,7 @@ Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
const promise = require("sdk/core/promise");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
|
||||
// The panel's window global is an EventEmitter firing the following events:
|
||||
@ -31,12 +32,14 @@ const EVENTS = {
|
||||
};
|
||||
|
||||
const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties"
|
||||
const HIGHLIGHT_COLOR = [1, 0, 0, 1];
|
||||
const BLACKBOX_COLOR = [0, 0, 0, 0];
|
||||
const TYPING_MAX_DELAY = 500;
|
||||
const HIGHLIGHT_COLOR = [1, 0, 0, 1]; // rgba
|
||||
const TYPING_MAX_DELAY = 500; // ms
|
||||
const SHADERS_AUTOGROW_ITEMS = 4;
|
||||
const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px
|
||||
const GUTTER_ERROR_PANEL_DELAY = 100; // ms
|
||||
const DEFAULT_EDITOR_CONFIG = {
|
||||
mode: Editor.modes.text,
|
||||
gutters: ["errors"],
|
||||
lineNumbers: true,
|
||||
showAnnotationRuler: true
|
||||
};
|
||||
@ -174,25 +177,25 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
||||
showItemCheckboxes: true
|
||||
});
|
||||
|
||||
this._onShaderSelect = this._onShaderSelect.bind(this);
|
||||
this._onShaderCheck = this._onShaderCheck.bind(this);
|
||||
this._onShaderMouseEnter = this._onShaderMouseEnter.bind(this);
|
||||
this._onShaderMouseLeave = this._onShaderMouseLeave.bind(this);
|
||||
this._onProgramSelect = this._onProgramSelect.bind(this);
|
||||
this._onProgramCheck = this._onProgramCheck.bind(this);
|
||||
this._onProgramMouseEnter = this._onProgramMouseEnter.bind(this);
|
||||
this._onProgramMouseLeave = this._onProgramMouseLeave.bind(this);
|
||||
|
||||
this.widget.addEventListener("select", this._onShaderSelect, false);
|
||||
this.widget.addEventListener("check", this._onShaderCheck, false);
|
||||
this.widget.addEventListener("mouseenter", this._onShaderMouseEnter, true);
|
||||
this.widget.addEventListener("mouseleave", this._onShaderMouseLeave, true);
|
||||
this.widget.addEventListener("select", this._onProgramSelect, false);
|
||||
this.widget.addEventListener("check", this._onProgramCheck, false);
|
||||
this.widget.addEventListener("mouseenter", this._onProgramMouseEnter, true);
|
||||
this.widget.addEventListener("mouseleave", this._onProgramMouseLeave, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destruction function, called when the tool is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
this.widget.removeEventListener("select", this._onShaderSelect, false);
|
||||
this.widget.removeEventListener("check", this._onShaderCheck, false);
|
||||
this.widget.removeEventListener("mouseenter", this._onShaderMouseEnter, true);
|
||||
this.widget.removeEventListener("mouseleave", this._onShaderMouseLeave, true);
|
||||
this.widget.removeEventListener("select", this._onProgramSelect, false);
|
||||
this.widget.removeEventListener("check", this._onProgramCheck, false);
|
||||
this.widget.removeEventListener("mouseenter", this._onProgramMouseEnter, true);
|
||||
this.widget.removeEventListener("mouseleave", this._onProgramMouseLeave, true);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -248,9 +251,9 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
||||
},
|
||||
|
||||
/**
|
||||
* The select listener for the sources container.
|
||||
* The select listener for the programs container.
|
||||
*/
|
||||
_onShaderSelect: function({ detail: sourceItem }) {
|
||||
_onProgramSelect: function({ detail: sourceItem }) {
|
||||
if (!sourceItem) {
|
||||
return;
|
||||
}
|
||||
@ -280,19 +283,19 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
||||
},
|
||||
|
||||
/**
|
||||
* The check listener for the sources container.
|
||||
* The check listener for the programs container.
|
||||
*/
|
||||
_onShaderCheck: function({ detail: { checked }, target }) {
|
||||
_onProgramCheck: function({ detail: { checked }, target }) {
|
||||
let sourceItem = this.getItemForElement(target);
|
||||
let attachment = sourceItem.attachment;
|
||||
attachment.isBlackBoxed = !checked;
|
||||
attachment.programActor[checked ? "unhighlight" : "highlight"](BLACKBOX_COLOR);
|
||||
attachment.programActor[checked ? "unblackbox" : "blackbox"]();
|
||||
},
|
||||
|
||||
/**
|
||||
* The mouseenter listener for the sources container.
|
||||
* The mouseenter listener for the programs container.
|
||||
*/
|
||||
_onShaderMouseEnter: function(e) {
|
||||
_onProgramMouseEnter: function(e) {
|
||||
let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
|
||||
if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
|
||||
sourceItem.attachment.programActor.highlight(HIGHLIGHT_COLOR);
|
||||
@ -305,9 +308,9 @@ let ShadersListView = Heritage.extend(WidgetMethods, {
|
||||
},
|
||||
|
||||
/**
|
||||
* The mouseleave listener for the sources container.
|
||||
* The mouseleave listener for the programs container.
|
||||
*/
|
||||
_onShaderMouseLeave: function(e) {
|
||||
_onProgramMouseLeave: function(e) {
|
||||
let sourceItem = this.getItemForElement(e.target, { noSiblings: true });
|
||||
if (sourceItem && !sourceItem.attachment.isBlackBoxed) {
|
||||
sourceItem.attachment.programActor.unhighlight();
|
||||
@ -427,6 +430,9 @@ let ShadersEditorsView = {
|
||||
*/
|
||||
_onChanged: function(type) {
|
||||
setNamedTimeout("gl-typed", TYPING_MAX_DELAY, () => this._doCompile(type));
|
||||
|
||||
// Remove all the gutter markers and line classes from the editor.
|
||||
this._cleanEditor(type);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -443,13 +449,117 @@ let ShadersEditorsView = {
|
||||
|
||||
try {
|
||||
yield shaderActor.compile(editor.getText());
|
||||
window.emit(EVENTS.SHADER_COMPILED, null);
|
||||
// TODO: remove error gutter markers, after bug 919709 lands.
|
||||
} catch (error) {
|
||||
window.emit(EVENTS.SHADER_COMPILED, error);
|
||||
// TODO: add error gutter markers, after bug 919709 lands.
|
||||
this._onSuccessfulCompilation();
|
||||
} catch (e) {
|
||||
this._onFailedCompilation(type, editor, e);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called uppon a successful shader compilation.
|
||||
*/
|
||||
_onSuccessfulCompilation: function() {
|
||||
// Signal that the shader was compiled successfully.
|
||||
window.emit(EVENTS.SHADER_COMPILED, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called uppon an unsuccessful shader compilation.
|
||||
*/
|
||||
_onFailedCompilation: function(type, editor, errors) {
|
||||
let lineCount = editor.lineCount();
|
||||
let currentLine = editor.getCursor().line;
|
||||
let listeners = { mouseenter: this._onMarkerMouseEnter };
|
||||
|
||||
function matchLinesAndMessages(string) {
|
||||
return {
|
||||
// First number that is not equal to 0.
|
||||
lineMatch: string.match(/\d{2,}|[1-9]/),
|
||||
// The string after all the numbers, semicolons and spaces.
|
||||
textMatch: string.match(/[^\s\d:][^\r\n|]*/)
|
||||
};
|
||||
}
|
||||
function discardInvalidMatches(e) {
|
||||
// Discard empty line and text matches.
|
||||
return e.lineMatch && e.textMatch;
|
||||
}
|
||||
function sanitizeValidMatches(e) {
|
||||
return {
|
||||
// Drivers might yield retarded line numbers under some obscure
|
||||
// circumstances. Don't throw the errors away in those cases,
|
||||
// just display them on the currently edited line.
|
||||
line: e.lineMatch[0] > lineCount ? currentLine : e.lineMatch[0] - 1,
|
||||
// Trim whitespace from the beginning and the end of the message,
|
||||
// and replace all other occurences of double spaces to a single space.
|
||||
text: e.textMatch[0].trim().replace(/\s{2,}/g, " ")
|
||||
};
|
||||
}
|
||||
function sortByLine(first, second) {
|
||||
// Sort all the errors ascending by their corresponding line number.
|
||||
return first.line > second.line ? 1 : -1;
|
||||
}
|
||||
function groupSameLineMessages(accumulator, current) {
|
||||
// Group errors corresponding to the same line number to a single object.
|
||||
let previous = accumulator[accumulator.length - 1];
|
||||
if (!previous || previous.line != current.line) {
|
||||
return [...accumulator, {
|
||||
line: current.line,
|
||||
messages: [current.text]
|
||||
}];
|
||||
} else {
|
||||
previous.messages.push(current.text);
|
||||
return accumulator;
|
||||
}
|
||||
}
|
||||
function displayErrors({ line, messages }) {
|
||||
// Add gutter markers and line classes for every error in the source.
|
||||
editor.addMarker(line, "errors", "error");
|
||||
editor.setMarkerListeners(line, "errors", "error", listeners, messages);
|
||||
editor.addLineClass(line, "error-line");
|
||||
}
|
||||
|
||||
(this._errors[type] = errors.link
|
||||
.split("ERROR")
|
||||
.map(matchLinesAndMessages)
|
||||
.filter(discardInvalidMatches)
|
||||
.map(sanitizeValidMatches)
|
||||
.sort(sortByLine)
|
||||
.reduce(groupSameLineMessages, []))
|
||||
.forEach(displayErrors);
|
||||
|
||||
// Signal that the shader wasn't compiled successfully.
|
||||
window.emit(EVENTS.SHADER_COMPILED, errors);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event listener for the 'mouseenter' event on a marker in the editor gutter.
|
||||
*/
|
||||
_onMarkerMouseEnter: function(line, node, messages) {
|
||||
if (node._markerErrorsTooltip) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tooltip = node._markerErrorsTooltip = new Tooltip(document);
|
||||
tooltip.defaultOffsetX = GUTTER_ERROR_PANEL_OFFSET_X;
|
||||
tooltip.setTextContent.apply(tooltip, messages);
|
||||
tooltip.startTogglingOnHover(node, () => true, GUTTER_ERROR_PANEL_DELAY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all the gutter markers and line classes from the editor.
|
||||
*/
|
||||
_cleanEditor: function(type) {
|
||||
this._getEditor(type).then(editor => {
|
||||
editor.removeAllMarkers("errors");
|
||||
this._errors[type].forEach(e => editor.removeLineClass(e.line));
|
||||
this._errors[type].length = 0;
|
||||
});
|
||||
},
|
||||
|
||||
_errors: {
|
||||
vs: [],
|
||||
fs: []
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
doc_multiple-contexts.html
|
||||
doc_overlapping-geometry.html
|
||||
doc_shader-order.html
|
||||
doc_simple-canvas.html
|
||||
head.js
|
||||
@ -8,6 +9,8 @@ support-files =
|
||||
[browser_se_aaa_run_first_leaktest.js]
|
||||
[browser_se_bfcache.js]
|
||||
[browser_se_editors-contents.js]
|
||||
[browser_se_editors-error-gutter.js]
|
||||
[browser_se_editors-error-tooltip.js]
|
||||
[browser_se_editors-lazy-init.js]
|
||||
[browser_se_first-run.js]
|
||||
[browser_se_navigation.js]
|
||||
@ -34,3 +37,4 @@ support-files =
|
||||
[browser_webgl-actor-test-14.js]
|
||||
[browser_webgl-actor-test-15.js]
|
||||
[browser_webgl-actor-test-16.js]
|
||||
[browser_webgl-actor-test-17.js]
|
||||
|
@ -0,0 +1,156 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if error indicators are shown in the editor's gutter and text area
|
||||
* when there's a shader compilation error.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
|
||||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks added in the vertex shader editor.");
|
||||
|
||||
vsEditor.insertText(" ", { line: 1, ch: 0 });
|
||||
is(vsEditor.getText(1), " precision lowp float;", "Typed space.");
|
||||
checkHasVertFirstError(false, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks removed while typing in the vertex shader editor.");
|
||||
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
info("Error marks were re-added after recompiling the vertex shader.");
|
||||
|
||||
fsEditor.replaceText("vec4", { line: 2, ch: 14 }, { line: 2, ch: 18 });
|
||||
let fragError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks added in the fragment shader editor.");
|
||||
|
||||
fsEditor.insertText(" ", { line: 1, ch: 0 });
|
||||
is(fsEditor.getText(1), " precision lowp float;", "Typed space.");
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(false, fragError);
|
||||
info("Error marks removed while typing in the fragment shader editor.");
|
||||
|
||||
let fragError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks were re-added after recompiling the fragment shader.");
|
||||
|
||||
vsEditor.replaceText("2", { line: 3, ch: 19 }, { line: 3, ch: 20 });
|
||||
checkHasVertFirstError(false, vertError);
|
||||
checkHasVertSecondError(false, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks removed while typing in the vertex shader editor again.");
|
||||
|
||||
let vertError = yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
checkHasVertFirstError(true, vertError);
|
||||
checkHasVertSecondError(true, vertError);
|
||||
checkHasFragError(true, fragError);
|
||||
info("Error marks were re-added after recompiling the fragment shader again.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
||||
function checkHasVertFirstError(bool, error) {
|
||||
ok(error, "Vertex shader compiled with errors.");
|
||||
isnot(error.link, "", "The linkage status should not be empty.");
|
||||
|
||||
let line = 7;
|
||||
info("Checking first vertex shader error on line " + line + "...");
|
||||
|
||||
is(vsEditor.hasMarker(line, "errors", "error"), bool,
|
||||
"Error is " + (bool ? "" : "not ") + "shown in the editor's gutter.");
|
||||
is(vsEditor.hasLineClass(line, "error-line"), bool,
|
||||
"Error style is " + (bool ? "" : "not ") + "applied to the faulty line.");
|
||||
|
||||
let parsed = ShadersEditorsView._errors.vs;
|
||||
is(parsed.length >= 1, bool,
|
||||
"There's " + (bool ? ">= 1" : "< 1") + " parsed vertex shader error(s).");
|
||||
|
||||
if (bool) {
|
||||
is(parsed[0].line, line,
|
||||
"The correct line was parsed.");
|
||||
is(parsed[0].messages.length, 2,
|
||||
"There are 2 parsed messages.");
|
||||
ok(parsed[0].messages[0].contains("'constructor' : too many arguments"),
|
||||
"The correct first message was parsed.");
|
||||
ok(parsed[0].messages[1].contains("'assign' : cannot convert from"),
|
||||
"The correct second message was parsed.");
|
||||
}
|
||||
}
|
||||
|
||||
function checkHasVertSecondError(bool, error) {
|
||||
ok(error, "Vertex shader compiled with errors.");
|
||||
isnot(error.link, "", "The linkage status should not be empty.");
|
||||
|
||||
let line = 8;
|
||||
info("Checking second vertex shader error on line " + line + "...");
|
||||
|
||||
is(vsEditor.hasMarker(line, "errors", "error"), bool,
|
||||
"Error is " + (bool ? "" : "not ") + "shown in the editor's gutter.");
|
||||
is(vsEditor.hasLineClass(line, "error-line"), bool,
|
||||
"Error style is " + (bool ? "" : "not ") + "applied to the faulty line.");
|
||||
|
||||
let parsed = ShadersEditorsView._errors.vs;
|
||||
is(parsed.length >= 2, bool,
|
||||
"There's " + (bool ? ">= 2" : "< 2") + " parsed vertex shader error(s).");
|
||||
|
||||
if (bool) {
|
||||
is(parsed[1].line, line,
|
||||
"The correct line was parsed.");
|
||||
is(parsed[1].messages.length, 1,
|
||||
"There is 1 parsed message.");
|
||||
ok(parsed[1].messages[0].contains("'assign' : cannot convert from"),
|
||||
"The correct message was parsed.");
|
||||
}
|
||||
}
|
||||
|
||||
function checkHasFragError(bool, error) {
|
||||
ok(error, "Fragment shader compiled with errors.");
|
||||
isnot(error.link, "", "The linkage status should not be empty.");
|
||||
|
||||
let line = 5;
|
||||
info("Checking first vertex shader error on line " + line + "...");
|
||||
|
||||
is(fsEditor.hasMarker(line, "errors", "error"), bool,
|
||||
"Error is " + (bool ? "" : "not ") + "shown in the editor's gutter.");
|
||||
is(fsEditor.hasLineClass(line, "error-line"), bool,
|
||||
"Error style is " + (bool ? "" : "not ") + "applied to the faulty line.");
|
||||
|
||||
let parsed = ShadersEditorsView._errors.fs;
|
||||
is(parsed.length >= 1, bool,
|
||||
"There's " + (bool ? ">= 2" : "< 1") + " parsed fragment shader error(s).");
|
||||
|
||||
if (bool) {
|
||||
is(parsed[0].line, line,
|
||||
"The correct line was parsed.");
|
||||
is(parsed[0].messages.length, 1,
|
||||
"There is 1 parsed message.");
|
||||
ok(parsed[0].messages[0].contains("'constructor' : too many arguments"),
|
||||
"The correct message was parsed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if error tooltips can be opened from the editor's gutter when there's
|
||||
* a shader compilation error.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, panel] = yield initShaderEditor(SIMPLE_CANVAS_URL);
|
||||
let { gFront, EVENTS, ShadersEditorsView } = panel.panelWin;
|
||||
|
||||
reload(target);
|
||||
yield once(gFront, "program-linked");
|
||||
|
||||
let vsEditor = yield ShadersEditorsView._getEditor("vs");
|
||||
let fsEditor = yield ShadersEditorsView._getEditor("fs");
|
||||
|
||||
vsEditor.replaceText("vec3", { line: 7, ch: 22 }, { line: 7, ch: 26 });
|
||||
yield once(panel.panelWin, EVENTS.SHADER_COMPILED);
|
||||
|
||||
// Synthesizing 'mouseenter' events doesn't work, hack around this by
|
||||
// manually calling the event listener with the expected arguments.
|
||||
let editorDocument = vsEditor.container.contentDocument;
|
||||
let marker = editorDocument.querySelector(".error");
|
||||
let parsed = ShadersEditorsView._errors.vs[0].messages;
|
||||
ShadersEditorsView._onMarkerMouseEnter(7, marker, parsed);
|
||||
|
||||
let tooltip = marker._markerErrorsTooltip;
|
||||
ok(tooltip, "A tooltip was created successfully.");
|
||||
|
||||
let content = tooltip.content;
|
||||
ok(tooltip.content,
|
||||
"Some tooltip's content was set.");
|
||||
is(tooltip.content.className, "devtools-tooltip-simple-text-container",
|
||||
"The tooltip's content container was created correctly.");
|
||||
|
||||
let messages = content.childNodes;
|
||||
is(messages.length, 2,
|
||||
"There are two messages displayed in the tooltip.");
|
||||
is(messages[0].className, "devtools-tooltip-simple-text",
|
||||
"The first message was created correctly.");
|
||||
is(messages[1].className, "devtools-tooltip-simple-text",
|
||||
"The second message was created correctly.");
|
||||
|
||||
ok(messages[0].textContent.contains("'constructor' : too many arguments"),
|
||||
"The first message contains the correct text.");
|
||||
ok(messages[1].textContent.contains("'assign' : cannot convert"),
|
||||
"The second message contains the correct text.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
function once(aTarget, aEvent) {
|
||||
let deferred = promise.defer();
|
||||
aTarget.once(aEvent, (aName, aData) => deferred.resolve(aData));
|
||||
return deferred.promise;
|
||||
}
|
@ -55,9 +55,9 @@ function ifWebGLSupported() {
|
||||
is(getBlackBoxCheckbox(panel, 1).checked, true,
|
||||
"The second blackbox checkbox should still be checked.");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The first program was correctly blackboxed.");
|
||||
|
||||
@ -72,35 +72,35 @@ function ifWebGLSupported() {
|
||||
is(getBlackBoxCheckbox(panel, 1).checked, false,
|
||||
"The second blackbox checkbox should now be unchecked.");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The second program was correctly blackboxed.");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "Highlighting didn't work while blackboxed (1).");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "Highlighting didn't work while blackboxed (2).");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 0 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "Highlighting didn't work while blackboxed (3).");
|
||||
|
||||
getBlackBoxCheckbox(panel, 0).click();
|
||||
@ -121,7 +121,7 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The two programs were correctly unblackboxed.");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
@ -129,8 +129,8 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The first program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
@ -138,7 +138,7 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The second program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
@ -35,7 +35,7 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
@ -43,8 +43,8 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The first program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onShaderMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
@ -52,7 +52,7 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The second program was correctly highlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
@ -60,7 +60,7 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The two programs were correctly unhighlighted.");
|
||||
|
||||
ShadersListView._onShaderMouseEnter({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
ShadersListView._onProgramMouseEnter({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
@ -68,7 +68,7 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
ok(true, "The two programs were left unchanged after hovering a blackbox checkbox.");
|
||||
|
||||
ShadersListView._onShaderMouseLeave({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
ShadersListView._onProgramMouseLeave({ target: getBlackBoxCheckbox(panel, 0) });
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1");
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 255, b: 255, a: 255 }, true, "#canvas2");
|
||||
|
@ -2,7 +2,8 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if editing a vertex and a fragment shader works properly.
|
||||
* Tests if editing a vertex and a fragment shader would permanently store
|
||||
* their new source on the backend and reshow it in the frontend when required.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
|
@ -2,8 +2,8 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the highlight/unhighlight operations on program actors
|
||||
* work as expected.
|
||||
* Tests that the highlight/unhighlight and blackbox/unblackbox operations on
|
||||
* program actors work as expected.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
@ -17,19 +17,31 @@ function ifWebGLSupported() {
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are correct before highlighting.");
|
||||
ok(true, "The top left pixel color was correct before highlighting.");
|
||||
ok(true, "The corner pixel colors are correct before highlighting.");
|
||||
|
||||
yield programActor.highlight([0, 0, 1, 1]);
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are preserved after highlighting.");
|
||||
ok(true, "The top left pixel color is correct after highlighting.");
|
||||
ok(true, "The corner pixel colors are correct after highlighting.");
|
||||
|
||||
yield programActor.unhighlight();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are correct after unhighlighting.");
|
||||
ok(true, "The top left pixel color is correct after unhighlighting.");
|
||||
ok(true, "The corner pixel colors are correct after unhighlighting.");
|
||||
|
||||
yield programActor.blackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are preserved after blackboxing.");
|
||||
ok(true, "The corner pixel colors are correct after blackboxing.");
|
||||
|
||||
yield programActor.unblackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true);
|
||||
yield checkShaderSource("The shader sources are correct after unblackboxing.");
|
||||
ok(true, "The corner pixel colors are correct after unblackboxing.");
|
||||
|
||||
function checkShaderSource(aMessage) {
|
||||
return Task.spawn(function() {
|
||||
|
@ -0,0 +1,47 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the blackbox/unblackbox operations work as expected with
|
||||
* overlapping geometry.
|
||||
*/
|
||||
|
||||
function ifWebGLSupported() {
|
||||
let [target, debuggee, front] = yield initBackend(OVERLAPPING_GEOMETRY_CANVAS_URL);
|
||||
front.setup({ reload: true });
|
||||
|
||||
let firstProgramActor = yield once(front, "program-linked");
|
||||
let secondProgramActor = yield once(front, "program-linked");
|
||||
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct before blackboxing.");
|
||||
|
||||
yield firstProgramActor.blackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after blackboxing (1).");
|
||||
|
||||
yield firstProgramActor.unblackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after unblackboxing (1).");
|
||||
|
||||
yield secondProgramActor.blackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after blackboxing (2).");
|
||||
|
||||
yield secondProgramActor.unblackbox();
|
||||
yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 255, b: 255, a: 255 }, true);
|
||||
yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true);
|
||||
ok(true, "The corner vs. center pixel colors are correct after unblackboxing (2).");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
}
|
120
browser/devtools/shadereditor/test/doc_overlapping-geometry.html
Normal file
120
browser/devtools/shadereditor/test/doc_overlapping-geometry.html
Normal file
@ -0,0 +1,120 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>WebGL editor test page</title>
|
||||
|
||||
<script id="shader-vs" type="x-shader/x-vertex">
|
||||
precision lowp float;
|
||||
attribute vec3 aVertexPosition;
|
||||
uniform float uDepth;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = vec4(aVertexPosition, uDepth);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="shader-fs-0" type="x-shader/x-fragment">
|
||||
precision lowp float;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="shader-fs-1" type="x-shader/x-fragment">
|
||||
precision lowp float;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="canvas" width="128" height="128"></canvas>
|
||||
|
||||
<script type="text/javascript;version=1.8">
|
||||
"use strict";
|
||||
|
||||
let canvas, gl;
|
||||
let program = [];
|
||||
let squareVerticesPositionBuffer;
|
||||
let vertexPositionAttribute = [];
|
||||
let depthUniform = [];
|
||||
|
||||
window.onload = function() {
|
||||
canvas = document.querySelector("canvas");
|
||||
gl = canvas.getContext("webgl");
|
||||
gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
initProgram(0);
|
||||
initProgram(1);
|
||||
initBuffers();
|
||||
drawScene();
|
||||
}
|
||||
|
||||
function initProgram(i) {
|
||||
let vertexShader = getShader("shader-vs");
|
||||
let fragmentShader = getShader("shader-fs-" + i);
|
||||
|
||||
program[i] = gl.createProgram();
|
||||
gl.attachShader(program[i], vertexShader);
|
||||
gl.attachShader(program[i], fragmentShader);
|
||||
gl.linkProgram(program[i]);
|
||||
|
||||
vertexPositionAttribute[i] = gl.getAttribLocation(program[i], "aVertexPosition");
|
||||
gl.enableVertexAttribArray(vertexPositionAttribute[i]);
|
||||
|
||||
depthUniform[i] = gl.getUniformLocation(program[i], "uDepth");
|
||||
}
|
||||
|
||||
function getShader(id) {
|
||||
let script = document.getElementById(id);
|
||||
let source = script.textContent;
|
||||
let shader;
|
||||
|
||||
if (script.type == "x-shader/x-fragment") {
|
||||
shader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
} else if (script.type == "x-shader/x-vertex") {
|
||||
shader = gl.createShader(gl.VERTEX_SHADER);
|
||||
}
|
||||
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
function initBuffers() {
|
||||
squareVerticesPositionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
||||
1.0, 1.0, 0.0,
|
||||
-1.0, 1.0, 0.0,
|
||||
1.0, -1.0, 0.0,
|
||||
-1.0, -1.0, 0.0
|
||||
]), gl.STATIC_DRAW);
|
||||
}
|
||||
|
||||
function drawScene() {
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesPositionBuffer);
|
||||
gl.vertexAttribPointer(vertexPositionAttribute[i], 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
gl.useProgram(program[i]);
|
||||
gl.uniform1f(depthUniform[i], i + 1);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(drawScene);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -27,6 +27,7 @@ const EXAMPLE_URL = "http://example.com/browser/browser/devtools/shadereditor/te
|
||||
const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html";
|
||||
const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html";
|
||||
const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html";
|
||||
const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html";
|
||||
|
||||
// All tests are asynchronous.
|
||||
waitForExplicitFinish();
|
||||
|
@ -95,6 +95,10 @@ function Tooltip(doc) {
|
||||
module.exports.Tooltip = Tooltip;
|
||||
|
||||
Tooltip.prototype = {
|
||||
defaultPosition: "before_start",
|
||||
defaultOffsetX: 0,
|
||||
defaultOffsetY: 0,
|
||||
|
||||
/**
|
||||
* Show the tooltip. It might be wise to append some content first if you
|
||||
* don't want the tooltip to be empty. You may access the content of the
|
||||
@ -105,9 +109,12 @@ Tooltip.prototype = {
|
||||
* https://developer.mozilla.org/en-US/docs/XUL/PopupGuide/Positioning
|
||||
* Defaults to before_start
|
||||
*/
|
||||
show: function(anchor, position="before_start") {
|
||||
show: function(anchor,
|
||||
position = this.defaultPosition,
|
||||
x = this.defaultOffsetX,
|
||||
y = this.defaultOffsetY) {
|
||||
this.panel.hidden = false;
|
||||
this.panel.openPopup(anchor, position);
|
||||
this.panel.openPopup(anchor, position, x, y);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -258,11 +265,44 @@ Tooltip.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with an image, displayed over a tiled background useful
|
||||
* for transparent images.
|
||||
* Also adds the image dimension as a label at the bottom.
|
||||
* Sets some text as the content of this tooltip.
|
||||
*
|
||||
* @param string[] messages
|
||||
* A list of text messages.
|
||||
*/
|
||||
setImageContent: function(imageUrl, maxDim=400) {
|
||||
setTextContent: function(...messages) {
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.className = "devtools-tooltip-simple-text-container";
|
||||
vbox.setAttribute("flex", "1");
|
||||
|
||||
for (let text of messages) {
|
||||
let description = this.doc.createElement("description");
|
||||
description.setAttribute("flex", "1");
|
||||
description.className = "devtools-tooltip-simple-text";
|
||||
description.textContent = text;
|
||||
vbox.appendChild(description);
|
||||
}
|
||||
|
||||
this.content = vbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with an image, displayed over a tiled background useful
|
||||
* for transparent images. Also adds the image dimension as a label at the
|
||||
* bottom.
|
||||
* @param {string} imageUrl
|
||||
* The url to load the image from
|
||||
* @param {Object} options
|
||||
* The following options are supported:
|
||||
* - resized : whether or not the image identified by imageUrl has been
|
||||
* resized before this function was called.
|
||||
* - naturalWidth/naturalHeight : the original size of the image before
|
||||
* it was resized, if if was resized before this function was called.
|
||||
* If not provided, will be measured on the loaded image.
|
||||
* - maxDim : if the image should be resized before being shown, pass
|
||||
* a number here
|
||||
*/
|
||||
setImageContent: function(imageUrl, options={}) {
|
||||
// Main container
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.setAttribute("align", "center")
|
||||
@ -279,9 +319,9 @@ Tooltip.prototype = {
|
||||
// Display the image
|
||||
let image = this.doc.createElement("image");
|
||||
image.setAttribute("src", imageUrl);
|
||||
if (maxDim) {
|
||||
image.style.maxWidth = maxDim + "px";
|
||||
image.style.maxHeight = maxDim + "px";
|
||||
if (options.maxDim) {
|
||||
image.style.maxWidth = options.maxDim + "px";
|
||||
image.style.maxHeight = options.maxDim + "px";
|
||||
}
|
||||
tiles.appendChild(image);
|
||||
|
||||
@ -294,11 +334,9 @@ Tooltip.prototype = {
|
||||
imgObj.onload = null;
|
||||
|
||||
// Display dimensions
|
||||
label.textContent = imgObj.naturalWidth + " x " + imgObj.naturalHeight;
|
||||
if (imgObj.naturalWidth > maxDim ||
|
||||
imgObj.naturalHeight > maxDim) {
|
||||
label.textContent += " *";
|
||||
}
|
||||
let w = options.naturalWidth || imgObj.naturalWidth;
|
||||
let h = options.naturalHeight || imgObj.naturalHeight;
|
||||
label.textContent = w + " x " + h;
|
||||
}
|
||||
},
|
||||
|
||||
@ -309,7 +347,9 @@ Tooltip.prototype = {
|
||||
setCssBackgroundImageContent: function(cssBackground, sheetHref, maxDim=400) {
|
||||
let uri = getBackgroundImageUri(cssBackground, sheetHref);
|
||||
if (uri) {
|
||||
this.setImageContent(uri, maxDim);
|
||||
this.setImageContent(uri, {
|
||||
maxDim: maxDim
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,15 +1,25 @@
|
||||
/* 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/. */
|
||||
|
||||
.errors,
|
||||
.breakpoints {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.breakpoint, .debugLocation, .breakpoint-debugLocation {
|
||||
.error, .breakpoint, .debugLocation, .breakpoint-debugLocation {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: 12px;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-image: url("chrome://browser/skin/devtools/orion-error.png");
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.breakpoint {
|
||||
@ -23,4 +33,8 @@
|
||||
.breakpoint.debugLocation {
|
||||
background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"),
|
||||
url("chrome://browser/skin/devtools/orion-breakpoint.png");
|
||||
}
|
||||
}
|
||||
|
||||
.error-line {
|
||||
background: rgba(255,0,0,0.2);
|
||||
}
|
||||
|
@ -6,38 +6,6 @@
|
||||
|
||||
const dbginfo = new WeakMap();
|
||||
|
||||
// Private functions
|
||||
|
||||
/**
|
||||
* Adds a marker to the breakpoints gutter.
|
||||
* Type should be either a 'breakpoint' or a 'debugLocation'.
|
||||
*/
|
||||
function addMarker(cm, line, type) {
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
if (info.gutterMarkers)
|
||||
return void info.gutterMarkers.breakpoints.classList.add(type);
|
||||
|
||||
let mark = cm.getWrapperElement().ownerDocument.createElement("div");
|
||||
mark.className = type;
|
||||
mark.innerHTML = "";
|
||||
|
||||
cm.setGutterMarker(info.line, "breakpoints", mark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a marker from the breakpoints gutter.
|
||||
* Type should be either a 'breakpoint' or a 'debugLocation'.
|
||||
*/
|
||||
function removeMarker(cm, line, type) {
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
if (!info || !info.gutterMarkers)
|
||||
return;
|
||||
|
||||
info.gutterMarkers.breakpoints.classList.remove(type);
|
||||
}
|
||||
|
||||
// These functions implement search within the debugger. Since
|
||||
// search in the debugger is different from other components,
|
||||
// we can't use search.js CodeMirror addon. This is a slightly
|
||||
@ -155,7 +123,7 @@ function addBreakpoint(ctx, line, cond) {
|
||||
let meta = dbginfo.get(ed);
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
addMarker(cm, line, "breakpoint");
|
||||
ed.addMarker(line, "breakpoints", "breakpoint");
|
||||
meta.breakpoints[line] = { condition: cond };
|
||||
|
||||
info.handle.on("delete", function onDelete() {
|
||||
@ -179,7 +147,7 @@ function removeBreakpoint(ctx, line) {
|
||||
let info = cm.lineInfo(line);
|
||||
|
||||
meta.breakpoints[info.line] = null;
|
||||
removeMarker(cm, info.line, "breakpoint");
|
||||
ed.removeMarker(info.line, "breakpoints", "breakpoint");
|
||||
ed.emit("breakpointRemoved", line);
|
||||
}
|
||||
|
||||
@ -203,11 +171,11 @@ function getBreakpoints(ctx) {
|
||||
* display the line on which the Debugger is currently paused.
|
||||
*/
|
||||
function setDebugLocation(ctx, line) {
|
||||
let { ed, cm } = ctx;
|
||||
let { ed } = ctx;
|
||||
let meta = dbginfo.get(ed);
|
||||
|
||||
meta.debugLocation = line;
|
||||
addMarker(cm, line, "debugLocation");
|
||||
ed.addMarker(line, "breakpoints", "debugLocation");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,11 +194,11 @@ function getDebugLocation(ctx) {
|
||||
* also removes a visual anchor from the breakpoints gutter.
|
||||
*/
|
||||
function clearDebugLocation(ctx) {
|
||||
let { ed, cm } = ctx;
|
||||
let { ed } = ctx;
|
||||
let meta = dbginfo.get(ed);
|
||||
|
||||
if (meta.debugLocation != null) {
|
||||
removeMarker(cm, meta.debugLocation, "debugLocation");
|
||||
ed.removeMarker(meta.debugLocation, "breakpoints", "debugLocation");
|
||||
meta.debugLocation = null;
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +340,7 @@ Editor.prototype = {
|
||||
* Replaces contents of a text area within the from/to {line, ch}
|
||||
* range. If neither from nor to arguments are provided works
|
||||
* exactly like setText. If only from object is provided, inserts
|
||||
* text at that point.
|
||||
* text at that point, *overwriting* as many characters as needed.
|
||||
*/
|
||||
replaceText: function (value, from, to) {
|
||||
let cm = editors.get(this);
|
||||
@ -356,6 +356,15 @@ Editor.prototype = {
|
||||
cm.replaceRange(value, from, to);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts text at the specified {line, ch} position, shifting existing
|
||||
* contents as necessary.
|
||||
*/
|
||||
insertText: function (value, at) {
|
||||
let cm = editors.get(this);
|
||||
cm.replaceRange(value, at, at);
|
||||
},
|
||||
|
||||
/**
|
||||
* Deselects contents of the text area.
|
||||
*/
|
||||
@ -370,7 +379,7 @@ Editor.prototype = {
|
||||
* Marks the contents as clean and returns the current
|
||||
* version number.
|
||||
*/
|
||||
markClean: function () {
|
||||
setClean: function () {
|
||||
let cm = editors.get(this);
|
||||
this.version = cm.changeGeneration();
|
||||
return this.version;
|
||||
@ -519,6 +528,120 @@ Editor.prototype = {
|
||||
this.setFirstVisibleLine(topLine);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a marker of a specified class exists in a line's gutter.
|
||||
*/
|
||||
hasMarker: function (line, gutterName, markerClass) {
|
||||
let cm = editors.get(this);
|
||||
let info = cm.lineInfo(line);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
let gutterMarkers = info.gutterMarkers;
|
||||
if (!gutterMarkers)
|
||||
return false;
|
||||
|
||||
let marker = gutterMarkers[gutterName];
|
||||
if (!marker)
|
||||
return false;
|
||||
|
||||
return marker.classList.contains(markerClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a marker with a specified class to a line's gutter. If another marker
|
||||
* exists on that line, the new marker class is added to its class list.
|
||||
*/
|
||||
addMarker: function (line, gutterName, markerClass) {
|
||||
let cm = editors.get(this);
|
||||
let info = cm.lineInfo(line);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
let gutterMarkers = info.gutterMarkers;
|
||||
if (gutterMarkers) {
|
||||
let marker = gutterMarkers[gutterName];
|
||||
if (marker) {
|
||||
marker.classList.add(markerClass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let marker = cm.getWrapperElement().ownerDocument.createElement("div");
|
||||
marker.className = markerClass;
|
||||
cm.setGutterMarker(info.line, gutterName, marker);
|
||||
},
|
||||
|
||||
/**
|
||||
* The reverse of addMarker. Removes a marker of a specified class from a
|
||||
* line's gutter.
|
||||
*/
|
||||
removeMarker: function (line, gutterName, markerClass) {
|
||||
if (!this.hasMarker(line, gutterName, markerClass))
|
||||
return;
|
||||
|
||||
let cm = editors.get(this);
|
||||
cm.lineInfo(line).gutterMarkers[gutterName].classList.remove(markerClass);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all gutter markers in the gutter with the given name.
|
||||
*/
|
||||
removeAllMarkers: function (gutterName) {
|
||||
let cm = editors.get(this);
|
||||
cm.clearGutter(gutterName);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles attaching a set of events listeners on a marker. They should
|
||||
* be passed as an object literal with keys as event names and values as
|
||||
* function listeners. The line number, marker node and optional data
|
||||
* will be passed as arguments to the function listener.
|
||||
*
|
||||
* You don't need to worry about removing these event listeners.
|
||||
* They're automatically orphaned when clearing markers.
|
||||
*/
|
||||
setMarkerListeners: function(line, gutterName, markerClass, events, data) {
|
||||
if (!this.hasMarker(line, gutterName, markerClass))
|
||||
return;
|
||||
|
||||
let cm = editors.get(this);
|
||||
let marker = cm.lineInfo(line).gutterMarkers[gutterName];
|
||||
|
||||
for (let name in events) {
|
||||
let listener = events[name].bind(this, line, marker, data);
|
||||
marker.addEventListener(name, listener);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a line is decorated using the specified class name.
|
||||
*/
|
||||
hasLineClass: function (line, className) {
|
||||
let cm = editors.get(this);
|
||||
let info = cm.lineInfo(line);
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
return info.wrapClass == className;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set a CSS class name for the given line, including the text and gutter.
|
||||
*/
|
||||
addLineClass: function (line, className) {
|
||||
let cm = editors.get(this);
|
||||
cm.addLineClass(line, "wrap", className);
|
||||
},
|
||||
|
||||
/**
|
||||
* The reverse of addLineClass.
|
||||
*/
|
||||
removeLineClass: function (line, className) {
|
||||
let cm = editors.get(this);
|
||||
cm.removeLineClass(line, "wrap", className);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.container = null;
|
||||
this.config = null;
|
||||
|
@ -376,7 +376,7 @@ StyleSheetEditor.prototype = {
|
||||
if (callback) {
|
||||
callback(returnFile);
|
||||
}
|
||||
this.sourceEditor.markClean();
|
||||
this.sourceEditor.setClean();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
|
@ -34,8 +34,8 @@ validator.invalidAppType=Unknown app type: '%S'.
|
||||
validator.invalidHostedPriviledges=Hosted App can't be type '%S'.
|
||||
validator.noCertifiedSupport='certified' apps are not fully supported on the App manager.
|
||||
validator.nonAbsoluteLaunchPath=Launch path has to be an absolute path starting with '/': '%S'
|
||||
validator.invalidLaunchPath=Unable to access to app starting document '%S'
|
||||
# LOCALIZATION NOTE (validator.invalidLaunchPathBadHttpCode): %1$S is the URI of
|
||||
validator.accessFailedLaunchPath=Unable to access the app starting document '%S'
|
||||
# LOCALIZATION NOTE (validator.accessFailedLaunchPathBadHttpCode): %1$S is the URI of
|
||||
# the launch document, %2$S is the http error code.
|
||||
validator.invalidLaunchPathBadHttpCode=Unable to access to app starting document '%1$S', got HTTP code %2$S
|
||||
validator.accessFailedLaunchPathBadHttpCode=Unable to access the app starting document '%1$S', got HTTP code %2$S
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
- 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/. -->
|
||||
|
||||
<!ENTITY popups.label "Pop-ups">
|
||||
|
||||
<!ENTITY blockPopups.label "Block pop-up windows">
|
||||
<!ENTITY blockPopups.accesskey "B">
|
||||
<!ENTITY popupExceptions.label "Exceptions…">
|
||||
|
@ -2,6 +2,8 @@
|
||||
- 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/. -->
|
||||
|
||||
<!ENTITY general.label "General">
|
||||
|
||||
<!ENTITY warnAddonInstall.label "Warn me when sites try to install add-ons">
|
||||
<!ENTITY warnAddonInstall.accesskey "W">
|
||||
|
||||
|
@ -44,6 +44,8 @@ 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() {
|
||||
@ -60,7 +62,7 @@ let IndexedDB = {
|
||||
}
|
||||
|
||||
prompt.prompt({
|
||||
type: type,
|
||||
types: types,
|
||||
uri: Services.io.newURI(payload.location, null, null),
|
||||
window: null,
|
||||
element: aMessage.target,
|
||||
|
@ -56,8 +56,8 @@ ContentPermissionPrompt.prototype = {
|
||||
return chromeWin.Browser.getNotificationBox(request.element);
|
||||
},
|
||||
|
||||
handleExistingPermission: function handleExistingPermission(request) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type);
|
||||
handleExistingPermission: function handleExistingPermission(request, type) {
|
||||
let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type);
|
||||
if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
|
||||
request.allow();
|
||||
return true;
|
||||
@ -70,20 +70,28 @@ 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))
|
||||
if (this.handleExistingPermission(request, perm.type))
|
||||
return;
|
||||
|
||||
let pm = Services.perms;
|
||||
let notificationBox = this.getNotificationBoxForRequest(request);
|
||||
let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
let notification = notificationBox.getNotificationWithValue(request.type);
|
||||
let notification = notificationBox.getNotificationWithValue(perm.type);
|
||||
if (notification)
|
||||
return;
|
||||
|
||||
let entityName = kEntities[request.type];
|
||||
let icon = kIcons[request.type] || "";
|
||||
let entityName = kEntities[perm.type];
|
||||
let icon = kIcons[perm.type] || "";
|
||||
|
||||
let buttons = [{
|
||||
label: browserBundle.GetStringFromName(entityName + ".allow"),
|
||||
@ -96,7 +104,7 @@ ContentPermissionPrompt.prototype = {
|
||||
label: browserBundle.GetStringFromName("contentPermissions.alwaysForSite"),
|
||||
accessKey: "",
|
||||
callback: function(notification) {
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
request.allow();
|
||||
}
|
||||
},
|
||||
@ -104,7 +112,7 @@ ContentPermissionPrompt.prototype = {
|
||||
label: browserBundle.GetStringFromName("contentPermissions.neverForSite"),
|
||||
accessKey: "",
|
||||
callback: function(notification) {
|
||||
Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION);
|
||||
request.cancel();
|
||||
}
|
||||
}];
|
||||
@ -112,12 +120,12 @@ ContentPermissionPrompt.prototype = {
|
||||
let message = browserBundle.formatStringFromName(entityName + ".wantsTo",
|
||||
[request.principal.URI.host], 1);
|
||||
let newBar = notificationBox.appendNotification(message,
|
||||
request.type,
|
||||
perm.type,
|
||||
icon,
|
||||
notificationBox.PRIORITY_WARNING_MEDIUM,
|
||||
buttons);
|
||||
|
||||
if (request.type == "geolocation") {
|
||||
if (perm.type == "geolocation") {
|
||||
// Add the "learn more" link.
|
||||
let link = newBar.ownerDocument.createElement("label");
|
||||
link.setAttribute("value", browserBundle.GetStringFromName("geolocation.learnMore"));
|
||||
|
@ -255,7 +255,11 @@ this.UITour = {
|
||||
if (uri.schemeIs("chrome"))
|
||||
return true;
|
||||
|
||||
if (!uri.schemeIs("https"))
|
||||
let allowedSchemes = new Set(["https"]);
|
||||
if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
|
||||
allowedSchemes.add("http");
|
||||
|
||||
if (!allowedSchemes.has(uri.scheme))
|
||||
return false;
|
||||
|
||||
this.importPermissions();
|
||||
|
@ -32,13 +32,12 @@ function is_element_hidden(element, msg) {
|
||||
ok(is_hidden(element), msg);
|
||||
}
|
||||
|
||||
function loadTestPage(callback, untrustedHost = false) {
|
||||
function loadTestPage(callback, host = "https://example.com/") {
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
|
||||
let url = getRootDirectory(gTestPath) + "uitour.html";
|
||||
if (untrustedHost)
|
||||
url = url.replace("chrome://mochitests/content/", "http://example.com/");
|
||||
url = url.replace("chrome://mochitests/content/", host);
|
||||
|
||||
gTestTab = gBrowser.addTab(url);
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
@ -55,6 +54,8 @@ function loadTestPage(callback, untrustedHost = false) {
|
||||
|
||||
function test() {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
let testUri = Services.io.newURI("http://example.com", null, null);
|
||||
Services.perms.add(testUri, "uitour", Services.perms.ALLOW_ACTION);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
@ -65,6 +66,7 @@ function test() {
|
||||
gBrowser.removeTab(gTestTab);
|
||||
delete window.gTestTab;
|
||||
Services.prefs.clearUserPref("browser.uitour.enabled", true);
|
||||
Services.perms.remove("example.com", "uitour");
|
||||
});
|
||||
|
||||
function done() {
|
||||
@ -98,6 +100,41 @@ function test() {
|
||||
}
|
||||
|
||||
let tests = [
|
||||
function test_untrusted_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a untrusted host");
|
||||
|
||||
done();
|
||||
}, "http://mochi.test:8888/");
|
||||
},
|
||||
function test_unsecure_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a unsecure host");
|
||||
|
||||
done();
|
||||
}, "http://example.com/");
|
||||
},
|
||||
function test_unsecure_host_override(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.requireSecure", false);
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_visible(highlight, "Highlight should be shown on a unsecure host when override pref is set");
|
||||
|
||||
Services.prefs.setBoolPref("browser.uitour.requireSecure", true);
|
||||
done();
|
||||
}, "http://example.com/");
|
||||
},
|
||||
function test_disabled(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", false);
|
||||
|
||||
@ -110,17 +147,6 @@ let tests = [
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
done();
|
||||
},
|
||||
function test_untrusted_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a untrusted domain");
|
||||
|
||||
done();
|
||||
}, true);
|
||||
},
|
||||
function test_highlight(done) {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
BIN
browser/themes/linux/devtools/orion-error.png
Normal file
BIN
browser/themes/linux/devtools/orion-error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
@ -105,7 +105,7 @@ browser.jar:
|
||||
skin/classic/browser/preferences/Options-sync.png (preferences/Options-sync.png)
|
||||
#endif
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
@ -143,6 +143,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
skin/classic/browser/devtools/breadcrumbs-scrollbutton.png (devtools/breadcrumbs-scrollbutton.png)
|
||||
|
@ -21,12 +21,12 @@
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
richlistitem label {
|
||||
#handlersView > richlistitem label {
|
||||
-moz-margin-start: 1px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
|
@ -6,95 +6,115 @@
|
||||
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
|
||||
#preferences-home {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.landingButton {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
.landingButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.landingButton-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.landingButton-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.preference-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.preference-icon[type="general"],
|
||||
.landingButton-icon[type="general"] {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="content"],
|
||||
.landingButton-icon[type="content"] {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="applications"],
|
||||
.landingButton-icon[type="applications"] {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="privacy"],
|
||||
.landingButton-icon[type="privacy"] {
|
||||
background-position: -128px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="security"],
|
||||
.landingButton-icon[type="security"] {
|
||||
background-position: -160px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="advanced"],
|
||||
.landingButton-icon[type="advanced"] {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="sync"],
|
||||
.landingButton-icon[type="sync"] {
|
||||
background-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 20px;
|
||||
font-size: 1.667rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 50px;
|
||||
background-color: rgba(192,199,210,0.7);
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 15px;
|
||||
-moz-box-align: center;
|
||||
.main-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
-moz-margin-end: -1px;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
margin-top: 41px;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
border-width: 1px;
|
||||
-moz-border-end-width: 0;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
padding: 9px 4px 10px;
|
||||
-moz-padding-end: 8px;
|
||||
-moz-box-align: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
color: WindowText;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
border-color: ThreeDShadow;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 1.5rem;
|
||||
-moz-padding-end: 24px;
|
||||
}
|
||||
|
||||
/* Maximize the size of the viewport when the window is small */
|
||||
@media (max-width: 800px) {
|
||||
.category-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 6px;
|
||||
-moz-margin-start: 6px;
|
||||
-moz-margin-end: 5px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
}
|
||||
|
||||
#category-general > .category-icon {
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
-moz-image-region: rect(0, 96px, 32px, 64px)
|
||||
}
|
||||
|
||||
#category-application > .category-icon {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px)
|
||||
}
|
||||
|
||||
#category-privacy > .category-icon {
|
||||
-moz-image-region: rect(0, 160px, 32px, 128px)
|
||||
}
|
||||
|
||||
#category-security > .category-icon {
|
||||
-moz-image-region: rect(0, 192px, 32px, 160px)
|
||||
}
|
||||
|
||||
#category-advanced > .category-icon {
|
||||
-moz-image-region: rect(0, 224px, 32px, 192px)
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#category-sync > .category-icon {
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* Applications Pane Styles */
|
||||
|
||||
#applications-content {
|
||||
|
BIN
browser/themes/osx/devtools/orion-error.png
Normal file
BIN
browser/themes/osx/devtools/orion-error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
@ -169,7 +169,7 @@ browser.jar:
|
||||
#endif
|
||||
skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png)
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
@ -232,6 +232,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
|
@ -14,12 +14,12 @@
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
richlistitem label {
|
||||
#handlersView > richlistitem label {
|
||||
-moz-margin-start: 3px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
|
@ -2,102 +2,119 @@
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
%include ../../shared.inc
|
||||
|
||||
@import url("chrome://global/skin/inContentUI.css");
|
||||
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
|
||||
#preferences-home {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.landingButton {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: vertical;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.landingButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.landingButton-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.landingButton-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.preference-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.preference-icon[type="general"],
|
||||
.landingButton-icon[type="general"] {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="content"],
|
||||
.landingButton-icon[type="content"] {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="applications"],
|
||||
.landingButton-icon[type="applications"] {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="privacy"],
|
||||
.landingButton-icon[type="privacy"] {
|
||||
background-position: -128px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="security"],
|
||||
.landingButton-icon[type="security"] {
|
||||
background-position: -160px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="advanced"],
|
||||
.landingButton-icon[type="advanced"] {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="sync"],
|
||||
.landingButton-icon[type="sync"] {
|
||||
background-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 20px;
|
||||
font-size: 1.667rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 50px;
|
||||
background-color: rgba(192,199,210,0.7);
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 15px;
|
||||
-moz-box-align: center;
|
||||
.main-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
-moz-margin-end: -1px;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
margin-top: 31px;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
color: #252F3B;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
padding: 10px 4px;
|
||||
-moz-padding-end: 8px;
|
||||
-moz-box-align: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: rgba(255, 255, 255, 0.35);
|
||||
color: -moz-dialogtext;
|
||||
border-color: rgba(50, 65, 92, 0.4);
|
||||
-moz-border-end-color: #C9CFD7;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 1.5rem;
|
||||
-moz-padding-end: 24px;
|
||||
}
|
||||
|
||||
/* Maximize the size of the viewport when the window is small */
|
||||
@media (max-width: 800px) {
|
||||
.category-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
-moz-margin-start: 6px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
}
|
||||
|
||||
#category-general > .category-icon {
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
-moz-image-region: rect(0, 96px, 32px, 64px)
|
||||
}
|
||||
|
||||
#category-application > .category-icon {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px)
|
||||
}
|
||||
|
||||
#category-privacy > .category-icon {
|
||||
-moz-image-region: rect(0, 160px, 32px, 128px)
|
||||
}
|
||||
|
||||
#category-security > .category-icon {
|
||||
-moz-image-region: rect(0, 192px, 32px, 160px)
|
||||
}
|
||||
|
||||
#category-advanced > .category-icon {
|
||||
-moz-image-region: rect(0, 224px, 32px, 192px)
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#category-sync > .category-icon {
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* Applications Pane Styles */
|
||||
|
||||
#applications-content {
|
||||
|
@ -121,11 +121,29 @@
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.devtools-tooltip.devtools-tooltip-panel .panel-arrowcontent {
|
||||
/* If the tooltip uses a <panel> XUL element instead */
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text {
|
||||
background: linear-gradient(1deg, transparent 0%, rgba(94,136,176,0.1) 100%);
|
||||
max-width: 400px;
|
||||
margin: 0 -4px; /* Compensate for the .panel-arrowcontent padding. */
|
||||
padding: 8px 12px;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text:first-child {
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text:last-child {
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
.devtools-tooltip-tiles {
|
||||
background-color: #eee;
|
||||
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
|
||||
|
BIN
browser/themes/windows/devtools/orion-error.png
Normal file
BIN
browser/themes/windows/devtools/orion-error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
@ -125,7 +125,7 @@ browser.jar:
|
||||
#endif
|
||||
skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png)
|
||||
* skin/classic/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/browser/social/services-16.png (social/services-16.png)
|
||||
@ -167,6 +167,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
@ -402,7 +403,7 @@ browser.jar:
|
||||
#endif
|
||||
skin/classic/aero/browser/preferences/saveFile.png (preferences/saveFile-aero.png)
|
||||
* skin/classic/aero/browser/preferences/preferences.css (preferences/preferences.css)
|
||||
skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
* skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
|
||||
skin/classic/aero/browser/preferences/applications.css (preferences/applications.css)
|
||||
skin/classic/aero/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
|
||||
skin/classic/aero/browser/social/services-16.png (social/services-16.png)
|
||||
@ -444,6 +445,7 @@ browser.jar:
|
||||
skin/classic/aero/browser/devtools/orion.css (devtools/orion.css)
|
||||
skin/classic/aero/browser/devtools/orion-container.css (devtools/orion-container.css)
|
||||
skin/classic/aero/browser/devtools/orion-task.png (devtools/orion-task.png)
|
||||
skin/classic/aero/browser/devtools/orion-error.png (devtools/orion-error.png)
|
||||
skin/classic/aero/browser/devtools/orion-breakpoint.png (devtools/orion-breakpoint.png)
|
||||
skin/classic/aero/browser/devtools/orion-debug-location.png (devtools/orion-debug-location.png)
|
||||
* skin/classic/aero/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
|
@ -21,12 +21,12 @@
|
||||
-moz-margin-end: 3px;
|
||||
}
|
||||
|
||||
richlistitem label {
|
||||
#handlersView > richlistitem label {
|
||||
-moz-margin-start: 1px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
#handlersView > richlistitem {
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
|
@ -6,98 +6,116 @@
|
||||
|
||||
@namespace html "http://www.w3.org/1999/xhtml";
|
||||
|
||||
#preferences-home {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#header {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.landingButton {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: vertical;
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.landingButton:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.landingButton-label {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.landingButton-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.preference-icon {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.preference-icon[type="general"],
|
||||
.landingButton-icon[type="general"] {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="content"],
|
||||
.landingButton-icon[type="content"] {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="applications"],
|
||||
.landingButton-icon[type="applications"] {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="privacy"],
|
||||
.landingButton-icon[type="privacy"] {
|
||||
background-position: -128px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="security"],
|
||||
.landingButton-icon[type="security"] {
|
||||
background-position: -160px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="advanced"],
|
||||
.landingButton-icon[type="advanced"] {
|
||||
background-position: -192px 0;
|
||||
}
|
||||
|
||||
.preference-icon[type="sync"],
|
||||
.landingButton-icon[type="sync"] {
|
||||
background-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
|
||||
caption {
|
||||
font-size: 20px;
|
||||
font-size: 1.667rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
height: 50px;
|
||||
background-color: rgba(192,199,210,0.7);
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin-bottom: 15px;
|
||||
-moz-box-align: center;
|
||||
.main-content {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
prefpane > .content-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Category List */
|
||||
|
||||
#categories {
|
||||
-moz-appearance: none;
|
||||
border: none;
|
||||
-moz-margin-end: -1px;
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
margin-top: 31px;
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-appearance: none;
|
||||
background-color: transparent;
|
||||
color: #252F3B;
|
||||
padding: 10px 4px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
-moz-padding-end: 8px;
|
||||
-moz-box-align: center;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
height: 52px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(ltr) {
|
||||
border-top-left-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.category:-moz-locale-dir(rtl) {
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.category[selected] {
|
||||
background-color: rgba(255, 255, 255, 0.4);
|
||||
color: #252F3B;
|
||||
border-color: #C3CEDF;
|
||||
-moz-border-end-color: #E2E9F2;
|
||||
}
|
||||
|
||||
.category-name {
|
||||
font-size: 1.5rem;
|
||||
-moz-padding-end: 24px;
|
||||
}
|
||||
|
||||
/* Maximize the size of the viewport when the window is small */
|
||||
@media (max-width: 800px) {
|
||||
.category-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: 0 6px;
|
||||
-moz-margin-start: 6px;
|
||||
-moz-margin-end: 5px;
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options.png");
|
||||
}
|
||||
|
||||
#category-general > .category-icon {
|
||||
-moz-image-region: rect(0, 32px, 32px, 0);
|
||||
}
|
||||
|
||||
#category-content > .category-icon {
|
||||
-moz-image-region: rect(0, 96px, 32px, 64px)
|
||||
}
|
||||
|
||||
#category-application > .category-icon {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px)
|
||||
}
|
||||
|
||||
#category-privacy > .category-icon {
|
||||
-moz-image-region: rect(0, 160px, 32px, 128px)
|
||||
}
|
||||
|
||||
#category-security > .category-icon {
|
||||
-moz-image-region: rect(0, 192px, 32px, 160px)
|
||||
}
|
||||
|
||||
#category-advanced > .category-icon {
|
||||
-moz-image-region: rect(0, 224px, 32px, 192px)
|
||||
}
|
||||
|
||||
%ifdef MOZ_SERVICES_SYNC
|
||||
#category-sync > .category-icon {
|
||||
list-style-image: url("chrome://browser/skin/preferences/Options-sync.png");
|
||||
}
|
||||
%endif
|
||||
|
||||
/* Applications Pane Styles */
|
||||
|
||||
#applications-content {
|
||||
|
@ -215,6 +215,8 @@
|
||||
#include "mozilla/dom/XPathEvaluator.h"
|
||||
#include "nsIDocumentEncoder.h"
|
||||
#include "nsIStructuredCloneContainer.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -10647,17 +10649,11 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest,
|
||||
nsIContentPermissionRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetType(nsACString& aType)
|
||||
nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
aType = "pointerLock";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -116,20 +116,21 @@ MOCHITEST_FILES = \
|
||||
test_volume.html \
|
||||
test_video_to_canvas.html \
|
||||
test_audiowrite.html \
|
||||
test_mediarecorder_creation.html \
|
||||
test_mediarecorder_avoid_recursion.html \
|
||||
test_mediarecorder_record_timeslice.html \
|
||||
test_mediarecorder_creation.html \
|
||||
test_mediarecorder_record_audiocontext.html \
|
||||
test_mediarecorder_record_stopms.html \
|
||||
test_mediarecorder_record_immediate_stop.html \
|
||||
test_mediarecorder_record_no_timeslice.html \
|
||||
test_mediarecorder_record_nosrc.html \
|
||||
test_mediarecorder_record_session.html \
|
||||
test_mediarecorder_record_stopms.html \
|
||||
test_mediarecorder_record_timeslice.html \
|
||||
test_mediarecorder_reload_crash.html \
|
||||
test_mediarecorder_state_transition.html \
|
||||
test_mozHasAudio.html \
|
||||
test_source_media.html \
|
||||
test_autoplay_contentEditable.html \
|
||||
test_decoder_disable.html \
|
||||
test_mediarecorder_record_no_timeslice.html \
|
||||
test_mediarecorder_reload_crash.html \
|
||||
test_mediarecorder_record_immediate_stop.html \
|
||||
test_mediarecorder_record_session.html \
|
||||
test_playback.html \
|
||||
test_seekLies.html \
|
||||
test_media_sniffer.html \
|
||||
|
170
content/media/test/test_mediarecorder_state_transition.html
Normal file
170
content/media/test/test_mediarecorder_state_transition.html
Normal file
@ -0,0 +1,170 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test MediaRecorder State Transition</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
// List of operation tests for media recorder objects to verify if running
|
||||
// these operations should result in an exception or not
|
||||
var operationTests = [
|
||||
{
|
||||
operations: ['stop'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['requestData'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['pause'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['resume'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['start'],
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
operations: ['start'],
|
||||
isValid: true,
|
||||
timeSlice: 200
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause'],
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause'],
|
||||
isValid: true,
|
||||
timeSlice: 200
|
||||
},
|
||||
{
|
||||
operations: ['start', 'start'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['start', 'resume'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['start', 'stop'],
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
operations: ['start', 'stop'],
|
||||
isValid: true,
|
||||
timeSlice: 200
|
||||
},
|
||||
{
|
||||
operations: ['start', 'requestData'],
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
operations: ['start', 'requestData'],
|
||||
isValid: true,
|
||||
timeSlice: 200
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause', 'stop'],
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause', 'start'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause', 'pause'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause', 'requestData'],
|
||||
isValid: false
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause', 'resume'],
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
operations: ['start', 'pause', 'resume'],
|
||||
isValid: true,
|
||||
timeSlice: 200
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Runs through each available state transition test by running all
|
||||
* available operations on a media recorder object. Then, we report
|
||||
* back if the test was expected through an exception or not.
|
||||
*
|
||||
* @param {MediaStream} testStream the media stream used for media recorder
|
||||
* operation tests
|
||||
*/
|
||||
function runStateTransitionTests(testStream) {
|
||||
for (operationTest of operationTests) {
|
||||
var mediaRecorder = new MediaRecorder(testStream);
|
||||
var operationsString = operationTest.operations.toString();
|
||||
|
||||
try {
|
||||
for (operation of operationTest.operations) {
|
||||
if (operationTest.timeSlice && operation === 'start') {
|
||||
operationsString += ' with timeslice ' + operationTest.timeSlice;
|
||||
mediaRecorder[operation](operationTest.timeSlice);
|
||||
} else {
|
||||
mediaRecorder[operation]();
|
||||
}
|
||||
}
|
||||
|
||||
if (operationTest.isValid) {
|
||||
ok(true, 'Successful transitions for ' + operationsString);
|
||||
} else {
|
||||
ok(false, 'Failed transitions for ' + operationsString);
|
||||
}
|
||||
} catch (err) {
|
||||
if (!operationTest.isValid && err.name === 'InvalidStateError') {
|
||||
ok(true, 'InvalidStateError fired for ' + operationsString);
|
||||
} else {
|
||||
ok(false, 'No InvalidStateError for ' + operationsString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a test on every media recorder file included to check that various
|
||||
* state transition flows that can happen in the media recorder object throw
|
||||
* exceptions when they are expected to and vice versa.
|
||||
*/
|
||||
function startTest(test, token) {
|
||||
var element = document.createElement('audio');
|
||||
var expectedMimeType = test.type.substring(0, test.type.indexOf(';'));
|
||||
|
||||
element.token = token;
|
||||
manager.started(token);
|
||||
|
||||
element.src = test.name;
|
||||
element.test = test;
|
||||
element.stream = element.mozCaptureStream();
|
||||
|
||||
element.oncanplaythrough = function () {
|
||||
runStateTransitionTests(element.stream);
|
||||
manager.finished(token);
|
||||
};
|
||||
|
||||
element.play();
|
||||
}
|
||||
|
||||
manager.runTests(gMediaRecorderTests, startTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -302,6 +302,11 @@ this.PermissionsTable = { geolocation: {
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: PROMPT_ACTION
|
||||
},
|
||||
"video-capture": {
|
||||
app: PROMPT_ACTION,
|
||||
privileged: PROMPT_ACTION,
|
||||
certified: PROMPT_ACTION
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2,19 +2,155 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "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;
|
||||
|
||||
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);
|
||||
@ -26,14 +162,12 @@ nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentPermissionRequestProxy::Init(const nsACString & type,
|
||||
const nsACString & access,
|
||||
nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests,
|
||||
ContentPermissionRequestParent* parent)
|
||||
{
|
||||
NS_ASSERTION(parent, "null parent");
|
||||
mParent = parent;
|
||||
mType = type;
|
||||
mAccess = access;
|
||||
mPermissionRequests = requests;
|
||||
|
||||
nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
|
||||
if (!prompt) {
|
||||
@ -53,17 +187,14 @@ nsContentPermissionRequestProxy::OnParentDestroyed()
|
||||
NS_IMPL_ISUPPORTS1(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetType(nsACString & aType)
|
||||
nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
aType = mType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsContentPermissionRequestProxy::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = mAccess;
|
||||
return NS_OK;
|
||||
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (ConvertPermissionRequestToArray(mPermissionRequests, types)) {
|
||||
types.forget(aTypes);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -134,55 +265,3 @@ nsContentPermissionRequestProxy::Allow()
|
||||
mParent = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
ContentPermissionRequestParent::ContentPermissionRequestParent(const nsACString& aType,
|
||||
const nsACString& aAccess,
|
||||
Element* aElement,
|
||||
const IPC::Principal& aPrincipal)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
|
||||
|
||||
mPrincipal = aPrincipal;
|
||||
mElement = aElement;
|
||||
mType = aType;
|
||||
mAccess = aAccess;
|
||||
}
|
||||
|
||||
ContentPermissionRequestParent::~ContentPermissionRequestParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ContentPermissionRequestParent);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentPermissionRequestParent::Recvprompt()
|
||||
{
|
||||
mProxy = new nsContentPermissionRequestProxy();
|
||||
NS_ASSERTION(mProxy, "Alloc of request proxy failed");
|
||||
if (NS_FAILED(mProxy->Init(mType, mAccess, this))) {
|
||||
mProxy->Cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
if (mProxy) {
|
||||
mProxy->OnParentDestroyed();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ContentPermissionRequestParent::IsBeingDestroyed()
|
||||
{
|
||||
// When TabParent::Destroy() is called, we are being destroyed. It's unsafe
|
||||
// to send out any message now.
|
||||
TabParent* tabParent = static_cast<TabParent*>(Manager());
|
||||
return tabParent->IsDestroyed();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -6,60 +6,75 @@
|
||||
#define nsContentPermissionHelper_h
|
||||
|
||||
#include "nsIContentPermissionPrompt.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/PContentPermissionRequestParent.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIMutableArray.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 ContentPermissionRequestParent : public PContentPermissionRequestParent
|
||||
class ContentPermissionType : public nsIContentPermissionType
|
||||
{
|
||||
public:
|
||||
ContentPermissionRequestParent(const nsACString& type,
|
||||
const nsACString& access,
|
||||
Element* element,
|
||||
const IPC::Principal& principal);
|
||||
virtual ~ContentPermissionRequestParent();
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPERMISSIONTYPE
|
||||
|
||||
bool IsBeingDestroyed();
|
||||
ContentPermissionType(const nsACString& aType, const nsACString& aAccess);
|
||||
virtual ~ContentPermissionType();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<Element> mElement;
|
||||
nsCOMPtr<nsContentPermissionRequestProxy> mProxy;
|
||||
protected:
|
||||
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 nsACString& type, const nsACString& access, mozilla::dom::ContentPermissionRequestParent* parent);
|
||||
nsresult Init(const nsTArray<mozilla::dom::PermissionRequest>& requests,
|
||||
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;
|
||||
nsCString mType;
|
||||
nsCString mAccess;
|
||||
nsTArray<mozilla::dom::PermissionRequest> mPermissionRequests;
|
||||
};
|
||||
#endif // nsContentPermissionHelper_h
|
||||
|
||||
#endif // nsContentPermissionHelper_h
|
||||
|
@ -68,8 +68,10 @@ static bool sAdapterDiscoverable = false;
|
||||
static nsString sAdapterBdAddress;
|
||||
static nsString sAdapterBdName;
|
||||
static uint32_t sAdapterDiscoverableTimeout;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
|
||||
|
||||
/**
|
||||
* Static callback functions
|
||||
@ -197,6 +199,24 @@ IsReady()
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
StringToBdAddressType(const nsAString& aBdAddress,
|
||||
bt_bdaddr_t *aRetBdAddressType)
|
||||
{
|
||||
const char* str = NS_ConvertUTF16toUTF8(aBdAddress).get();
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
aRetBdAddressType->address[i] = (uint8_t) strtoul(str, (char **)&str, 16);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AdapterPropertiesChangeCallback will be called after enable() but before
|
||||
* AdapterStateChangeCallback sIsBtEnabled get updated.
|
||||
* At that moment, both BluetoothManager/BluetoothAdapter does not register
|
||||
* observer yet.
|
||||
*/
|
||||
static void
|
||||
AdapterPropertiesChangeCallback(bt_status_t aStatus, int aNumProperties,
|
||||
bt_property_t *aProperties)
|
||||
@ -355,6 +375,120 @@ DiscoveryStateChangedCallback(bt_discovery_state_t aState)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
PinRequestCallback(bt_bdaddr_t* aRemoteBdAddress,
|
||||
bt_bdname_t* aRemoteBdName, uint32_t aRemoteClass)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
InfallibleTArray<BluetoothNamedValue> propertiesArray;
|
||||
nsAutoString remoteAddress;
|
||||
BdAddressTypeToString(aRemoteBdAddress, remoteAddress);
|
||||
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("address"), remoteAddress));
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("method"),
|
||||
NS_LITERAL_STRING("pincode")));
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("name"),
|
||||
NS_ConvertUTF8toUTF16(
|
||||
(const char*)aRemoteBdName->name)));
|
||||
|
||||
BluetoothValue value = propertiesArray;
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("RequestPinCode"),
|
||||
NS_LITERAL_STRING(KEY_LOCAL_AGENT), value);
|
||||
nsRefPtr<DistributeBluetoothSignalTask>
|
||||
t = new DistributeBluetoothSignalTask(signal);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
||||
NS_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SspRequestCallback(bt_bdaddr_t* aRemoteBdAddress, bt_bdname_t* aRemoteBdName,
|
||||
uint32_t aRemoteClass, bt_ssp_variant_t aPairingVariant,
|
||||
uint32_t aPasskey)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
InfallibleTArray<BluetoothNamedValue> propertiesArray;
|
||||
nsAutoString remoteAddress;
|
||||
BdAddressTypeToString(aRemoteBdAddress, remoteAddress);
|
||||
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("address"), remoteAddress));
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("method"),
|
||||
NS_LITERAL_STRING("confirmation")));
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("name"),
|
||||
NS_ConvertUTF8toUTF16(
|
||||
(const char*)aRemoteBdName->name)));
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("passkey"), aPasskey));
|
||||
|
||||
BluetoothValue value = propertiesArray;
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("RequestConfirmation"),
|
||||
NS_LITERAL_STRING(KEY_LOCAL_AGENT), value);
|
||||
nsRefPtr<DistributeBluetoothSignalTask>
|
||||
t = new DistributeBluetoothSignalTask(signal);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
||||
NS_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
BondStateChangedCallback(bt_status_t aStatus, bt_bdaddr_t* aRemoteBdAddress,
|
||||
bt_bond_state_t aState)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (aState == BT_BOND_STATE_BONDING) {
|
||||
//We don't need to handle bonding state
|
||||
return;
|
||||
}
|
||||
|
||||
bool bonded = (aState == BT_BOND_STATE_BONDED);
|
||||
nsAutoString remoteAddress;
|
||||
BdAddressTypeToString(aRemoteBdAddress, remoteAddress);
|
||||
InfallibleTArray<BluetoothNamedValue> propertiesArray;
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("address"), remoteAddress));
|
||||
propertiesArray.AppendElement(
|
||||
BluetoothNamedValue(NS_LITERAL_STRING("status"), bonded));
|
||||
|
||||
BluetoothSignal newSignal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER),
|
||||
BluetoothValue(propertiesArray));
|
||||
NS_DispatchToMainThread(new DistributeBluetoothSignalTask(newSignal));
|
||||
|
||||
if (bonded && !sBondingRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sBondingRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
|
||||
sBondingRunnableArray.RemoveElementAt(0);
|
||||
} else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sUnbondingRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
|
||||
sUnbondingRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AclStateChangedCallback(bt_status_t aStatus, bt_bdaddr_t* aRemoteBdAddress,
|
||||
bt_acl_state_t aState)
|
||||
{
|
||||
//FIXME: This will be implemented in the later patchset
|
||||
}
|
||||
|
||||
static void
|
||||
CallbackThreadEvent(bt_cb_thread_evt evt)
|
||||
{
|
||||
//FIXME: This will be implemented in the later patchset
|
||||
}
|
||||
|
||||
bt_callbacks_t sBluetoothCallbacks =
|
||||
{
|
||||
sizeof(sBluetoothCallbacks),
|
||||
@ -362,7 +496,12 @@ bt_callbacks_t sBluetoothCallbacks =
|
||||
AdapterPropertiesChangeCallback,
|
||||
RemoteDevicePropertiesChangeCallback,
|
||||
DeviceFoundCallback,
|
||||
DiscoveryStateChangedCallback
|
||||
DiscoveryStateChangedCallback,
|
||||
PinRequestCallback,
|
||||
SspRequestCallback,
|
||||
BondStateChangedCallback,
|
||||
AclStateChangedCallback,
|
||||
CallbackThreadEvent
|
||||
};
|
||||
|
||||
/**
|
||||
@ -515,6 +654,8 @@ nsresult
|
||||
BluetoothServiceBluedroid::GetPairedDevicePropertiesInternal(
|
||||
const nsTArray<nsString>& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -700,14 +841,50 @@ BluetoothServiceBluedroid::CreatePairedDeviceInternal(
|
||||
const nsAString& aDeviceAddress, int aTimeout,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
||||
int ret = sBtInterface->create_bond(&remoteAddress);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("CreatedPairedDevice"));
|
||||
} else {
|
||||
sBondingRunnableArray.AppendElement(aRunnable);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceBluedroid::RemoveDeviceInternal(
|
||||
const nsAString& aDeviceObjectPath,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
||||
int ret = sBtInterface->remove_bond(&remoteAddress);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret,
|
||||
NS_LITERAL_STRING("RemoveDevice"));
|
||||
} else {
|
||||
sUnbondingRunnableArray.AppendElement(aRunnable);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -716,6 +893,27 @@ BluetoothServiceBluedroid::SetPinCodeInternal(
|
||||
const nsAString& aDeviceAddress, const nsAString& aPinCode,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
||||
int ret = sBtInterface->pin_reply(
|
||||
&remoteAddress, true, aPinCode.Length(),
|
||||
(bt_pin_code_t*)NS_ConvertUTF16toUTF8(aPinCode).get());
|
||||
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("SetPinCode"));
|
||||
} else {
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -732,6 +930,26 @@ BluetoothServiceBluedroid::SetPairingConfirmationInternal(
|
||||
const nsAString& aDeviceAddress, bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
||||
int ret = sBtInterface->ssp_reply(&remoteAddress, (bt_ssp_variant_t)0,
|
||||
aConfirm, 0);
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret,
|
||||
NS_LITERAL_STRING("SetPairingConfirmation"));
|
||||
} else {
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIDocument.h"
|
||||
#include <algorithm>
|
||||
#include "nsContentPermissionHelper.h"
|
||||
|
||||
#include "mozilla/dom/DeviceStorageBinding.h"
|
||||
|
||||
@ -1695,17 +1696,14 @@ nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorageCursor::GetType(nsACString & aType)
|
||||
nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
return DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
|
||||
aType);
|
||||
}
|
||||
nsCString type;
|
||||
nsresult rv =
|
||||
DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorageCursor::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = NS_LITERAL_CSTRING("read");
|
||||
return NS_OK;
|
||||
return CreatePermissionArray(type, NS_LITERAL_CSTRING("read"), aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -2180,8 +2178,10 @@ public:
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(type, access));
|
||||
child->SendPContentPermissionRequestConstructor(
|
||||
this, type, access, IPC::Principal(mPrincipal));
|
||||
this, permArray, IPC::Principal(mPrincipal));
|
||||
|
||||
Sendprompt();
|
||||
return NS_OK;
|
||||
@ -2195,26 +2195,23 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetType(nsACString & aType)
|
||||
NS_IMETHODIMP GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
nsCString type;
|
||||
nsresult rv
|
||||
= DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
|
||||
aType);
|
||||
nsresult rv =
|
||||
DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetAccess(nsACString & aAccess)
|
||||
{
|
||||
nsresult rv = DeviceStorageTypeChecker::GetAccessForRequest(
|
||||
DeviceStorageRequestType(mRequestType), aAccess);
|
||||
nsCString access;
|
||||
rv = DeviceStorageTypeChecker::GetAccessForRequest(
|
||||
DeviceStorageRequestType(mRequestType), access);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
return CreatePermissionArray(type, access, aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
|
||||
@ -3191,8 +3188,10 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
child->SendPContentPermissionRequestConstructor(r, type,
|
||||
NS_LITERAL_CSTRING("read"),
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(type, NS_LITERAL_CSTRING("read")));
|
||||
child->SendPContentPermissionRequestConstructor(r,
|
||||
permArray,
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
r->Sendprompt();
|
||||
|
@ -401,6 +401,14 @@ dictionary MozStkTimer
|
||||
unsigned short timerAction;
|
||||
};
|
||||
|
||||
dictionary MozStkBipMessage
|
||||
{
|
||||
/**
|
||||
* Text String
|
||||
*/
|
||||
DOMString text;
|
||||
};
|
||||
|
||||
dictionary MozStkCommand
|
||||
{
|
||||
/**
|
||||
@ -469,6 +477,13 @@ dictionary MozStkCommand
|
||||
* option is MozStkTimer
|
||||
*
|
||||
* When typeOfCommand is
|
||||
* - STK_CMD_OPEN_CHANNEL
|
||||
* - STK_CMD_CLOSE_CHANNEL
|
||||
* - STK_CMD_SEND_DATA
|
||||
* - STK_CMD_RECEIVE_DATA
|
||||
* options is MozStkBipMessage
|
||||
*
|
||||
* When typeOfCommand is
|
||||
* - STK_CMD_POLL_OFF
|
||||
* options is null.
|
||||
*
|
||||
|
@ -9,7 +9,7 @@ interface nsIDOMDOMRequest;
|
||||
interface nsIDOMEventListener;
|
||||
interface nsIDOMMozIccInfo;
|
||||
|
||||
[scriptable, builtinclass, uuid(b403e307-e4ff-47a0-ac1e-c97b042b4595)]
|
||||
[scriptable, builtinclass, uuid(50782fe0-4185-4471-a374-e362b73febdb)]
|
||||
interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
{
|
||||
/**
|
||||
@ -50,6 +50,10 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
const unsigned short STK_CMD_PROVIDE_LOCAL_INFO = 0x26;
|
||||
const unsigned short STK_CMD_TIMER_MANAGEMENT = 0x27;
|
||||
const unsigned short STK_CMD_SET_UP_IDLE_MODE_TEXT = 0x28;
|
||||
const unsigned short STK_CMD_OPEN_CHANNEL = 0x30;
|
||||
const unsigned short STK_CMD_CLOSE_CHANNEL = 0x31;
|
||||
const unsigned short STK_CMD_RECEIVE_DATA = 0x32;
|
||||
const unsigned short STK_CMD_SEND_DATA = 0x33;
|
||||
|
||||
/**
|
||||
* STK Result code.
|
||||
|
@ -7,15 +7,13 @@
|
||||
interface nsIPrincipal;
|
||||
interface nsIDOMWindow;
|
||||
interface nsIDOMElement;
|
||||
interface nsIArray;
|
||||
|
||||
/**
|
||||
* Interface allows access to a content to request
|
||||
* permission to perform a privileged operation such as
|
||||
* geolocation.
|
||||
* Interface provides the request type and its access.
|
||||
*/
|
||||
[scriptable, uuid(1de67000-2de8-11e2-81c1-0800200c9a66)]
|
||||
interface nsIContentPermissionRequest : nsISupports {
|
||||
|
||||
[scriptable, builtinclass, uuid(384b6cc4-a66b-4bea-98e0-eb10562a9ba4)]
|
||||
interface nsIContentPermissionType : nsISupports {
|
||||
/**
|
||||
* The type of the permission request, such as
|
||||
* "geolocation".
|
||||
@ -27,8 +25,22 @@ interface nsIContentPermissionRequest : 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;
|
||||
|
@ -6,15 +6,27 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AppProcessChecker.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#ifdef MOZ_CHILD_PERMISSIONS
|
||||
#include "ContentParent.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "mozilla/hal_sandbox/PHalParent.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "TabParent.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::hal_sandbox;
|
||||
using namespace mozilla::services;
|
||||
#else
|
||||
class PContentParent;
|
||||
class nsIPrincipal;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@ -126,6 +138,106 @@ AssertAppProcess(PHalParent* aActor,
|
||||
return AssertAppProcess(aActor->Manager(), aType, aCapability);
|
||||
}
|
||||
|
||||
bool
|
||||
AssertAppPrincipal(PContentParent* aActor,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
if (!aPrincipal) {
|
||||
NS_WARNING("Principal is invalid, killing app process");
|
||||
static_cast<ContentParent*>(aActor)->KillHard();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t principalAppId = aPrincipal->GetAppId();
|
||||
bool inBrowserElement = aPrincipal->GetIsInBrowserElement();
|
||||
|
||||
// Check if the permission's appId matches a child we manage.
|
||||
const InfallibleTArray<PBrowserParent*>& browsers =
|
||||
aActor->ManagedPBrowserParent();
|
||||
for (uint32_t i = 0; i < browsers.Length(); ++i) {
|
||||
TabParent* tab = static_cast<TabParent*>(browsers[i]);
|
||||
if (tab->OwnOrContainingAppId() == principalAppId) {
|
||||
// If the child only runs inBrowserElement content and the principal claims
|
||||
// it's not in a browser element, it's lying.
|
||||
if (!tab->IsBrowserElement() || inBrowserElement) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NS_WARNING("Principal is invalid, killing app process");
|
||||
static_cast<ContentParent*>(aActor)->KillHard();
|
||||
return false;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal>
|
||||
GetAppPrincipal(uint32_t aAppId)
|
||||
{
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
|
||||
nsString manifestURL;
|
||||
nsresult rv = appsService->GetManifestURLByLocalId(aAppId, manifestURL);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_NewURI(getter_AddRefs(uri), manifestURL);
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> appPrincipal;
|
||||
rv = secMan->GetAppCodebasePrincipal(uri, aAppId, false,
|
||||
getter_AddRefs(appPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
return appPrincipal.forget();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CheckPermission(PContentParent* aActor,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const char* aPermission)
|
||||
{
|
||||
if (!AssertAppPrincipal(aActor, aPrincipal)) {
|
||||
return nsIPermissionManager::DENY_ACTION;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> pm =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
|
||||
|
||||
// Make sure that `aPermission' is an app permission before checking the origin.
|
||||
nsCOMPtr<nsIPrincipal> appPrincipal = GetAppPrincipal(aPrincipal->GetAppId());
|
||||
uint32_t appPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
nsresult rv = pm->TestExactPermissionFromPrincipal(appPrincipal, aPermission, &appPerm);
|
||||
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION);
|
||||
if (appPerm == nsIPermissionManager::UNKNOWN_ACTION ||
|
||||
appPerm == nsIPermissionManager::DENY_ACTION) {
|
||||
return appPerm;
|
||||
}
|
||||
|
||||
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
rv = pm->TestExactPermissionFromPrincipal(aPrincipal, aPermission, &permission);
|
||||
NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION);
|
||||
if (permission == nsIPermissionManager::UNKNOWN_ACTION ||
|
||||
permission == nsIPermissionManager::DENY_ACTION) {
|
||||
return permission;
|
||||
}
|
||||
|
||||
if (appPerm == nsIPermissionManager::PROMPT_ACTION ||
|
||||
permission == nsIPermissionManager::PROMPT_ACTION) {
|
||||
return nsIPermissionManager::PROMPT_ACTION;
|
||||
}
|
||||
|
||||
if (appPerm == nsIPermissionManager::ALLOW_ACTION ||
|
||||
permission == nsIPermissionManager::ALLOW_ACTION) {
|
||||
return nsIPermissionManager::ALLOW_ACTION;
|
||||
}
|
||||
|
||||
NS_RUNTIMEABORT("Invalid permission value");
|
||||
return nsIPermissionManager::DENY_ACTION;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool
|
||||
@ -167,6 +279,21 @@ AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
AssertAppPrincipal(PContentParent* aActor,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CheckPermission(PContentParent*,
|
||||
nsIPrincipal*,
|
||||
const char*)
|
||||
{
|
||||
return nsIPermissionManager::ALLOW_ACTION;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -8,6 +8,10 @@
|
||||
#ifndef mozilla_AppProcessChecker_h
|
||||
#define mozilla_AppProcessChecker_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
@ -66,6 +70,21 @@ AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor,
|
||||
// return AssertAppProcess(aActor->Manager(), aType);
|
||||
// }
|
||||
|
||||
bool
|
||||
AssertAppPrincipal(mozilla::dom::PContentParent* aParent,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
/**
|
||||
* Check if the specified principal is valid, and return the saved permission
|
||||
* value for permission `aPermission' on that principal.
|
||||
* See nsIPermissionManager.idl for possible return values.
|
||||
*
|
||||
* nsIPermissionManager::UNKNOWN_ACTION is retuned if the principal is invalid.
|
||||
*/
|
||||
uint32_t
|
||||
CheckPermission(mozilla::dom::PContentParent* aParent,
|
||||
nsIPrincipal* aPrincipal, const char* aPermission);
|
||||
|
||||
/**
|
||||
* Inline function for asserting the process's permission.
|
||||
*/
|
||||
|
@ -16,6 +16,7 @@ include protocol PIndexedDB;
|
||||
include DOMTypes;
|
||||
include JavaScriptTypes;
|
||||
include URIParams;
|
||||
include PContentPermission;
|
||||
|
||||
|
||||
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
|
||||
@ -202,10 +203,8 @@ parent:
|
||||
* Initiates an asynchronous request for permission for the
|
||||
* provided principal.
|
||||
*
|
||||
* @param aType
|
||||
* The type of permission to request.
|
||||
* @param aAccess
|
||||
* Access type. "read" for example.
|
||||
* @param aRequests
|
||||
* The array of permissions to request.
|
||||
* @param aPrincipal
|
||||
* The principal of the request.
|
||||
*
|
||||
@ -213,7 +212,7 @@ parent:
|
||||
* principals that can live in the content process should
|
||||
* provided.
|
||||
*/
|
||||
PContentPermissionRequest(nsCString aType, nsCString aAccess, Principal principal);
|
||||
PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal);
|
||||
|
||||
PContentDialog(uint32_t aType, nsCString aName, nsCString aFeatures,
|
||||
int32_t[] aIntParams, nsString[] aStringParams);
|
||||
|
14
dom/ipc/PContentPermission.ipdlh
Normal file
14
dom/ipc/PContentPermission.ipdlh
Normal file
@ -0,0 +1,14 @@
|
||||
/* 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
|
@ -2008,7 +2008,8 @@ TabChild::DeallocPContentDialogChild(PContentDialogChild* aDialog)
|
||||
}
|
||||
|
||||
PContentPermissionRequestChild*
|
||||
TabChild::AllocPContentPermissionRequestChild(const nsCString& aType, const nsCString& aAccess, const IPC::Principal&)
|
||||
TabChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal)
|
||||
{
|
||||
NS_RUNTIMEABORT("unused");
|
||||
return nullptr;
|
||||
|
@ -269,13 +269,11 @@ public:
|
||||
#ifdef DEBUG
|
||||
virtual PContentPermissionRequestChild*
|
||||
SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor,
|
||||
const nsCString& aType,
|
||||
const nsCString& aAccess,
|
||||
const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal);
|
||||
#endif /* DEBUG */
|
||||
|
||||
virtual PContentPermissionRequestChild* AllocPContentPermissionRequestChild(const nsCString& aType,
|
||||
const nsCString& aAccess,
|
||||
virtual PContentPermissionRequestChild* AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal);
|
||||
virtual bool DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor);
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#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"
|
||||
@ -570,9 +571,10 @@ TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor)
|
||||
}
|
||||
|
||||
PContentPermissionRequestParent*
|
||||
TabParent::AllocPContentPermissionRequestParent(const nsCString& type, const nsCString& access, const IPC::Principal& principal)
|
||||
TabParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal)
|
||||
{
|
||||
return new ContentPermissionRequestParent(type, access, mFrameElement, principal);
|
||||
return CreateContentPermissionRequestParent(aRequests, mFrameElement, aPrincipal);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -215,7 +215,8 @@ public:
|
||||
virtual bool DeallocPDocumentRendererParent(PDocumentRendererParent* actor);
|
||||
|
||||
virtual PContentPermissionRequestParent*
|
||||
AllocPContentPermissionRequestParent(const nsCString& aType, const nsCString& aAccess, const IPC::Principal& aPrincipal);
|
||||
AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
|
||||
const IPC::Principal& aPrincipal);
|
||||
virtual bool DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor);
|
||||
|
||||
virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdateParent(
|
||||
|
@ -64,6 +64,7 @@ IPDL_SOURCES += [
|
||||
'PBrowser.ipdl',
|
||||
'PContent.ipdl',
|
||||
'PContentDialog.ipdl',
|
||||
'PContentPermission.ipdlh',
|
||||
'PContentPermissionRequest.ipdl',
|
||||
'PCrashReporter.ipdl',
|
||||
'PDocumentRenderer.ipdl',
|
||||
|
@ -43,7 +43,7 @@
|
||||
#include "MediaEngineWebRTC.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#ifdef MOZ_B2G
|
||||
#include "MediaPermissionGonk.h"
|
||||
#endif
|
||||
|
||||
@ -810,7 +810,6 @@ public:
|
||||
, mListener(aListener)
|
||||
, mPrefs(aPrefs)
|
||||
, mDeviceChosen(false)
|
||||
, mBackendChosen(false)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
{}
|
||||
|
||||
@ -832,15 +831,11 @@ public:
|
||||
, mListener(aListener)
|
||||
, mPrefs(aPrefs)
|
||||
, mDeviceChosen(false)
|
||||
, mBackendChosen(true)
|
||||
, mBackend(aBackend)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
{}
|
||||
|
||||
~GetUserMediaRunnable() {
|
||||
if (mBackendChosen) {
|
||||
delete mBackend;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -848,10 +843,7 @@ public:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
// Was a backend provided?
|
||||
if (!mBackendChosen) {
|
||||
mBackend = mManager->GetBackend(mWindowID);
|
||||
}
|
||||
mBackend = mManager->GetBackend(mWindowID);
|
||||
|
||||
// Was a device provided?
|
||||
if (!mDeviceChosen) {
|
||||
@ -1038,7 +1030,6 @@ private:
|
||||
MediaEnginePrefs mPrefs;
|
||||
|
||||
bool mDeviceChosen;
|
||||
bool mBackendChosen;
|
||||
|
||||
MediaEngine* mBackend;
|
||||
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
|
||||
@ -1256,10 +1247,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_WIDGET_GONK
|
||||
#ifdef MOZ_B2G
|
||||
// Initialize MediaPermissionManager before send out any permission request.
|
||||
(void) MediaPermissionManager::GetInstance();
|
||||
#endif //MOZ_WIDGET_GONK
|
||||
#endif //MOZ_B2G
|
||||
}
|
||||
|
||||
// Store the WindowID in a hash table and mark as active. The entry is removed
|
||||
@ -1311,7 +1302,7 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
|
||||
if (c.mFake) {
|
||||
// Fake stream from default backend.
|
||||
gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
|
||||
onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
|
||||
onError.forget(), windowID, listener, mPrefs, GetBackend(windowID, true));
|
||||
} else {
|
||||
// Stream from default device from WebRTC backend.
|
||||
gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
|
||||
@ -1392,22 +1383,26 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
|
||||
MediaEngine*
|
||||
MediaManager::GetBackend(uint64_t aWindowId)
|
||||
MediaManager::GetBackend(uint64_t aWindowId, bool aFake)
|
||||
{
|
||||
// Plugin backends as appropriate. The default engine also currently
|
||||
// includes picture support for Android.
|
||||
// This IS called off main-thread.
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mBackend) {
|
||||
if (aFake) {
|
||||
mBackend = new MediaEngineDefault();
|
||||
} else {
|
||||
#if defined(MOZ_WEBRTC)
|
||||
#ifndef MOZ_B2G_CAMERA
|
||||
mBackend = new MediaEngineWebRTC();
|
||||
#else
|
||||
mBackend = new MediaEngineWebRTC(mCameraManager, aWindowId);
|
||||
#endif
|
||||
#ifndef MOZ_B2G_CAMERA
|
||||
mBackend = new MediaEngineWebRTC();
|
||||
#else
|
||||
mBackend = new MediaEngineDefault();
|
||||
mBackend = new MediaEngineWebRTC(mCameraManager, aWindowId);
|
||||
#endif
|
||||
#else
|
||||
mBackend = new MediaEngineDefault();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return mBackend;
|
||||
}
|
||||
|
@ -405,7 +405,7 @@ public:
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIMEDIAMANAGERSERVICE
|
||||
|
||||
MediaEngine* GetBackend(uint64_t aWindowId = 0);
|
||||
MediaEngine* GetBackend(uint64_t aWindowId = 0, bool aFake = false);
|
||||
StreamListeners *GetWindowListeners(uint64_t aWindowId) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
|
||||
|
||||
@ -469,7 +469,7 @@ private:
|
||||
|
||||
static StaticRefPtr<MediaManager> sSingleton;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#ifdef MOZ_B2G_CAMERA
|
||||
nsRefPtr<nsDOMCameraManager> mCameraManager;
|
||||
#endif
|
||||
};
|
||||
|
@ -20,14 +20,36 @@
|
||||
#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)
|
||||
@ -93,6 +115,7 @@ 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
|
||||
};
|
||||
@ -108,6 +131,7 @@ 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]);
|
||||
@ -116,10 +140,34 @@ 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)
|
||||
{
|
||||
@ -135,24 +183,6 @@ 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)
|
||||
{
|
||||
@ -278,13 +308,12 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
|
||||
dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
|
||||
NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
|
||||
|
||||
nsAutoCString type;
|
||||
rv = req->GetType(type);
|
||||
nsCOMPtr<nsIArray> typeArray;
|
||||
rv = req->GetTypes(getter_AddRefs(typeArray));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString access;
|
||||
rv = req->GetAccess(access);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
ConvertArrayToPermissionRequest(typeArray, permArray);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = req->GetPrincipal(getter_AddRefs(principal));
|
||||
@ -292,8 +321,7 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
|
||||
|
||||
req->AddRef();
|
||||
child->SendPContentPermissionRequestConstructor(req,
|
||||
type,
|
||||
access,
|
||||
permArray,
|
||||
IPC::Principal(principal));
|
||||
|
||||
req->Sendprompt();
|
||||
|
@ -7,3 +7,9 @@ ifdef MOZ_WEBRTC_LEAKING_TESTS
|
||||
MOCHITEST_FILES += \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_B2G_CAMERA
|
||||
MOCHITEST_FILES += \
|
||||
test_getUserMedia_permission.html \
|
||||
$(NULL)
|
||||
endif
|
||||
|
77
dom/media/tests/mochitest/test_getUserMedia_permission.html
Normal file
77
dom/media/tests/mochitest/test_getUserMedia_permission.html
Normal file
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=853356
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>mozGetUserMedia Permission Test</title>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="head.js"></script>
|
||||
<script type="application/javascript" src="mediaStreamPlayback.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=853356">Display camera/microphone permission acquisition prompt</a>
|
||||
<script type="application/javascript">
|
||||
var MockPermissionPrompt = SpecialPowers.MockPermissionPrompt;
|
||||
MockPermissionPrompt.init();
|
||||
|
||||
var gCount = 0;
|
||||
var gTests = [
|
||||
{
|
||||
constraints: {video: true, audio: false}
|
||||
}
|
||||
,
|
||||
{
|
||||
constraints: {video: false, audio: true}
|
||||
}
|
||||
,
|
||||
{
|
||||
constraints: {video: true, audio: true},
|
||||
}
|
||||
];
|
||||
|
||||
function gUM(data) {
|
||||
var gum_success = function (stream) {
|
||||
SimpleTest.info("TEST-INFO | Got succss callback for " + JSON.stringify(data.constraints));
|
||||
|
||||
var hasAudioTrack = stream.getAudioTracks().length > 0;
|
||||
var hasVideoTrack = stream.getVideoTracks().length > 0;
|
||||
|
||||
is(data.constraints.audio, hasAudioTrack, "Request audio track:" +
|
||||
data.constraints.audio + " contain audio track:" + hasAudioTrack);
|
||||
is(data.constraints.video, hasVideoTrack, "Request video track:" +
|
||||
data.constraints.video + " contain audio track:" + hasVideoTrack);
|
||||
gCount++;
|
||||
if (gCount < gTests.length) {
|
||||
gUM(gTests[gCount]);
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var gum_fail = function () {
|
||||
ok(false, "permission not granted for " + JSON.stringify(data.constraints));
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.info("TEST-INFO | Call getUserMedia for " + JSON.stringify(data.constraints));
|
||||
navigator.mozGetUserMedia(data.constraints, gum_success, gum_fail);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set": [["media.navigator.permission.disabled", false]]},
|
||||
function () {
|
||||
SpecialPowers.addPermission('video-capture',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION, document);
|
||||
SpecialPowers.addPermission('audio-capture',
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION, document);
|
||||
|
||||
gUM(gTests[gCount]);
|
||||
});
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -14,7 +14,7 @@ dictionary SmsThreadListItem
|
||||
unsigned long long unreadCount;
|
||||
};
|
||||
|
||||
[scriptable, uuid(5f82f826-1956-11e3-a2bb-9b043b33de27)]
|
||||
[scriptable, uuid(58780660-4080-11e3-8397-a7bb1b58cf12)]
|
||||
interface nsIMobileMessageCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -31,6 +31,7 @@ interface nsIMobileMessageCallback : nsISupports
|
||||
const unsigned short RADIO_DISABLED_ERROR = 6;
|
||||
const unsigned short INVALID_ADDRESS_ERROR = 7;
|
||||
const unsigned short FDN_CHECK_ERROR = 8;
|
||||
const unsigned short NON_ACTIVE_SIM_CARD_ERROR = 9;
|
||||
|
||||
/**
|
||||
* |message| can be nsIDOMMoz{Mms,Sms}Message.
|
||||
|
@ -106,6 +106,9 @@ MobileMessageCallback::NotifyError(int32_t aError, bool aAsync)
|
||||
case nsIMobileMessageCallback::FDN_CHECK_ERROR:
|
||||
errorStr = NS_LITERAL_STRING("FdnCheckError");
|
||||
break;
|
||||
case nsIMobileMessageCallback::NON_ACTIVE_SIM_CARD_ERROR:
|
||||
errorStr = NS_LITERAL_STRING("NonActiveSimCardError");
|
||||
break;
|
||||
default: // SUCCESS_NO_ERROR is handled above.
|
||||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
@ -1698,13 +1698,21 @@ MmsService.prototype = {
|
||||
|
||||
this.broadcastReceivedMessageEvent(domMessage);
|
||||
|
||||
// In roaming environment, we send notify response only in
|
||||
// In the roaming environment, we send notify response only for the
|
||||
// automatic retrieval mode.
|
||||
if ((retrievalMode !== RETRIEVAL_MODE_AUTOMATIC) &&
|
||||
mmsConnection.isVoiceRoaming()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Under the "automatic" retrieval mode, for the non-active SIM, we have to
|
||||
// download the MMS as if it is downloaded by the "manual" retrieval mode.
|
||||
if ((retrievalMode == RETRIEVAL_MODE_AUTOMATIC ||
|
||||
retrievalMode == RETRIEVAL_MODE_AUTOMATIC_HOME) &&
|
||||
mmsConnection.serviceId != this.mmsDefaultServiceId) {
|
||||
retrievalMode = RETRIEVAL_MODE_MANUAL;
|
||||
}
|
||||
|
||||
if (RETRIEVAL_MODE_MANUAL === retrievalMode ||
|
||||
RETRIEVAL_MODE_NEVER === retrievalMode) {
|
||||
let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode
|
||||
@ -1722,6 +1730,7 @@ MmsService.prototype = {
|
||||
transaction.run();
|
||||
return;
|
||||
}
|
||||
|
||||
let url = savableMessage.headers["x-mms-content-location"].uri;
|
||||
|
||||
// For RETRIEVAL_MODE_AUTOMATIC or RETRIEVAL_MODE_AUTOMATIC_HOME but not
|
||||
@ -2187,6 +2196,15 @@ MmsService.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// To support DSDS, we have to stop users retrieving MMS when the needed
|
||||
// SIM is not active, thus avoiding the data disconnection of the current
|
||||
// SIM. Users have to manually swith the default SIM before retrieving.
|
||||
if (serviceId != this.mmsDefaultServiceId) {
|
||||
if (DEBUG) debug("RIL service is not active to retrieve MMS.");
|
||||
aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.NON_ACTIVE_SIM_CARD_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
let mmsConnection = gMmsConnections.getConnByServiceId(serviceId);
|
||||
|
||||
let url = aMessageRecord.headers["x-mms-content-location"].uri;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
@ -385,17 +386,11 @@ nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationRequest::GetType(nsACString & aType)
|
||||
nsGeolocationRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
aType = "geolocation";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationRequest::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1452,12 +1447,15 @@ 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,
|
||||
NS_LITERAL_CSTRING("geolocation"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
permArray,
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
request->Sendprompt();
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "PCOMContentPermissionRequestChild.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "PermissionMessageUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -177,9 +178,12 @@ 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(),
|
||||
NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
permArray,
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
request->Sendprompt();
|
||||
@ -351,17 +355,11 @@ DesktopNotificationRequest::Allow()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DesktopNotificationRequest::GetType(nsACString & aType)
|
||||
DesktopNotificationRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
aType = "desktop-notification";
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DesktopNotificationRequest::GetAccess(nsACString & aAccess)
|
||||
{
|
||||
aAccess = "unused";
|
||||
return NS_OK;
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "nsContentPermissionHelper.h"
|
||||
#ifdef MOZ_B2G
|
||||
#include "nsIDOMDesktopNotification.h"
|
||||
#endif
|
||||
@ -267,9 +268,11 @@ NotificationPermissionRequest::Run()
|
||||
// Corresponding release occurs in DeallocPContentPermissionRequest.
|
||||
AddRef();
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(type, "desktop-notification");
|
||||
NS_NAMED_LITERAL_CSTRING(access, "unused");
|
||||
child->SendPContentPermissionRequestConstructor(this, type, access,
|
||||
nsTArray<PermissionRequest> permArray;
|
||||
permArray.AppendElement(PermissionRequest(
|
||||
NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused")));
|
||||
child->SendPContentPermissionRequestConstructor(this, permArray,
|
||||
IPC::Principal(mPrincipal));
|
||||
|
||||
Sendprompt();
|
||||
@ -342,17 +345,11 @@ NotificationPermissionRequest::CallCallback()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NotificationPermissionRequest::GetAccess(nsACString& aAccess)
|
||||
NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
|
||||
{
|
||||
aAccess.AssignLiteral("unused");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NotificationPermissionRequest::GetType(nsACString& aType)
|
||||
{
|
||||
aType.AssignLiteral("desktop-notification");
|
||||
return NS_OK;
|
||||
return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
|
||||
NS_LITERAL_CSTRING("unused"),
|
||||
aTypes);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -750,6 +750,10 @@ this.STK_CMD_SEND_USSD = 0x12;
|
||||
this.STK_CMD_SEND_SMS = 0x13;
|
||||
this.STK_CMD_SEND_DTMF = 0x14;
|
||||
this.STK_CMD_LAUNCH_BROWSER = 0x15;
|
||||
this.STK_CMD_OPEN_CHANNEL = 0x16;
|
||||
this.STK_CMD_CLOSE_CHANNEL = 0x17;
|
||||
this.STK_CMD_RECEIVE_DATA = 0x18;
|
||||
this.STK_CMD_SEND_DATA = 0x19;
|
||||
this.STK_CMD_PLAY_TONE = 0x20;
|
||||
this.STK_CMD_DISPLAY_TEXT = 0x21;
|
||||
this.STK_CMD_GET_INKEY = 0x22;
|
||||
@ -1043,6 +1047,12 @@ this.STK_TERMINAL_SUPPORT_PROACTIVE_LANGUAGE_NOTIFICATION = 0;
|
||||
this.STK_TERMINAL_SUPPORT_PROACTIVE_LAUNCH_BROWSER = 1;
|
||||
this.STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_ACCESS_TECH = 0;
|
||||
|
||||
this.STK_TERMINAL_SUPPORT_BIP_COMMAND_OPEN_CHANNEL = 1;
|
||||
this.STK_TERMINAL_SUPPORT_BIP_COMMAND_CLOSE_CHANNEL = 1;
|
||||
this.STK_TERMINAL_SUPPORT_BIP_COMMAND_RECEIVE_DATA = 1;
|
||||
this.STK_TERMINAL_SUPPORT_BIP_COMMAND_SEND_DATA = 1;
|
||||
this.STK_TERMINAL_SUPPORT_BIP_COMMAND_GET_CHANNEL_STATUS = 0;
|
||||
|
||||
/**
|
||||
* SAT profile
|
||||
*
|
||||
@ -1124,6 +1134,13 @@ this.STK_TERMINAL_PROFILE_PROACTIVE_4 =
|
||||
(STK_TERMINAL_SUPPORT_PROACTIVE_LAUNCH_BROWSER << 6) |
|
||||
(STK_TERMINAL_SUPPORT_PROACTIVE_LOCAL_INFO_ACCESS_TECH << 7);
|
||||
|
||||
this.STK_TERMINAL_PROFILE_BIP_COMMAND =
|
||||
(STK_TERMINAL_SUPPORT_BIP_COMMAND_OPEN_CHANNEL << 0) |
|
||||
(STK_TERMINAL_SUPPORT_BIP_COMMAND_CLOSE_CHANNEL << 1) |
|
||||
(STK_TERMINAL_SUPPORT_BIP_COMMAND_RECEIVE_DATA << 2) |
|
||||
(STK_TERMINAL_SUPPORT_BIP_COMMAND_SEND_DATA << 3) |
|
||||
(STK_TERMINAL_SUPPORT_BIP_COMMAND_GET_CHANNEL_STATUS << 4);
|
||||
|
||||
this.STK_SUPPORTED_TERMINAL_PROFILE = [
|
||||
STK_TERMINAL_PROFILE_DOWNLOAD,
|
||||
STK_TERMINAL_PROFILE_OTHER,
|
||||
@ -1136,7 +1153,7 @@ this.STK_SUPPORTED_TERMINAL_PROFILE = [
|
||||
STK_TERMINAL_PROFILE_PROACTIVE_4,
|
||||
0x00, // Softkey support
|
||||
0x00, // Softkey information
|
||||
0x00, // BIP proactive commands
|
||||
STK_TERMINAL_PROFILE_BIP_COMMAND,
|
||||
0x00, // BIP supported bearers
|
||||
0x00, // Screen height
|
||||
0x00, // Screen width
|
||||
|
@ -9900,6 +9900,26 @@ let StkCommandParamsFactory = {
|
||||
}
|
||||
|
||||
return timer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Construct a param for BIP commands.
|
||||
*
|
||||
* @param cmdDetails
|
||||
* The value object of CommandDetails TLV.
|
||||
* @param ctlvs
|
||||
* The all TLVs in this proactive command.
|
||||
*/
|
||||
processBipMessage: function processBipMessage(cmdDetails, ctlvs) {
|
||||
let bipMsg = {};
|
||||
|
||||
let ctlv = StkProactiveCmdHelper.searchForTag(
|
||||
COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
|
||||
if (ctlv) {
|
||||
bipMsg.text = ctlv.value.identifier;
|
||||
}
|
||||
|
||||
return bipMsg;
|
||||
}
|
||||
};
|
||||
StkCommandParamsFactory[STK_CMD_REFRESH] = function STK_CMD_REFRESH(cmdDetails, ctlvs) {
|
||||
@ -9959,6 +9979,18 @@ StkCommandParamsFactory[STK_CMD_PLAY_TONE] = function STK_CMD_PLAY_TONE(cmdDetai
|
||||
StkCommandParamsFactory[STK_CMD_TIMER_MANAGEMENT] = function STK_CMD_TIMER_MANAGEMENT(cmdDetails, ctlvs) {
|
||||
return this.processTimerManagement(cmdDetails, ctlvs);
|
||||
};
|
||||
StkCommandParamsFactory[STK_CMD_OPEN_CHANNEL] = function STK_CMD_OPEN_CHANNEL(cmdDetails, ctlvs) {
|
||||
return this.processBipMessage(cmdDetails, ctlvs);
|
||||
};
|
||||
StkCommandParamsFactory[STK_CMD_CLOSE_CHANNEL] = function STK_CMD_CLOSE_CHANNEL(cmdDetails, ctlvs) {
|
||||
return this.processBipMessage(cmdDetails, ctlvs);
|
||||
};
|
||||
StkCommandParamsFactory[STK_CMD_RECEIVE_DATA] = function STK_CMD_RECEIVE_DATA(cmdDetails, ctlvs) {
|
||||
return this.processBipMessage(cmdDetails, ctlvs);
|
||||
};
|
||||
StkCommandParamsFactory[STK_CMD_SEND_DATA] = function STK_CMD_SEND_DATA(cmdDetails, ctlvs) {
|
||||
return this.processBipMessage(cmdDetails, ctlvs);
|
||||
};
|
||||
|
||||
let StkProactiveCmdHelper = {
|
||||
retrieve: function retrieve(tag, length) {
|
||||
|
@ -763,6 +763,110 @@ add_test(function test_stk_proactive_command_provide_local_information() {
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify Proactive command : BIP Messages
|
||||
*/
|
||||
add_test(function test_stk_proactive_command_open_channel() {
|
||||
let worker = newUint8Worker();
|
||||
let pduHelper = worker.GsmPDUHelper;
|
||||
let berHelper = worker.BerTlvHelper;
|
||||
let stkHelper = worker.StkProactiveCmdHelper;
|
||||
|
||||
// Open Channel
|
||||
let open_channel = [
|
||||
0xD0,
|
||||
0x0F,
|
||||
0x81, 0x03, 0x01, 0x16, 0x00,
|
||||
0x82, 0x02, 0x81, 0x82,
|
||||
0x85, 0x04, 0x4F, 0x70, 0x65, 0x6E //alpha id: "Open"
|
||||
];
|
||||
|
||||
for (let i = 0; i < open_channel.length; i++) {
|
||||
pduHelper.writeHexOctet(open_channel[i]);
|
||||
}
|
||||
|
||||
let berTlv = berHelper.decode(open_channel.length);
|
||||
let ctlvs = berTlv.value;
|
||||
let tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
|
||||
do_check_eq(tlv.value.commandNumber, 0x01);
|
||||
do_check_eq(tlv.value.typeOfCommand, STK_CMD_OPEN_CHANNEL);
|
||||
do_check_eq(tlv.value.commandQualifier, 0x00);
|
||||
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
|
||||
do_check_eq(tlv.value.identifier, "Open");
|
||||
|
||||
// Close Channel
|
||||
let close_channel = [
|
||||
0xD0,
|
||||
0x10,
|
||||
0x81, 0x03, 0x01, 0x17, 0x00,
|
||||
0x82, 0x02, 0x81, 0x82,
|
||||
0x85, 0x05, 0x43, 0x6C, 0x6F, 0x73, 0x65 //alpha id: "Close"
|
||||
];
|
||||
|
||||
for (let i = 0; i < close_channel.length; i++) {
|
||||
pduHelper.writeHexOctet(close_channel[i]);
|
||||
}
|
||||
|
||||
berTlv = berHelper.decode(close_channel.length);
|
||||
ctlvs = berTlv.value;
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
|
||||
do_check_eq(tlv.value.commandNumber, 0x01);
|
||||
do_check_eq(tlv.value.typeOfCommand, STK_CMD_CLOSE_CHANNEL);
|
||||
do_check_eq(tlv.value.commandQualifier, 0x00);
|
||||
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
|
||||
do_check_eq(tlv.value.identifier, "Close");
|
||||
|
||||
// Receive Data
|
||||
let receive_data = [
|
||||
0XD0,
|
||||
0X12,
|
||||
0x81, 0x03, 0x01, 0x18, 0x00,
|
||||
0x82, 0x02, 0x81, 0x82,
|
||||
0x85, 0x07, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65 //alpha id: "Receive"
|
||||
];
|
||||
|
||||
for (let i = 0; i < receive_data.length; i++) {
|
||||
pduHelper.writeHexOctet(receive_data[i]);
|
||||
}
|
||||
|
||||
berTlv = berHelper.decode(receive_data.length);
|
||||
ctlvs = berTlv.value;
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
|
||||
do_check_eq(tlv.value.commandNumber, 0x01);
|
||||
do_check_eq(tlv.value.typeOfCommand, STK_CMD_RECEIVE_DATA);
|
||||
do_check_eq(tlv.value.commandQualifier, 0x00);
|
||||
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
|
||||
do_check_eq(tlv.value.identifier, "Receive");
|
||||
|
||||
// Send Data
|
||||
let send_data = [
|
||||
0xD0,
|
||||
0x0F,
|
||||
0x81, 0x03, 0x01, 0x19, 0x00,
|
||||
0x82, 0x02, 0x81, 0x82,
|
||||
0x85, 0x04, 0x53, 0x65, 0x6E, 0x64 //alpha id: "Send"
|
||||
];
|
||||
|
||||
for (let i = 0; i < send_data.length; i++) {
|
||||
pduHelper.writeHexOctet(send_data[i]);
|
||||
}
|
||||
|
||||
berTlv = berHelper.decode(send_data.length);
|
||||
ctlvs = berTlv.value;
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_COMMAND_DETAILS, ctlvs);
|
||||
do_check_eq(tlv.value.commandNumber, 0x01);
|
||||
do_check_eq(tlv.value.typeOfCommand, STK_CMD_SEND_DATA);
|
||||
do_check_eq(tlv.value.commandQualifier, 0x00);
|
||||
|
||||
tlv = stkHelper.searchForTag(COMPREHENSIONTLV_TAG_ALPHA_ID, ctlvs);
|
||||
do_check_eq(tlv.value.identifier, "Send");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify Event Download Command : Location Status
|
||||
*/
|
||||
|
@ -301,15 +301,16 @@ public class GeckoSmsManager
|
||||
* defined in dom/mobilemessage/interfaces/nsISmsRequestManager.idl. They are owned
|
||||
* owned by the interface.
|
||||
*/
|
||||
public final static int kNoError = 0;
|
||||
public final static int kNoSignalError = 1;
|
||||
public final static int kNotFoundError = 2;
|
||||
public final static int kUnknownError = 3;
|
||||
public final static int kInternalError = 4;
|
||||
public final static int kNoSimCardError = 5;
|
||||
public final static int kRadioDisabledError = 6;
|
||||
public final static int kInvalidAddressError = 7;
|
||||
public final static int kFdnCheckError = 8;
|
||||
public final static int kNoError = 0;
|
||||
public final static int kNoSignalError = 1;
|
||||
public final static int kNotFoundError = 2;
|
||||
public final static int kUnknownError = 3;
|
||||
public final static int kInternalError = 4;
|
||||
public final static int kNoSimCardError = 5;
|
||||
public final static int kRadioDisabledError = 6;
|
||||
public final static int kInvalidAddressError = 7;
|
||||
public final static int kFdnCheckError = 8;
|
||||
public final static int kNonActiveSimCardError = 9;
|
||||
|
||||
private final static int kMaxMessageSize = 160;
|
||||
|
||||
|
@ -37,7 +37,6 @@ import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
@ -1475,15 +1474,6 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return;
|
||||
}
|
||||
|
||||
// If the keywordUrl is in ReadingList, convert the url to an about:reader url and load it.
|
||||
final ContentResolver cr = getContentResolver();
|
||||
final boolean inReadingList = BrowserDB.isReadingListItem(cr, keywordUrl);
|
||||
if (inReadingList) {
|
||||
final String readerUrl = ReaderModeUtils.getAboutReaderForUrl(keywordUrl);
|
||||
Tabs.getInstance().loadUrl(readerUrl, Tabs.LOADURL_USER_ENTERED);
|
||||
return;
|
||||
}
|
||||
|
||||
recordSearch(null, "barkeyword");
|
||||
|
||||
// Otherwise, construct a search query from the bookmark keyword.
|
||||
|
@ -295,15 +295,16 @@ public class GeckoSmsManager
|
||||
* dom/mobilemessage/src/Types.h
|
||||
* The error code are owned by the DOM.
|
||||
*/
|
||||
public final static int kNoError = 0;
|
||||
public final static int kNoSignalError = 1;
|
||||
public final static int kNotFoundError = 2;
|
||||
public final static int kUnknownError = 3;
|
||||
public final static int kInternalError = 4;
|
||||
public final static int kNoSimCardError = 5;
|
||||
public final static int kRadioDisabledError = 6;
|
||||
public final static int kInvalidAddressError = 7;
|
||||
public final static int kFdnCheckError = 8;
|
||||
public final static int kNoError = 0;
|
||||
public final static int kNoSignalError = 1;
|
||||
public final static int kNotFoundError = 2;
|
||||
public final static int kUnknownError = 3;
|
||||
public final static int kInternalError = 4;
|
||||
public final static int kNoSimCardError = 5;
|
||||
public final static int kRadioDisabledError = 6;
|
||||
public final static int kInvalidAddressError = 7;
|
||||
public final static int kFdnCheckError = 8;
|
||||
public final static int kNonActiveSimCardError = 9;
|
||||
|
||||
private final static int kMaxMessageSize = 160;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user