Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2013-11-05 15:36:23 -05:00
commit 323496ebfb
122 changed files with 3323 additions and 1165 deletions

View File

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

View File

@ -1,4 +1,4 @@
{
"revision": "9c89bc252dca005ec14f54d6d8524b44917322b2",
"revision": "53c6a2b0bf4c238d39ee0096b79d911aec2de0fc",
"repo_path": "/integration/gaia-central"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -159,7 +159,7 @@ var Scratchpad = {
{
this._dirty = aValue;
if (!aValue && this.editor)
this.editor.markClean();
this.editor.setClean();
this._updateTitle();
},

View File

@ -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: []
}
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -376,7 +376,7 @@ StyleSheetEditor.prototype = {
if (callback) {
callback(returnFile);
}
this.sourceEditor.markClean();
this.sourceEditor.setClean();
}.bind(this));
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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