diff --git a/CLOBBER b/CLOBBER index ae60a813d69..6a19e0b8aa4 100644 --- a/CLOBBER +++ b/CLOBBER @@ -18,4 +18,4 @@ # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 914270 needs a clobber since moving variables to moz.build always requires a clobber (bug 852814) +Bug 918345 needs a clobber due to WebIDL binding dependency issues (bug 928195). diff --git a/accessible/src/html/HTMLFormControlAccessible.cpp b/accessible/src/html/HTMLFormControlAccessible.cpp index 0f0ea7aaa26..1384b4b35f9 100644 --- a/accessible/src/html/HTMLFormControlAccessible.cpp +++ b/accessible/src/html/HTMLFormControlAccessible.cpp @@ -24,7 +24,6 @@ #include "nsINameSpaceManager.h" #include "nsIPersistentProperties2.h" #include "nsISelectionController.h" -#include "jsapi.h" #include "nsIServiceManager.h" #include "nsITextControlFrame.h" diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 748413a5c8f..27d8d7e0081 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -728,6 +728,7 @@ pref("font.size.inflation.disabledInMasterProcess", true); pref("memory.free_dirty_pages", true); pref("layout.imagevisibility.enabled", false); +pref("layout.imagevisibility.enabled_for_browser_elements_only", true); pref("layout.imagevisibility.numscrollportwidths", 1); pref("layout.imagevisibility.numscrollportheights", 1); diff --git a/b2g/components/ContentPermissionPrompt.js b/b2g/components/ContentPermissionPrompt.js index 0b163cbd44f..05ed5c01880 100644 --- a/b2g/components/ContentPermissionPrompt.js +++ b/b2g/components/ContentPermissionPrompt.js @@ -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]); diff --git a/browser/base/content/browser-plugins.js b/browser/base/content/browser-plugins.js index 3f56795656e..b585714d8a4 100644 --- a/browser/base/content/browser-plugins.js +++ b/browser/base/content/browser-plugins.js @@ -213,20 +213,12 @@ var gPluginHandler = { }, handleEvent : function(event) { - let plugin; - let doc; - let eventType = event.type; - if (eventType === "PluginRemoved") { - doc = event.target; - } - else { - plugin = event.target; - doc = plugin.ownerDocument; + let plugin = event.target; + let doc = plugin.ownerDocument; - if (!(plugin instanceof Ci.nsIObjectLoadingContent)) - return; - } + if (!(plugin instanceof Ci.nsIObjectLoadingContent)) + return; if (eventType == "PluginBindingAttached") { // The plugin binding fires this event when it is created. @@ -308,13 +300,12 @@ var gPluginHandler = { break; case "PluginInstantiated": - case "PluginRemoved": shouldShowNotification = true; break; } // Show the in-content UI if it's not too big. The crashed plugin handler already did this. - if (eventType != "PluginCrashed" && eventType != "PluginRemoved") { + if (eventType != "PluginCrashed") { let overlay = this.getPluginUI(plugin, "main"); if (overlay != null) { if (!this.isTooSmall(plugin, overlay)) @@ -336,7 +327,7 @@ var gPluginHandler = { // Only show the notification after we've done the isTooSmall check, so // that the notification can decide whether to show the "alert" icon if (shouldShowNotification) { - this._showClickToPlayNotification(browser); + this._showClickToPlayNotification(browser, plugin, false); } }, @@ -562,7 +553,7 @@ var gPluginHandler = { if (!(aEvent.originalTarget instanceof HTMLAnchorElement) && (aEvent.originalTarget.getAttribute('anonid') != 'closeIcon') && aEvent.button == 0 && aEvent.isTrusted) { - gPluginHandler._showClickToPlayNotification(browser, plugin); + gPluginHandler._showClickToPlayNotification(browser, plugin, true); aEvent.stopPropagation(); aEvent.preventDefault(); } @@ -607,7 +598,7 @@ var gPluginHandler = { }, true); if (!playPreviewInfo.ignoreCTP) { - gPluginHandler._showClickToPlayNotification(browser); + gPluginHandler._showClickToPlayNotification(browser, aPlugin, false); } }, @@ -625,14 +616,20 @@ var gPluginHandler = { if (gPluginHandler.canActivatePlugin(objLoadingContent)) gPluginHandler._handleClickToPlayEvent(plugin); } - gPluginHandler._showClickToPlayNotification(browser); + gPluginHandler._showClickToPlayNotification(browser, null, false); }, _clickToPlayNotificationEventCallback: function PH_ctpEventCallback(event) { if (event == "showing") { - gPluginHandler._makeCenterActions(this); Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_SHOWN") .add(!this.options.primaryPlugin); + // Histograms always start at 0, even though our data starts at 1 + let histogramCount = this.options.centerActions.size - 1; + if (histogramCount > 4) { + histogramCount = 4; + } + Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT") + .add(histogramCount); } else if (event == "dismissed") { // Once the popup is dismissed, clicking the icon should show the full @@ -655,86 +652,6 @@ var gPluginHandler = { return principal.origin; }, - _makeCenterActions: function PH_makeCenterActions(notification) { - let contentWindow = notification.browser.contentWindow; - let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - - let principal = contentWindow.document.nodePrincipal; - // This matches the behavior of nsPermssionManager, used for display purposes only - let principalHost = this._getHostFromPrincipal(principal); - - let centerActions = []; - let pluginsFound = new Set(); - for (let plugin of cwu.plugins) { - plugin.QueryInterface(Ci.nsIObjectLoadingContent); - if (plugin.getContentTypeForMIMEType(plugin.actualType) != Ci.nsIObjectLoadingContent.TYPE_PLUGIN) { - continue; - } - - let pluginInfo = this._getPluginInfo(plugin); - if (pluginInfo.permissionString === null) { - Components.utils.reportError("No permission string for active plugin."); - continue; - } - if (pluginsFound.has(pluginInfo.permissionString)) { - continue; - } - pluginsFound.add(pluginInfo.permissionString); - - // Add the per-site permissions and details URLs to pluginInfo here - // because they are more expensive to compute and so we avoid it in - // the tighter loop above. - let permissionObj = Services.perms. - getPermissionObject(principal, pluginInfo.permissionString, false); - if (permissionObj) { - pluginInfo.pluginPermissionHost = permissionObj.host; - pluginInfo.pluginPermissionType = permissionObj.expireType; - } - else { - pluginInfo.pluginPermissionHost = principalHost; - pluginInfo.pluginPermissionType = undefined; - } - - let url; - // TODO: allow the blocklist to specify a better link, bug 873093 - if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) { - url = Services.urlFormatter.formatURLPref("plugins.update.url"); - } - else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) { - url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag); - } - else { - url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay"; - } - pluginInfo.detailsLink = url; - - centerActions.push(pluginInfo); - } - - if (centerActions.length == 0) { - // TODO: this is a temporary band-aid to avoid broken doorhangers - // until bug 926605 is landed. - notification.options.centerActions = []; - setTimeout(() => PopupNotifications.remove(notification), 0); - return; - } - - centerActions.sort(function(a, b) { - return a.pluginName.localeCompare(b.pluginName); - }); - - notification.options.centerActions = centerActions; - - // Histograms always start at 0, even though our data starts at 1 - let histogramCount = centerActions.length - 1; - if (histogramCount > 4) { - histogramCount = 4; - } - Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT") - .add(histogramCount); - }, - /** * Called from the plugin doorhanger to set the new permissions for a plugin * and activate plugins if necessary. @@ -798,6 +715,7 @@ var gPluginHandler = { let plugins = cwu.plugins; let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); + let pluginFound = false; for (let plugin of plugins) { plugin.QueryInterface(Ci.nsIObjectLoadingContent); // canActivatePlugin will return false if this isn't a known plugin type, @@ -809,60 +727,141 @@ var gPluginHandler = { overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true); } plugin.playPlugin(); + pluginFound = true; } } + + // If there are no instances of the plugin on the page any more, what the + // user probably needs is for us to allow and then refresh. + if (!pluginFound) { + browser.reload(); + } }, - _showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser, aPrimaryPlugin) { + _showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser, aPlugin, aShowNow) { let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser); + let plugins = []; - let contentWindow = aBrowser.contentWindow; - let contentDoc = aBrowser.contentDocument; - let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - // cwu.plugins may contain non-plugin s, filter them out - let plugins = cwu.plugins.filter((plugin) => - plugin.getContentTypeForMIMEType(plugin.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN); - if (plugins.length == 0) { - if (notification) { - PopupNotifications.remove(notification); + // if aPlugin is null, that means the user has navigated back to a page with plugins, and we need + // to collect all the plugins + if (aPlugin === null) { + let contentWindow = aBrowser.contentWindow; + let contentDoc = aBrowser.contentDocument; + let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + // cwu.plugins may contain non-plugin s, filter them out + plugins = cwu.plugins.filter((plugin) => + plugin.getContentTypeForMIMEType(plugin.actualType) == Ci.nsIObjectLoadingContent.TYPE_PLUGIN); + + if (plugins.length == 0) { + if (notification) { + PopupNotifications.remove(notification); + } + return; + } + } else { + plugins = [aPlugin]; + } + + // If this is a new notification, create a centerActions map, otherwise append + let centerActions; + if (notification) { + centerActions = notification.options.centerActions; + } else { + centerActions = new Map(); + } + + let principal = aBrowser.contentDocument.nodePrincipal; + let principalHost = this._getHostFromPrincipal(principal); + + for (var plugin of plugins) { + let pluginInfo = this._getPluginInfo(plugin); + if (pluginInfo.permissionString === null) { + Cu.reportError("No permission string for active plugin."); + continue; + } + if (centerActions.has(pluginInfo.permissionString)) { + continue; + } + + // Assume that plugins are hidden and then set override later + pluginInfo.hidden = true; + + let overlay = this.getPluginUI(plugin, "main"); + if (overlay && overlay.style.visibility != "hidden" && overlay.style.visibility != "") { + pluginInfo.hidden = false; + } + + let permissionObj = Services.perms. + getPermissionObject(principal, pluginInfo.permissionString, false); + if (permissionObj) { + pluginInfo.pluginPermissionHost = permissionObj.host; + pluginInfo.pluginPermissionType = permissionObj.expireType; + } + else { + pluginInfo.pluginPermissionHost = principalHost; + pluginInfo.pluginPermissionType = undefined; + } + + let url; + // TODO: allow the blocklist to specify a better link, bug 873093 + if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) { + url = Services.urlFormatter.formatURLPref("plugins.update.url"); + } + else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) { + url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag); + } + else { + url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay"; + } + pluginInfo.detailsLink = url; + + centerActions.set(pluginInfo.permissionString, pluginInfo); + } + + let pluginBlocked = false; + let pluginHidden = false; + for (let pluginInfo of centerActions.values()) { + let fallbackType = pluginInfo.fallbackType; + if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE || + fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE || + fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED) { + pluginBlocked = true; + pluginHidden = false; + break; + } + if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY && pluginInfo.hidden) { + pluginHidden = true; + } + } + + let iconClasses = document.getElementById("plugins-notification-icon").classList; + iconClasses.toggle("plugin-blocked", pluginBlocked); + iconClasses.toggle("plugin-hidden", pluginHidden); + + let primaryPluginPermission = null; + if (aShowNow) { + primaryPluginPermission = this._getPluginInfo(aPlugin).permissionString; + } + + if (notification) { + // Don't modify the notification UI while it's on the screen, that would be + // jumpy and might allow clickjacking. + if (aShowNow) { + notification.options.primaryPlugin = primaryPluginPermission; + notification.reshow(); } return; } - let icon = 'plugins-notification-icon'; - for (let plugin of plugins) { - let fallbackType = plugin.pluginFallbackType; - if (fallbackType == plugin.PLUGIN_VULNERABLE_UPDATABLE || - fallbackType == plugin.PLUGIN_VULNERABLE_NO_UPDATE || - fallbackType == plugin.PLUGIN_BLOCKLISTED) { - icon = 'blocked-plugins-notification-icon'; - break; - } - if (fallbackType == plugin.PLUGIN_CLICK_TO_PLAY) { - let overlay = this.getPluginUI(plugin, "main"); - if (!overlay || overlay.style.visibility == 'hidden') { - icon = 'alert-plugins-notification-icon'; - } - } - } - - let dismissed = notification ? notification.dismissed : true; - if (aPrimaryPlugin) - dismissed = false; - - let primaryPluginPermission = null; - if (aPrimaryPlugin) { - primaryPluginPermission = this._getPluginInfo(aPrimaryPlugin).permissionString; - } - let options = { - dismissed: dismissed, + dismissed: !aShowNow, eventCallback: this._clickToPlayNotificationEventCallback, - primaryPlugin: primaryPluginPermission + primaryPlugin: primaryPluginPermission, + centerActions: centerActions }; PopupNotifications.show(aBrowser, "click-to-play-plugins", - "", icon, + "", "plugins-notification-icon", null, null, options); }, diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 9a23dde86d0..78dbbd173a3 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -756,7 +756,6 @@ var gBrowserInit = { gBrowser.addEventListener("PluginCrashed", gPluginHandler, true); gBrowser.addEventListener("PluginOutdated", gPluginHandler, true); gBrowser.addEventListener("PluginInstantiated", gPluginHandler, true); - gBrowser.addEventListener("PluginRemoved", gPluginHandler, true); gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true); diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 945b216469a..0c8beea7bf7 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -534,8 +534,6 @@ - - @@ -571,7 +569,7 @@ onclick="gPopupBlockerObserver.onReportButtonClick(event);"/> + onclick="if (event.button === 0) BookmarkingUI.onCommand(event);"/> !notification.dismissed, + test3, "Test 2, waited too long for context activation"); +} +function test3() { // Activate the plugin PopupNotifications.panel.firstChild._primaryButton.click(); let plugin = gTestBrowser.contentDocument.getElementById("test"); let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); - waitForCondition(() => objLoadingContent.activated, test3, "Waited too long for plugin to activate"); + waitForCondition(() => objLoadingContent.activated, test4, "Waited too long for plugin to activate"); } -function test3() { +function test4() { finishTest(); } diff --git a/browser/base/content/test/general/browser_CTP_data_urls.js b/browser/base/content/test/general/browser_CTP_data_urls.js index 7806f5a397a..b8856a95500 100644 --- a/browser/base/content/test/general/browser_CTP_data_urls.js +++ b/browser/base/content/test/general/browser_CTP_data_urls.js @@ -161,10 +161,10 @@ function test2b() { // Simulate choosing "Allow now" for the test plugin notification.reshow(); - is(notification.options.centerActions.length, 2, "Test 2b, Should have two types of plugin in the notification"); + is(notification.options.centerActions.size, 2, "Test 2b, Should have two types of plugin in the notification"); var centerAction = null; - for (var action of notification.options.centerActions) { + for (var action of notification.options.centerActions.values()) { if (action.pluginName == "Test") { centerAction = action; break; diff --git a/browser/base/content/test/general/browser_CTP_drag_drop.js b/browser/base/content/test/general/browser_CTP_drag_drop.js index 3718fcdae39..62c50d9fc23 100644 --- a/browser/base/content/test/general/browser_CTP_drag_drop.js +++ b/browser/base/content/test/general/browser_CTP_drag_drop.js @@ -64,13 +64,11 @@ function part5() { gBrowser.selectedBrowser.removeEventListener("PluginBindingAttached", handleEvent); ok(PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should have a click-to-play notification in the initial tab"); - gNextTest = part6; gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab); - gNewWindow.addEventListener("load", handleEvent, true); + waitForFocus(part6, gNewWindow); } function part6() { - gNewWindow.removeEventListener("load", handleEvent); let condition = function() PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser); waitForCondition(condition, part7, "Waited too long for click-to-play notification"); } diff --git a/browser/base/content/test/general/browser_CTP_iframe.js b/browser/base/content/test/general/browser_CTP_iframe.js deleted file mode 100644 index a88fa860ab0..00000000000 --- a/browser/base/content/test/general/browser_CTP_iframe.js +++ /dev/null @@ -1,135 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir; -const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/"); - -var gTestBrowser = null; -var gNextTest = null; -var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost); -var gPageLoads = 0; - -Components.utils.import("resource://gre/modules/Services.jsm"); - -// This listens for the next opened tab and checks it is of the right url. -// opencallback is called when the new tab is fully loaded -// closecallback is called when the tab is closed -function TabOpenListener(url, opencallback, closecallback) { - this.url = url; - this.opencallback = opencallback; - this.closecallback = closecallback; - - gBrowser.tabContainer.addEventListener("TabOpen", this, false); -} - -TabOpenListener.prototype = { - url: null, - opencallback: null, - closecallback: null, - tab: null, - browser: null, - - handleEvent: function(event) { - if (event.type == "TabOpen") { - gBrowser.tabContainer.removeEventListener("TabOpen", this, false); - this.tab = event.originalTarget; - this.browser = this.tab.linkedBrowser; - gBrowser.addEventListener("pageshow", this, false); - } else if (event.type == "pageshow") { - if (event.target.location.href != this.url) - return; - gBrowser.removeEventListener("pageshow", this, false); - this.tab.addEventListener("TabClose", this, false); - var url = this.browser.contentDocument.location.href; - is(url, this.url, "Should have opened the correct tab"); - this.opencallback(this.tab, this.browser.contentWindow); - } else if (event.type == "TabClose") { - if (event.originalTarget != this.tab) - return; - this.tab.removeEventListener("TabClose", this, false); - this.opencallback = null; - this.tab = null; - this.browser = null; - // Let the window close complete - executeSoon(this.closecallback); - this.closecallback = null; - } - } -}; - -function test() { - waitForExplicitFinish(); - registerCleanupFunction(function() { - clearAllPluginPermissions(); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - }); - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - var newTab = gBrowser.addTab(); - gBrowser.selectedTab = newTab; - gTestBrowser = gBrowser.selectedBrowser; - gTestBrowser.addEventListener("load", pageLoad, true); - - Services.prefs.setBoolPref("plugins.click_to_play", true); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_iframe.html"); -} - -function finishTest() { - clearAllPluginPermissions(); - gTestBrowser.removeEventListener("load", pageLoad, true); - gBrowser.removeCurrentTab(); - window.focus(); - finish(); -} - -function pageLoad() { - // Wait for the iframe to be loaded as well. - if (gPageLoads++ < 1) - return; - - // The plugin events are async dispatched and can come after the load event - // This just allows the events to fire before we then go on to test the states. - executeSoon(gNextTest); -} - -function prepareTest(nextTest, url) { - gNextTest = nextTest; - gTestBrowser.contentWindow.location = url; -} - -// Due to layout being async, "PluginBindAttached" may trigger later. -// This wraps a function to force a layout flush, thus triggering it, -// and schedules the function execution so they're definitely executed -// afterwards. -function runAfterPluginBindingAttached(func) { - return function() { - let doc = gTestBrowser.contentDocument.getElementById('frame').contentDocument; - let elems = doc.getElementsByTagName('embed'); - if (elems.length < 1) { - elems = doc.getElementsByTagName('object'); - } - elems[0].clientTop; - executeSoon(func); - }; -} - -// Test that we don't show a doorhanger after removing the last plugin -// when the plugin was in an iframe. - -function test1() { - let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); - ok(popupNotification, "Test 1, Should have a click-to-play notification"); - - let frame = gTestBrowser.contentDocument.getElementById("frame"); - frame.parentElement.removeChild(frame); - - let condition = () => { - let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); - if (notification) { - notification.reshow(); - } - return !notification; - } - - waitForCondition(condition, finishTest, "Test1, Waited too long for notification too be removed"); -} diff --git a/browser/base/content/test/general/browser_CTP_nonplugins.js b/browser/base/content/test/general/browser_CTP_nonplugins.js index 4cf5c3c9850..41190663784 100644 --- a/browser/base/content/test/general/browser_CTP_nonplugins.js +++ b/browser/base/content/test/general/browser_CTP_nonplugins.js @@ -149,12 +149,12 @@ function test2() { let plugin = gTestBrowser.contentDocument.getElementById("test"); plugin.parentNode.removeChild(plugin); - runAfterPluginRemoved(() => executeSoon(test3)); + executeSoon(test3); } function test3() { let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); - ok(!popupNotification, "Test 3, Should not have a click-to-play notification"); + ok(popupNotification, "Test 3, Should still have a click-to-play notification"); finishTest(); } diff --git a/browser/base/content/test/general/browser_bug820497.js b/browser/base/content/test/general/browser_bug820497.js index 36762d59883..54da01b0b3e 100644 --- a/browser/base/content/test/general/browser_bug820497.js +++ b/browser/base/content/test/general/browser_bug820497.js @@ -40,7 +40,7 @@ function pluginBindingAttached() { ok(notification, "should have popup notification"); // We don't set up the action list until the notification is shown notification.reshow(); - is(notification.options.centerActions.length, 1, "should be 1 type of plugin in the popup notification"); + is(notification.options.centerActions.size, 1, "should be 1 type of plugin in the popup notification"); XPCNativeWrapper.unwrap(gTestBrowser.contentWindow).addSecondPlugin(); } else if (gNumPluginBindingsAttached == 2) { var doc = gTestBrowser.contentDocument; @@ -51,7 +51,7 @@ function pluginBindingAttached() { var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); ok(notification, "should have popup notification"); notification.reshow(); - is(notification.options.centerActions.length, 2, "should be 2 types of plugin in the popup notification"); + is(notification.options.centerActions.size, 2, "should be 2 types of plugin in the popup notification"); finish(); } else { ok(false, "if we've gotten here, something is quite wrong"); diff --git a/browser/base/content/test/general/browser_pluginnotification.js b/browser/base/content/test/general/browser_pluginnotification.js index 02c4e17820c..74021ebc97e 100644 --- a/browser/base/content/test/general/browser_pluginnotification.js +++ b/browser/base/content/test/general/browser_pluginnotification.js @@ -179,7 +179,7 @@ function test5() { ok(notification.dismissed, "Test 5: The plugin notification should be dismissed by default"); notification.reshow(); - is(notification.options.centerActions.length, 1, "Test 5: Only the blocked plugin should be present in the notification"); + is(notification.options.centerActions.size, 1, "Test 5: Only the blocked plugin should be present in the notification"); ok(PopupNotifications.panel.firstChild._buttonContainer.hidden, "Part 5: The blocked plugins notification should not have any buttons visible."); ok(!gTestBrowser.missingPlugins, "Test 5, Should not be a missing plugin list"); @@ -441,10 +441,11 @@ function test18f() { var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); ok(!objLoadingContent.activated, "Test 18f, Plugin should not be activated"); - // XXXBAD: this code doesn't do what you think it does! it is actually - // observing the "removed" event of the old notification, since we create - // a *new* one when the plugin is clicked. - notification.options.eventCallback = function() { executeSoon(test18g); }; + var oldEventCallback = notification.options.eventCallback; + notification.options.eventCallback = function() { + oldEventCallback(); + executeSoon(test18g); + }; EventUtils.synthesizeMouseAtCenter(plugin, {}, gTestBrowser.contentWindow); } @@ -603,10 +604,10 @@ function test21a() { // we have to actually show the panel to get the bindings to instantiate notification.reshow(); - is(notification.options.centerActions.length, 2, "Test 21a, Should have two types of plugin in the notification"); + is(notification.options.centerActions.size, 2, "Test 21a, Should have two types of plugin in the notification"); var centerAction = null; - for (var action of notification.options.centerActions) { + for (var action of notification.options.centerActions.values()) { if (action.pluginName == "Test") { centerAction = action; break; @@ -640,7 +641,7 @@ function test21c() { ok(notification, "Test 21c, Should have a click-to-play notification"); notification.reshow(); - ok(notification.options.centerActions.length == 2, "Test 21c, Should have one type of plugin in the notification"); + ok(notification.options.centerActions.size == 2, "Test 21c, Should have one type of plugin in the notification"); var doc = gTestBrowser.contentDocument; var plugin = doc.getElementById("test"); @@ -661,7 +662,7 @@ function test21c() { } var centerAction = null; - for (var action of notification.options.centerActions) { + for (var action of notification.options.centerActions.values()) { if (action.pluginName == "Second Test") { centerAction = action; break; diff --git a/browser/base/content/test/general/browser_plugins_added_dynamically.js b/browser/base/content/test/general/browser_plugins_added_dynamically.js index 449ddd60728..5667288579d 100644 --- a/browser/base/content/test/general/browser_plugins_added_dynamically.js +++ b/browser/base/content/test/general/browser_plugins_added_dynamically.js @@ -68,7 +68,7 @@ function testActivateAddSameTypePart2() { function testActivateAddSameTypePart3() { let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); let centerAction = null; - for (let action of popupNotification.options.centerActions) { + for (let action of popupNotification.options.centerActions.values()) { if (action.pluginName == "Test") { centerAction = action; break; @@ -142,7 +142,7 @@ function testActivateAddDifferentTypePart2() { function testActivateAddDifferentTypePart3() { let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser); - is(popupNotification.options.centerActions.length, 1, "Should be one plugin action"); + is(popupNotification.options.centerActions.size, 1, "Should be one plugin action"); let plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0]; ok(!plugin.activated, "testActivateAddDifferentTypePart3: plugin should not be activated"); diff --git a/browser/base/content/test/general/plugin_iframe.html b/browser/base/content/test/general/plugin_iframe.html deleted file mode 100644 index eed5819d715..00000000000 --- a/browser/base/content/test/general/plugin_iframe.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index e9aa1b1626a..f4a593fe058 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -1567,14 +1567,20 @@ [] a.pluginName.localeCompare(b.pluginName)); + + for (let action of sortedActions) { let item = document.createElementNS(XUL_NS, "row"); item.setAttribute("class", "plugin-popupnotification-centeritem"); item.action = action; this.appendChild(item); this._items.push(item); } - switch (this.notification.options.centerActions.length) { + switch (this._items.length) { case 0: PopupNotifications._dismiss(); break; @@ -1639,7 +1645,7 @@ @@ -1811,7 +1817,7 @@ @@ -1819,7 +1825,7 @@ @@ -1827,7 +1833,7 @@ diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 77fa6750d66..fe5bc568baa 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1964,13 +1964,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; } @@ -1982,7 +1990,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) { @@ -1993,14 +2001,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; diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js index 8af156ea797..c44640c234c 100644 --- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js +++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js @@ -64,7 +64,7 @@ function test() { MockFilePicker.returnFiles = [aFile]; MockFilePicker.displayDirectory = null; - aWin.getTargetFile(params, function() { + aWin.promiseTargetFile(params).then(function() { // File picker should start with expected display dir. is(MockFilePicker.displayDirectory.path, aDisplayDir.path, "File picker should start with browser.download.lastDir"); @@ -78,7 +78,7 @@ function test() { gDownloadLastDir.cleanupPrivateFile(); aWin.close(); aCallback(); - }); + }).then(null, function() { ok(false); }); } testOnWindow(false, function(win, downloadDir) { diff --git a/browser/devtools/shadereditor/test/head.js b/browser/devtools/shadereditor/test/head.js index b3f16dfb0af..31ccc09406d 100644 --- a/browser/devtools/shadereditor/test/head.js +++ b/browser/devtools/shadereditor/test/head.js @@ -135,7 +135,7 @@ function once(aTarget, aEventName, aUseCapture = false) { function waitForFrame(aDebuggee) { let deferred = promise.defer(); - aDebuggee.mozRequestAnimationFrame(deferred.resolve); + aDebuggee.requestAnimationFrame(deferred.resolve); return deferred.promise; } diff --git a/browser/devtools/shared/widgets/SideMenuWidget.jsm b/browser/devtools/shared/widgets/SideMenuWidget.jsm index d134beba0d3..1fd1ccaf485 100644 --- a/browser/devtools/shared/widgets/SideMenuWidget.jsm +++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm @@ -487,7 +487,6 @@ function SideMenuGroup(aWidget, aName, aOptions={}) { if (aOptions.showCheckbox) { let checkbox = this._checkbox = makeCheckbox(title, { description: aName }); checkbox.className = "side-menu-widget-group-checkbox"; - checkbox.setAttribute("align", "start"); } title.appendChild(name); @@ -592,7 +591,6 @@ function SideMenuItem(aGroup, aContents, aTooltip, aAttachment={}, aOptions={}) if (aOptions.showCheckbox) { let checkbox = this._checkbox = makeCheckbox(container, aAttachment); checkbox.className = "side-menu-widget-item-checkbox"; - checkbox.setAttribute("align", "start"); } container.appendChild(target); diff --git a/browser/devtools/shared/widgets/widgets.css b/browser/devtools/shared/widgets/widgets.css index b3d646b688f..b72fdc7fcde 100644 --- a/browser/devtools/shared/widgets/widgets.css +++ b/browser/devtools/shared/widgets/widgets.css @@ -24,6 +24,11 @@ -moz-user-focus: normal; } +.side-menu-widget-group-checkbox .checkbox-label-box, +.side-menu-widget-item-checkbox .checkbox-label-box { + display: none; /* See bug 669507 */ +} + /* VariablesView */ .variables-view-container { diff --git a/browser/devtools/webconsole/test/browser_webconsole_property_provider.js b/browser/devtools/webconsole/test/browser_webconsole_property_provider.js index 463b1301a13..644345de4b3 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_property_provider.js +++ b/browser/devtools/webconsole/test/browser_webconsole_property_provider.js @@ -31,8 +31,10 @@ function testPropertyProvider() { completion = JSPropertyProvider(content, strComplete); ok(completion.matches.length == 2, "two matches found"); ok(completion.matchProp == "locatio", "matching part is 'test'"); - ok(completion.matches[0] == "location", "the first match is 'location'"); - ok(completion.matches[1] == "locationbar", "the second match is 'locationbar'"); + var matches = completion.matches; + matches.sort(); + ok(matches[0] == "location", "the first match is 'location'"); + ok(matches[1] == "locationbar", "the second match is 'locationbar'"); finishTest(); } diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 4d93642d4d5..a5668681988 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,4 +1,4 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 0.8.629 +Current extension version is: 0.8.641 diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 2e74baa6cee..6ab713e0edc 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.629'; -PDFJS.build = 'b16b3be'; +PDFJS.version = '0.8.641'; +PDFJS.build = '19485c3'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -6575,6 +6575,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var bounds = Util.getAxialAlignedBoundingBox( group.bbox, currentCtx.mozCurrentTransform); + // Clip the bounding box to the current canvas. + bounds = Util.intersect(bounds, [0, + 0, + currentCtx.canvas.width, + currentCtx.canvas.height]); // Use ceil in case we're between sizes so we don't create canvas that is // too small and make the canvas at least 1x1 pixels. var drawnWidth = Math.max(Math.ceil(bounds[2] - bounds[0]), 1); diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 4a53ed48e79..5ece3d5b9f0 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.629'; -PDFJS.build = 'b16b3be'; +PDFJS.version = '0.8.641'; +PDFJS.build = '19485c3'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it diff --git a/browser/extensions/pdfjs/content/web/viewer.html b/browser/extensions/pdfjs/content/web/viewer.html index a5bbdc3845b..ef2ef365c62 100644 --- a/browser/extensions/pdfjs/content/web/viewer.html +++ b/browser/extensions/pdfjs/content/web/viewer.html @@ -223,7 +223,7 @@ limitations under the License. data-l10n-id="page_rotate_ccw"> -
+
diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index da0a1c27e52..36bae571fb9 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -1299,11 +1299,12 @@ var SecondaryToolbar = { initialize: function secondaryToolbarInitialize(options) { this.toolbar = options.toolbar; + this.presentationMode = options.presentationMode; this.buttonContainer = this.toolbar.firstElementChild; // Define the toolbar buttons. this.toggleButton = options.toggleButton; - this.presentationMode = options.presentationMode; + this.presentationModeButton = options.presentationModeButton; this.openFile = options.openFile; this.print = options.print; this.download = options.download; @@ -1315,7 +1316,8 @@ var SecondaryToolbar = { // Attach the event listeners. var elements = [ { element: this.toggleButton, handler: this.toggle }, - { element: this.presentationMode, handler: this.presentationModeClick }, + { element: this.presentationModeButton, + handler: this.presentationModeClick }, { element: this.openFile, handler: this.openFileClick }, { element: this.print, handler: this.printClick }, { element: this.download, handler: this.downloadClick }, @@ -1335,7 +1337,7 @@ var SecondaryToolbar = { // Event handling functions. presentationModeClick: function secondaryToolbarPresentationModeClick(evt) { - PresentationMode.request(); + this.presentationMode.request(); this.close(); }, @@ -1372,7 +1374,7 @@ var SecondaryToolbar = { // Misc. functions for interacting with the toolbar. setMaxHeight: function secondaryToolbarSetMaxHeight(container) { - if (!container) { + if (!container || !this.buttonContainer) { return; } this.newContainerHeight = container.clientHeight; @@ -1410,10 +1412,6 @@ var SecondaryToolbar = { } else { this.open(); } - }, - - get isOpen() { - return this.opened; } }; @@ -1506,6 +1504,8 @@ var PresentationMode = { this.container = options.container; this.secondaryToolbar = options.secondaryToolbar; + this.viewer = this.container.firstElementChild; + this.firstPage = options.firstPage; this.lastPage = options.lastPage; this.pageRotateCw = options.pageRotateCw; @@ -1538,7 +1538,8 @@ var PresentationMode = { }, request: function presentationModeRequest() { - if (!PDFView.supportsFullscreen || this.isFullscreen) { + if (!PDFView.supportsFullscreen || this.isFullscreen || + !this.viewer.hasChildNodes()) { return false; } @@ -1720,8 +1721,10 @@ var PDFView = { SecondaryToolbar.initialize({ toolbar: document.getElementById('secondaryToolbar'), + presentationMode: PresentationMode, toggleButton: document.getElementById('secondaryToolbarToggle'), - presentationMode: document.getElementById('secondaryPresentationMode'), + presentationModeButton: + document.getElementById('secondaryPresentationMode'), openFile: document.getElementById('secondaryOpenFile'), print: document.getElementById('secondaryPrint'), download: document.getElementById('secondaryDownload'), @@ -1968,8 +1971,8 @@ var PDFView = { }, get isHorizontalScrollbarEnabled() { - var div = document.getElementById('viewerContainer'); - return div.scrollWidth > div.clientWidth; + return (PresentationMode.active ? false : + (this.container.scrollWidth > this.container.clientWidth)); }, initPassiveLoading: function pdfViewInitPassiveLoading() { @@ -3230,6 +3233,7 @@ var PageView = function pageView(container, id, scale, this.scrollIntoView = function pageViewScrollIntoView(dest) { if (PresentationMode.active) { // Avoid breaking presentation mode. dest = null; + PDFView.setScale(PDFView.currentScaleValue, true, true); } if (!dest) { scrollIntoView(div); @@ -4554,7 +4558,7 @@ window.addEventListener('DOMMouseScroll', function(evt) { window.addEventListener('click', function click(evt) { if (!PresentationMode.active) { - if (SecondaryToolbar.isOpen && PDFView.container.contains(evt.target)) { + if (SecondaryToolbar.opened && PDFView.container.contains(evt.target)) { SecondaryToolbar.close(); } } else if (evt.button === 0) { @@ -4649,6 +4653,9 @@ window.addEventListener('keydown', function keydown(evt) { return; // ignoring if the 'toolbar' element is focused curElement = curElement.parentNode; } + // Workaround for issue in Firefox, that prevents scroll keys from working + // when elements with 'tabindex' are focused. + PDFView.container.blur(); if (cmd === 0) { // no control key pressed at all. switch (evt.keyCode) { @@ -4673,7 +4680,7 @@ window.addEventListener('keydown', function keydown(evt) { handled = true; break; case 27: // esc key - if (SecondaryToolbar.isOpen) { + if (SecondaryToolbar.opened) { SecondaryToolbar.close(); handled = true; } diff --git a/browser/extensions/pdfjs/extension-files b/browser/extensions/pdfjs/extension-files index 6a68225cd64..1df5aab8cd8 100644 --- a/browser/extensions/pdfjs/extension-files +++ b/browser/extensions/pdfjs/extension-files @@ -1,11 +1,11 @@ chrome.manifest components/PdfRedirector.js components/PdfStreamConverter.js +content/PdfJs.jsm +content/PdfJsTelemetry.jsm content/build/pdf.js content/build/pdf.worker.js content/network.js -content/PdfJs.jsm -content/PdfJsTelemetry.jsm content/web/debugger.js content/web/images/annotation-check.svg content/web/images/annotation-comment.svg diff --git a/browser/metro/base/content/helperui/IndexedDB.js b/browser/metro/base/content/helperui/IndexedDB.js index 8c347ab5e36..c62fc4734da 100644 --- a/browser/metro/base/content/helperui/IndexedDB.js +++ b/browser/metro/base/content/helperui/IndexedDB.js @@ -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, diff --git a/browser/metro/components/ContentPermissionPrompt.js b/browser/metro/components/ContentPermissionPrompt.js index 44a69c2d566..c6ac9f5a589 100644 --- a/browser/metro/components/ContentPermissionPrompt.js +++ b/browser/metro/components/ContentPermissionPrompt.js @@ -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")); diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 58ddc032f50..d5884cae433 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -1296,30 +1296,22 @@ toolbar[iconsize="small"] #webrtc-status-button { #plugins-notification-icon { list-style-image: url(chrome://browser/skin/notification-pluginNormal.png); } - -#alert-plugins-notification-icon { +#plugins-notification-icon.plugin-hidden { list-style-image: url(chrome://browser/skin/notification-pluginAlert.png); } - -#blocked-plugins-notification-icon { +#plugins-notification-icon.plugin-blocked { list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png); } -#plugins-notification-icon, -#alert-plugins-notification-icon, -#blocked-plugins-notification-icon { +#plugins-notification-icon { -moz-image-region: rect(0, 16px, 16px, 0); } -#plugins-notification-icon:hover, -#alert-plugins-notification-icon:hover, -#blocked-plugins-notification-icon:hover { +#plugins-notification-icon:hover { -moz-image-region: rect(0, 32px, 16px, 16px); } -#plugins-notification-icon:active, -#alert-plugins-notification-icon:active, -#blocked-plugins-notification-icon:active { +#plugins-notification-icon:active { -moz-image-region: rect(0, 48px, 16px, 32px); } @@ -1334,7 +1326,7 @@ toolbar[iconsize="small"] #webrtc-status-button { visibility: collapse; } -#blocked-plugins-notification-icon[showing] { +#plugins-notification-icon.plugin-blocked[showing] { animation: pluginBlockedNotification 500ms ease 0s 5 alternate both; } diff --git a/browser/themes/linux/devtools/debugger.css b/browser/themes/linux/devtools/debugger.css index e6ffe51ae16..f45017b5801 100644 --- a/browser/themes/linux/devtools/debugger.css +++ b/browser/themes/linux/devtools/debugger.css @@ -24,8 +24,11 @@ -moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */ } +#sources-toolbar .devtools-toolbarbutton { + min-width: 32px; +} + #pretty-print { - min-width: 0; font-weight: bold; } diff --git a/browser/themes/linux/devtools/shadereditor.css b/browser/themes/linux/devtools/shadereditor.css index 34817e2453f..a914dfb5ee0 100644 --- a/browser/themes/linux/devtools/shadereditor.css +++ b/browser/themes/linux/devtools/shadereditor.css @@ -3,3 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ %include ../../shared/devtools/shadereditor.inc.css + +.side-menu-widget-item-checkbox > .checkbox-spacer-box { + -moz-appearance: none; +} diff --git a/browser/themes/linux/devtools/widgets.css b/browser/themes/linux/devtools/widgets.css index 984298ea92d..36255d04cdd 100644 --- a/browser/themes/linux/devtools/widgets.css +++ b/browser/themes/linux/devtools/widgets.css @@ -317,11 +317,6 @@ background-image: linear-gradient(#fff, #eee); } -.side-menu-widget-group-checkbox { - margin: 0; - padding: 0; -} - /* SideMenuWidget items */ .side-menu-widget-item[theme="dark"] { @@ -379,11 +374,6 @@ width: 8px; } -.side-menu-widget-item-checkbox { - -moz-margin-start: 4px; - -moz-margin-end: -6px; -} - .side-menu-widget-item-other { background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65); } @@ -403,6 +393,25 @@ text-shadow: 0 1px 1px #111; } +/* SideMenuWidget checkboxes */ + +.side-menu-widget-group-checkbox { + margin: 0; + -moz-margin-end: 4px; +} + +.side-menu-widget-item-checkbox { + margin: 0; + -moz-margin-start: 4px; + -moz-margin-end: -4px; +} + +.side-menu-widget-group-checkbox .checkbox-spacer-box, +.side-menu-widget-item-checkbox .checkbox-spacer-box { + margin: 0; + border: none; +} + /* SideMenuWidget misc */ .side-menu-widget-empty-notice-container { diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 321aa2ff58a..ba97aa693ae 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -3207,29 +3207,23 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { list-style-image: url(chrome://browser/skin/notification-pluginNormal.png); } -#alert-plugins-notification-icon { +#plugins-notification-icon.plugin-hidden { list-style-image: url(chrome://browser/skin/notification-pluginAlert.png); } -#blocked-plugins-notification-icon { +#plugins-notification-icon.plugin-blocked { list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png); } -#plugins-notification-icon, -#alert-plugins-notification-icon, -#blocked-plugins-notification-icon { +#plugins-notification-icon { -moz-image-region: rect(0, 16px, 16px, 0); } -#plugins-notification-icon:hover, -#alert-plugins-notification-icon:hover, -#blocked-plugins-notification-icon:hover { +#plugins-notification-icon:hover { -moz-image-region: rect(0, 32px, 16px, 16px); } -#plugins-notification-icon:active, -#alert-plugins-notification-icon:active, -#blocked-plugins-notification-icon:active { +#plugins-notification-icon:active { -moz-image-region: rect(0, 48px, 16px, 32px); } @@ -3238,29 +3232,23 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { list-style-image: url(chrome://browser/skin/notification-pluginNormal@2x.png); } - #alert-plugins-notification-icon { + #plugins-notification-icon.plugin-hidden { list-style-image: url(chrome://browser/skin/notification-pluginAlert@2x.png); } - #blocked-plugins-notification-icon { + #plugins-notification-icon.plugin-blocked { list-style-image: url(chrome://browser/skin/notification-pluginBlocked@2x.png); } - #plugins-notification-icon, - #alert-plugins-notification-icon, - #blocked-plugins-notification-icon { + #plugins-notification-icon { -moz-image-region: rect(0, 32px, 32px, 0); } - #plugins-notification-icon:hover, - #alert-plugins-notification-icon:hover, - #blocked-plugins-notification-icon:hover { + #plugins-notification-icon:hover { -moz-image-region: rect(0, 64px, 32px, 32px); } - #plugins-notification-icon:active, - #alert-plugins-notification-icon:active, - #blocked-plugins-notification-icon:active { + #plugins-notification-icon:active { -moz-image-region: rect(0, 96px, 32px, 64px); } } @@ -3281,7 +3269,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { visibility: collapse; } -#blocked-plugins-notification-icon[showing] { +#plugins-notification-icon.plugin-blocked[showing] { animation: pluginBlockedNotification 500ms ease 0s 5 alternate both; } diff --git a/browser/themes/osx/devtools/debugger.css b/browser/themes/osx/devtools/debugger.css index a8d3f94e7b7..c2f0305a6a3 100644 --- a/browser/themes/osx/devtools/debugger.css +++ b/browser/themes/osx/devtools/debugger.css @@ -26,8 +26,11 @@ -moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */ } +#sources-toolbar .devtools-toolbarbutton { + min-width: 32px; +} + #pretty-print { - min-width: 0; font-weight: bold; } diff --git a/browser/themes/osx/devtools/widgets.css b/browser/themes/osx/devtools/widgets.css index d5a93d14a54..cbe788464c6 100644 --- a/browser/themes/osx/devtools/widgets.css +++ b/browser/themes/osx/devtools/widgets.css @@ -317,11 +317,6 @@ background-image: linear-gradient(#fff, #eee); } -.side-menu-widget-group-checkbox { - margin: 0; - padding: 0; -} - /* SideMenuWidget items */ .side-menu-widget-item[theme="dark"] { @@ -379,11 +374,6 @@ width: 8px; } -.side-menu-widget-item-checkbox { - -moz-margin-start: 4px; - -moz-margin-end: -6px; -} - .side-menu-widget-item-other { background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65); } @@ -403,6 +393,19 @@ text-shadow: 0 1px 1px #111; } +/* SideMenuWidget checkboxes */ + +.side-menu-widget-group-checkbox { + margin: 0; + -moz-margin-end: 4px; +} + +.side-menu-widget-item-checkbox { + margin: 0; + -moz-margin-start: 4px; + -moz-margin-end: -4px; +} + /* SideMenuWidget misc */ .side-menu-widget-empty-notice-container { diff --git a/browser/themes/shared/devtools/shadereditor.inc.css b/browser/themes/shared/devtools/shadereditor.inc.css index dd6f0242a66..ef93a59b399 100644 --- a/browser/themes/shared/devtools/shadereditor.inc.css +++ b/browser/themes/shared/devtools/shadereditor.inc.css @@ -51,8 +51,6 @@ .side-menu-widget-item-checkbox { -moz-appearance: none; - -moz-margin-end: -6px; - padding: 0; opacity: 0; transition: opacity .15s ease-out 0s; } @@ -66,10 +64,10 @@ transition: opacity .15s ease-out 0s; } -.side-menu-widget-item-checkbox > .checkbox-check { +.side-menu-widget-item-checkbox .checkbox-check { -moz-appearance: none; background: none; - background-image: url("chrome://browser/skin/devtools/itemToggle.png"); + background-image: url(itemToggle.png); background-repeat: no-repeat; background-clip: content-box; background-size: 32px 16px; @@ -79,7 +77,7 @@ border: 0; } -.side-menu-widget-item-checkbox[checked] > .checkbox-check { +.side-menu-widget-item-checkbox[checked] .checkbox-check { background-position: 0 0; } diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index f202aecd729..7e2c44b61fb 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -2568,30 +2568,23 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { #plugins-notification-icon { list-style-image: url(chrome://browser/skin/notification-pluginNormal.png); } - -#alert-plugins-notification-icon { +#plugins-notification-icon.plugin-hidden { list-style-image: url(chrome://browser/skin/notification-pluginAlert.png); } -#blocked-plugins-notification-icon { +#plugins-notification-icon.plugin-blocked { list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png); } -#plugins-notification-icon, -#alert-plugins-notification-icon, -#blocked-plugins-notification-icon { +#plugins-notification-icon { -moz-image-region: rect(0, 16px, 16px, 0); } -#plugins-notification-icon:hover, -#alert-plugins-notification-icon:hover, -#blocked-plugins-notification-icon:hover { +#plugins-notification-icon:hover { -moz-image-region: rect(0, 32px, 16px, 16px); } -#plugins-notification-icon:active, -#alert-plugins-notification-icon:active, -#blocked-plugins-notification-icon:active { +#plugins-notification-icon:active { -moz-image-region: rect(0, 48px, 16px, 32px); } @@ -2606,7 +2599,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { visibility: collapse; } -#blocked-plugins-notification-icon[showing] { +#plugins-notification-icon.plugin-blocked[showing] { animation: pluginBlockedNotification 500ms ease 0s 5 alternate both; } diff --git a/browser/themes/windows/devtools/debugger.css b/browser/themes/windows/devtools/debugger.css index 439cc46ab15..7109a860393 100644 --- a/browser/themes/windows/devtools/debugger.css +++ b/browser/themes/windows/devtools/debugger.css @@ -24,8 +24,11 @@ -moz-border-end: 1px solid #222426; /* Match the sources list's dark margin. */ } +#sources-toolbar .devtools-toolbarbutton { + min-width: 32px; +} + #pretty-print { - min-width: 0; font-weight: bold; } diff --git a/browser/themes/windows/devtools/widgets.css b/browser/themes/windows/devtools/widgets.css index 48e8e8babe9..ebb1c852e81 100644 --- a/browser/themes/windows/devtools/widgets.css +++ b/browser/themes/windows/devtools/widgets.css @@ -321,11 +321,6 @@ background-image: linear-gradient(#fff, #eee); } -.side-menu-widget-group-checkbox { - margin: 0; - padding: 0; -} - /* SideMenuWidget items */ .side-menu-widget-item[theme="dark"] { @@ -383,11 +378,6 @@ width: 8px; } -.side-menu-widget-item-checkbox { - -moz-margin-start: 4px; - -moz-margin-end: -6px; -} - .side-menu-widget-item-other { background: url(background-noise-toolbar.png), hsla(208,11%,27%, 0.65); } @@ -406,6 +396,19 @@ color: #f5f7fa; } +/* SideMenuWidget checkboxes */ + +.side-menu-widget-group-checkbox { + margin: 0; + -moz-margin-end: 4px; +} + +.side-menu-widget-item-checkbox { + margin: 0; + -moz-margin-start: 4px; + -moz-margin-end: -4px; +} + /* SideMenuWidget misc */ .side-menu-widget-empty-notice-container { diff --git a/caps/src/nsJSPrincipals.cpp b/caps/src/nsJSPrincipals.cpp index 473f7f3c8c8..b981d1d91d3 100644 --- a/caps/src/nsJSPrincipals.cpp +++ b/caps/src/nsJSPrincipals.cpp @@ -11,7 +11,6 @@ #include "plstr.h" #include "nsXPIDLString.h" #include "nsCOMPtr.h" -#include "jsapi.h" #include "nsIJSRuntimeService.h" #include "nsIServiceManager.h" #include "nsMemory.h" diff --git a/content/base/src/Element.cpp b/content/base/src/Element.cpp index 535c6014f2c..cd951acad11 100644 --- a/content/base/src/Element.cpp +++ b/content/base/src/Element.cpp @@ -84,8 +84,6 @@ #include "nsIBaseWindow.h" #include "nsIWidget.h" -#include "jsapi.h" - #include "nsNodeInfoManager.h" #include "nsICategoryManager.h" #include "nsIDOMDocumentType.h" diff --git a/content/base/src/FragmentOrElement.cpp b/content/base/src/FragmentOrElement.cpp index 1410633ce7d..5530891fa00 100644 --- a/content/base/src/FragmentOrElement.cpp +++ b/content/base/src/FragmentOrElement.cpp @@ -79,7 +79,7 @@ #include "nsIBaseWindow.h" #include "nsIWidget.h" -#include "jsapi.h" +#include "js/GCAPI.h" #include "nsNodeInfoManager.h" #include "nsICategoryManager.h" diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index ffd9331225f..5ec61d64cda 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -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 diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp index a6ee7a83a70..35c50594a54 100644 --- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -88,6 +88,7 @@ #include "mozilla/dom/HTMLVideoElement.h" #include "mozilla/dom/TextMetrics.h" #include "mozilla/dom/UnionTypes.h" +#include "nsGlobalWindow.h" #ifdef USE_SKIA_GPU #undef free // apparently defined by some windows header, clashing with a free() @@ -2858,27 +2859,27 @@ CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset) } void -CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence& mSegments) { +CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence& aSegments) { FallibleTArray& dash = CurrentState().dash; dash.Clear(); - for(mozilla::dom::AutoSequence::index_type x = 0; x < mSegments.Length(); x++) { - dash.AppendElement(mSegments[x]); + for (uint32_t x = 0; x < aSegments.Length(); x++) { + dash.AppendElement(aSegments[x]); } - if(mSegments.Length()%2) { // If the number of elements is odd, concatenate again - for(mozilla::dom::AutoSequence::index_type x = 0; x < mSegments.Length(); x++) { - dash.AppendElement(mSegments[x]); + if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again + for (uint32_t x = 0; x < aSegments.Length(); x++) { + dash.AppendElement(aSegments[x]); } } } void -CanvasRenderingContext2D::GetLineDash(nsTArray& mSegments) const { +CanvasRenderingContext2D::GetLineDash(nsTArray& aSegments) const { const FallibleTArray& dash = CurrentState().dash; - mSegments.Clear(); + aSegments.Clear(); - for(FallibleTArray::index_type x = 0; x < dash.Length(); x++) { - mSegments.AppendElement(dash[x]); + for (uint32_t x = 0; x < dash.Length(); x++) { + aSegments.AppendElement(dash[x]); } } @@ -3224,7 +3225,7 @@ CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString& op, } void -CanvasRenderingContext2D::DrawWindow(nsIDOMWindow* window, double x, +CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x, double y, double w, double h, const nsAString& bgColor, uint32_t flags, ErrorResult& error) @@ -3253,16 +3254,13 @@ CanvasRenderingContext2D::DrawWindow(nsIDOMWindow* window, double x, // Flush layout updates if (!(flags & nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH)) { - nsContentUtils::FlushLayoutForTree(window); + nsContentUtils::FlushLayoutForTree(&window); } nsRefPtr presContext; - nsCOMPtr win = do_QueryInterface(window); - if (win) { - nsIDocShell* docshell = win->GetDocShell(); - if (docshell) { - docshell->GetPresContext(getter_AddRefs(presContext)); - } + nsIDocShell* docshell = window.GetDocShell(); + if (docshell) { + docshell->GetPresContext(getter_AddRefs(presContext)); } if (!presContext) { error.Throw(NS_ERROR_FAILURE); diff --git a/content/canvas/src/CanvasRenderingContext2D.h b/content/canvas/src/CanvasRenderingContext2D.h index 41cbb9bc8b9..91c1770d845 100644 --- a/content/canvas/src/CanvasRenderingContext2D.h +++ b/content/canvas/src/CanvasRenderingContext2D.h @@ -24,6 +24,7 @@ #include "gfx2DGlue.h" #include "imgIEncoder.h" +class nsGlobalWindow; class nsXULElement; namespace mozilla { @@ -369,7 +370,7 @@ public: } } - void DrawWindow(nsIDOMWindow* window, double x, double y, double w, double h, + void DrawWindow(nsGlobalWindow& window, double x, double y, double w, double h, const nsAString& bgColor, uint32_t flags, mozilla::ErrorResult& error); void AsyncDrawXULElement(nsXULElement& elem, double x, double y, double w, diff --git a/content/html/content/src/HTMLScriptElement.cpp b/content/html/content/src/HTMLScriptElement.cpp index 1794d7b36ea..236e32c4ce0 100644 --- a/content/html/content/src/HTMLScriptElement.cpp +++ b/content/html/content/src/HTMLScriptElement.cpp @@ -11,7 +11,6 @@ #include "nsNetUtil.h" #include "nsContentUtils.h" #include "nsUnicharUtils.h" // for nsCaseInsensitiveStringComparator() -#include "jsapi.h" #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIXPConnect.h" diff --git a/content/html/content/src/HTMLVideoElement.cpp b/content/html/content/src/HTMLVideoElement.cpp index bbc2bee036d..e447eeb6091 100644 --- a/content/html/content/src/HTMLVideoElement.cpp +++ b/content/html/content/src/HTMLVideoElement.cpp @@ -24,7 +24,6 @@ #include "nsIScriptSecurityManager.h" #include "nsIXPConnect.h" -#include "jsapi.h" #include "nsITimer.h" diff --git a/content/media/encoder/ContainerWriter.h b/content/media/encoder/ContainerWriter.h index 9d07d7cb053..d08d2cdbf35 100644 --- a/content/media/encoder/ContainerWriter.h +++ b/content/media/encoder/ContainerWriter.h @@ -8,6 +8,8 @@ #include "nsTArray.h" #include "nsAutoPtr.h" +#include "EncodedFrameContainer.h" +#include "TrackMetadataBase.h" namespace mozilla { /** @@ -31,11 +33,20 @@ public: * END_OF_STREAM if this is the last packet of track. * Currently, WriteEncodedTrack doesn't support multiple tracks. */ - virtual nsresult WriteEncodedTrack(const nsTArray& aBuffer, - int aDuration, uint32_t aFlags = 0) = 0; + virtual nsresult WriteEncodedTrack(const EncodedFrameContainer& aData, + uint32_t aFlags = 0) = 0; + + /** + * Set the meta data pointer into muxer + * This function will check the integrity of aMetadata. + * If the meta data isn't well format, this function will return NS_ERROR_FAILURE to caller, + * else save the pointer to mMetadata and return NS_OK. + */ + virtual nsresult SetMetadata(nsRefPtr aMetadata) = 0; enum { - FLUSH_NEEDED = 1 << 0 + FLUSH_NEEDED = 1 << 0, + GET_HEADER = 1 << 1 }; /** @@ -50,6 +61,7 @@ public: uint32_t aFlags = 0) = 0; protected: + nsRefPtr mMetadata; bool mInitialized; }; } diff --git a/content/media/encoder/EncodedFrameContainer.h b/content/media/encoder/EncodedFrameContainer.h new file mode 100644 index 00000000000..fd6d09b5cf3 --- /dev/null +++ b/content/media/encoder/EncodedFrameContainer.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* 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/. */ + +#ifndef EncodedFrameContainer_H_ +#define EncodedFrameContainer_H_ + +#include "nsTArray.h" + +namespace mozilla { + +class EncodedFrame; + +/* + * This container is used to carry video or audio encoded data from encoder to muxer. + * The media data object is created by encoder and recycle by the destructor. + * Only allow to store audio or video encoded data in EncodedData. + */ +class EncodedFrameContainer +{ +public: + // Append encoded frame data + void AppendEncodedFrame(EncodedFrame* aEncodedFrame) + { + mEncodedFrames.AppendElement(aEncodedFrame); + } + // Retrieve all of the encoded frames + const nsTArray >& GetEncodedFrames() const + { + return mEncodedFrames; + } +private: + // This container is used to store the video or audio encoded packets. + // Muxer should check mFrameType and get the encoded data type from mEncodedFrames. + nsTArray > mEncodedFrames; +}; + +// Represent one encoded frame +class EncodedFrame +{ +public: + EncodedFrame() : + mTimeStamp(0), + mDuration(0), + mFrameType(UNKNOW) + {} + enum FrameType { + I_FRAME, // intraframe + P_FRAME, // predicted frame + B_FRAME, // bidirectionally predicted frame + AUDIO_FRAME, // audio frame + UNKNOW // FrameType not set + }; + const nsTArray& GetFrameData() const + { + return mFrameData; + } + void SetFrameData(nsTArray *aData) + { + mFrameData.SwapElements(*aData); + } + uint64_t GetTimeStamp() const { return mTimeStamp; } + void SetTimeStamp(uint64_t aTimeStamp) { mTimeStamp = aTimeStamp; } + + uint64_t GetDuration() const { return mDuration; } + void SetDuration(uint64_t aDuration) { mDuration = aDuration; } + + FrameType GetFrameType() const { return mFrameType; } + void SetFrameType(FrameType aFrameType) { mFrameType = aFrameType; } +private: + // Encoded data + nsTArray mFrameData; + uint64_t mTimeStamp; + // The playback duration of this packet in number of samples + uint64_t mDuration; + // Represent what is in the FrameData + FrameType mFrameType; +}; + +} +#endif diff --git a/content/media/encoder/MediaEncoder.cpp b/content/media/encoder/MediaEncoder.cpp index e9f17fd0972..42536f4642c 100644 --- a/content/media/encoder/MediaEncoder.cpp +++ b/content/media/encoder/MediaEncoder.cpp @@ -5,6 +5,7 @@ #include "MediaEncoder.h" #include "MediaDecoder.h" #include "nsIPrincipal.h" + #ifdef MOZ_OGG #include "OggWriter.h" #endif @@ -128,17 +129,15 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType) /** * GetEncodedData() runs as a state machine, starting with mState set to - * ENCODE_HEADER, the procedure should be as follow: + * GET_METADDATA, the procedure should be as follow: * * While non-stop - * If mState is ENCODE_HEADER - * Create the header from audio/video encoder - * If a header is generated - * Insert header data into the container stream of writer - * Force copied the final container data from writer - * Return the copy of final container data - * Else + * If mState is GET_METADDATA + * Get the meta data from audio/video encoder + * If a meta data is generated + * Get meta data from audio/video encoder * Set mState to ENCODE_TRACK + * Return the final container data * * If mState is ENCODE_TRACK * Get encoded track data from audio/video encoder @@ -163,48 +162,36 @@ MediaEncoder::GetEncodedData(nsTArray >* aOutputBufs, bool reloop = true; while (reloop) { switch (mState) { - case ENCODE_HEADER: { - nsTArray buffer; - nsresult rv = mAudioEncoder->GetHeader(&buffer); + case ENCODE_METADDATA: { + nsRefPtr meta = mAudioEncoder->GetMetadata(); + MOZ_ASSERT(meta); + nsresult rv = mWriter->SetMetadata(meta); if (NS_FAILED(rv)) { - // Encoding might be canceled. - mState = ENCODE_DONE; - break; + mState = ENCODE_DONE; + break; } - if (!buffer.IsEmpty()) { - rv = mWriter->WriteEncodedTrack(buffer, 0); - if (NS_FAILED(rv)) { - LOG("ERROR! Fail to write header to the media container."); - mState = ENCODE_DONE; - break; - } - - rv = mWriter->GetContainerData(aOutputBufs, - ContainerWriter::FLUSH_NEEDED); - if (NS_SUCCEEDED(rv)) { - // Successfully get the copy of final container data from writer. - reloop = false; - } - } else { - // No more headers, starts to encode tracks. - mState = ENCODE_TRACK; + rv = mWriter->GetContainerData(aOutputBufs, + ContainerWriter::GET_HEADER); + if (NS_FAILED(rv)) { + mState = ENCODE_DONE; + break; } + + mState = ENCODE_TRACK; break; } case ENCODE_TRACK: { - nsTArray buffer; - int encodedDuration = 0; - nsresult rv = mAudioEncoder->GetEncodedTrack(&buffer, encodedDuration); + EncodedFrameContainer encodedData; + nsresult rv = mAudioEncoder->GetEncodedTrack(encodedData); if (NS_FAILED(rv)) { // Encoding might be canceled. LOG("ERROR! Fail to get encoded data from encoder."); mState = ENCODE_DONE; break; } - - rv = mWriter->WriteEncodedTrack(buffer, encodedDuration, + rv = mWriter->WriteEncodedTrack(encodedData, mAudioEncoder->IsEncodingComplete() ? ContainerWriter::END_OF_STREAM : 0); if (NS_FAILED(rv)) { diff --git a/content/media/encoder/MediaEncoder.h b/content/media/encoder/MediaEncoder.h index f4617b35263..04016ae27e2 100644 --- a/content/media/encoder/MediaEncoder.h +++ b/content/media/encoder/MediaEncoder.h @@ -51,7 +51,7 @@ class MediaEncoder : public MediaStreamListener { public : enum { - ENCODE_HEADER, + ENCODE_METADDATA, ENCODE_TRACK, ENCODE_DONE, }; @@ -64,7 +64,7 @@ public : , mAudioEncoder(aAudioEncoder) , mVideoEncoder(aVideoEncoder) , mMIMEType(aMIMEType) - , mState(MediaEncoder::ENCODE_HEADER) + , mState(MediaEncoder::ENCODE_METADDATA) , mShutdown(false) {} diff --git a/content/media/encoder/OpusTrackEncoder.cpp b/content/media/encoder/OpusTrackEncoder.cpp index 2ddf4525761..f07632be834 100644 --- a/content/media/encoder/OpusTrackEncoder.cpp +++ b/content/media/encoder/OpusTrackEncoder.cpp @@ -115,7 +115,6 @@ SerializeOpusCommentHeader(const nsCString& aVendor, OpusTrackEncoder::OpusTrackEncoder() : AudioTrackEncoder() - , mEncoderState(ID_HEADER) , mEncoder(nullptr) , mSourceSegment(new AudioSegment()) , mLookahead(0) @@ -187,8 +186,8 @@ OpusTrackEncoder::GetPacketDuration() return GetOutputSampleRate() * kFrameDurationMs / 1000; } -nsresult -OpusTrackEncoder::GetHeader(nsTArray* aOutput) +nsRefPtr +OpusTrackEncoder::GetMetadata() { { // Wait if mEncoder is not initialized. @@ -199,50 +198,35 @@ OpusTrackEncoder::GetHeader(nsTArray* aOutput) } if (mCanceled || mDoneEncoding) { - return NS_ERROR_FAILURE; + return nullptr; } - switch (mEncoderState) { - case ID_HEADER: - { + OpusMetadata* meta = new OpusMetadata(); + + mLookahead = 0; + int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead)); + if (error != OPUS_OK) { mLookahead = 0; - int error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead)); - if (error != OPUS_OK) { - mLookahead = 0; - } - - // The ogg time stamping and pre-skip is always timed at 48000. - SerializeOpusIdHeader(mChannels, mLookahead*(kOpusSamplingRate/mSamplingRate), - mSamplingRate, aOutput); - - mEncoderState = COMMENT_HEADER; - break; } - case COMMENT_HEADER: - { - nsCString vendor; - vendor.AppendASCII(opus_get_version_string()); - nsTArray comments; - comments.AppendElement(NS_LITERAL_CSTRING("ENCODER=Mozilla" MOZ_APP_UA_VERSION)); + // The ogg time stamping and pre-skip is always timed at 48000. + SerializeOpusIdHeader(mChannels, mLookahead*(kOpusSamplingRate/mSamplingRate), + mSamplingRate, &meta->mIdHeader); - SerializeOpusCommentHeader(vendor, comments, aOutput); + nsCString vendor; + vendor.AppendASCII(opus_get_version_string()); - mEncoderState = DATA; - break; - } - case DATA: - // No more headers. - break; - default: - MOZ_CRASH("Invalid state"); - } - return NS_OK; + nsTArray comments; + comments.AppendElement(NS_LITERAL_CSTRING("ENCODER=Mozilla" MOZ_APP_UA_VERSION)); + + SerializeOpusCommentHeader(vendor, comments, + &meta->mCommentHeader); + + return meta; } nsresult -OpusTrackEncoder::GetEncodedTrack(nsTArray* aOutput, - int &aOutputDuration) +OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) { { // Move all the samples from mRawSegment to mSourceSegment. We only hold @@ -297,6 +281,8 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray* aOutput, iter.Next(); } + EncodedFrame* audiodata = new EncodedFrame(); + audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME); if (mResampler) { nsAutoTArray resamplingDest; // We want to consume all the input data, so we slightly oversize the @@ -321,10 +307,10 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray* aOutput, pcm = resamplingDest; // This is always at 48000Hz. - aOutputDuration = outframes; + audiodata->SetDuration(outframes); } else { // The ogg time stamping and pre-skip is always timed at 48000. - aOutputDuration = frameCopied * (kOpusSamplingRate / mSamplingRate); + audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate)); } // Remove the raw data which has been pulled to pcm buffer. @@ -348,26 +334,28 @@ OpusTrackEncoder::GetEncodedTrack(nsTArray* aOutput, memset(pcm.Elements() + frameCopied * mChannels, 0, (GetPacketDuration()-frameCopied)*mChannels*sizeof(AudioDataValue)); } - + nsTArray frameData; // Encode the data with Opus Encoder. - aOutput->SetLength(MAX_DATA_BYTES); + frameData.SetLength(MAX_DATA_BYTES); // result is returned as opus error code if it is negative. int result = 0; #ifdef MOZ_SAMPLE_TYPE_S16 const opus_int16* pcmBuf = static_cast(pcm.Elements()); result = opus_encode(mEncoder, pcmBuf, GetPacketDuration(), - aOutput->Elements(), MAX_DATA_BYTES); + frameData.Elements(), MAX_DATA_BYTES); #else const float* pcmBuf = static_cast(pcm.Elements()); result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(), - aOutput->Elements(), MAX_DATA_BYTES); + frameData.Elements(), MAX_DATA_BYTES); #endif - aOutput->SetLength(result >= 0 ? result : 0); + frameData.SetLength(result >= 0 ? result : 0); if (result < 0) { LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result)); } + audiodata->SetFrameData(&frameData); + aData.AppendEncodedFrame(audiodata); return result >= 0 ? NS_OK : NS_ERROR_FAILURE; } diff --git a/content/media/encoder/OpusTrackEncoder.h b/content/media/encoder/OpusTrackEncoder.h index 39c305d4540..1a4c4c054bc 100644 --- a/content/media/encoder/OpusTrackEncoder.h +++ b/content/media/encoder/OpusTrackEncoder.h @@ -9,21 +9,32 @@ #include #include #include "TrackEncoder.h" -#include "nsCOMPtr.h" struct OpusEncoder; namespace mozilla { +// Opus meta data structure +class OpusMetadata : public TrackMetadataBase +{ +public: + // The ID Header of OggOpus. refer to http://wiki.xiph.org/OggOpus. + nsTArray mIdHeader; + // The Comment Header of OggOpus. + nsTArray mCommentHeader; + + MetadataKind GetKind() const MOZ_OVERRIDE { return METADATA_OPUS; } +}; + class OpusTrackEncoder : public AudioTrackEncoder { public: OpusTrackEncoder(); virtual ~OpusTrackEncoder(); - nsresult GetHeader(nsTArray* aOutput) MOZ_OVERRIDE; + nsRefPtr GetMetadata() MOZ_OVERRIDE; - nsresult GetEncodedTrack(nsTArray* aOutput, int &aOutputDuration) MOZ_OVERRIDE; + nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE; protected: int GetPacketDuration() MOZ_OVERRIDE; @@ -31,12 +42,6 @@ protected: nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE; private: - enum { - ID_HEADER, - COMMENT_HEADER, - DATA - } mEncoderState; - /** * Get the samplerate of the data to be fed to the Opus encoder. This might be * different from the intput samplerate if resampling occurs. diff --git a/content/media/encoder/TrackEncoder.h b/content/media/encoder/TrackEncoder.h index c3cabcd1dc8..a1da5faa5d1 100644 --- a/content/media/encoder/TrackEncoder.h +++ b/content/media/encoder/TrackEncoder.h @@ -10,6 +10,8 @@ #include "AudioSegment.h" #include "StreamBuffer.h" +#include "TrackMetadataBase.h" +#include "EncodedFrameContainer.h" namespace mozilla { @@ -48,17 +50,14 @@ public: virtual void NotifyRemoved(MediaStreamGraph* aGraph) = 0; /** - * Creates and sets up header for a specific codec. Result data is returned - * in aOutput. + * Creates and sets up meta data for a specific codec */ - virtual nsresult GetHeader(nsTArray* aOutput) = 0; + virtual nsRefPtr GetMetadata() = 0; /** - * Encodes raw segments. Result data is returned in aOutput. aOutputDuration - * is the playback duration of this packet in number of samples. + * Encodes raw segments. Result data is returned in aData. */ - virtual nsresult GetEncodedTrack(nsTArray* aOutput, - int &aOutputDuration) = 0; + virtual nsresult GetEncodedTrack(EncodedFrameContainer& aData) = 0; }; class AudioTrackEncoder : public TrackEncoder diff --git a/content/media/encoder/TrackMetadataBase.h b/content/media/encoder/TrackMetadataBase.h new file mode 100644 index 00000000000..6faadcecd6a --- /dev/null +++ b/content/media/encoder/TrackMetadataBase.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* 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/. */ + +#ifndef TrackMetadataBase_h_ +#define TrackMetadataBase_h_ + +#include "nsTArray.h" +#include "nsCOMPtr.h" +namespace mozilla { + +// A class represent meta data for various codec format. Only support one track information. +class TrackMetadataBase +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackMetadataBase) + enum MetadataKind { + METADATA_OPUS, // Represent the Opus metadata + METADATA_VP8, + METADATA_UNKNOW // Metadata Kind not set + }; + virtual ~TrackMetadataBase() {} + // Return the specific metadata kind + virtual MetadataKind GetKind() const = 0; +}; + +} +#endif diff --git a/content/media/encoder/moz.build b/content/media/encoder/moz.build index 42cc5dbbd48..29b357ad1d0 100644 --- a/content/media/encoder/moz.build +++ b/content/media/encoder/moz.build @@ -8,8 +8,10 @@ MODULE = 'content' EXPORTS += [ 'ContainerWriter.h', + 'EncodedFrameContainer.h', 'MediaEncoder.h', 'TrackEncoder.h', + 'TrackMetadataBase.h', ] SOURCES += [ diff --git a/content/media/ogg/OggWriter.cpp b/content/media/ogg/OggWriter.cpp index 7b2698773bc..c84851a191c 100644 --- a/content/media/ogg/OggWriter.cpp +++ b/content/media/ogg/OggWriter.cpp @@ -4,6 +4,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "OggWriter.h" #include "prtime.h" +#include "OpusTrackEncoder.h" #undef LOG #ifdef MOZ_WIDGET_GONK @@ -46,8 +47,29 @@ OggWriter::Init() } nsresult -OggWriter::WriteEncodedTrack(const nsTArray& aBuffer, int aDuration, +OggWriter::WriteEncodedTrack(const EncodedFrameContainer& aData, uint32_t aFlags) +{ + for (uint32_t i = 0; i < aData.GetEncodedFrames().Length(); i++) { + if (aData.GetEncodedFrames()[i]->GetFrameType() != EncodedFrame::AUDIO_FRAME) { + LOG("[OggWriter] wrong encoded data type!"); + return NS_ERROR_FAILURE; + } + + nsresult rv = WriteEncodedData(aData.GetEncodedFrames()[i]->GetFrameData(), + aData.GetEncodedFrames()[i]->GetDuration(), + aFlags); + if (NS_FAILED(rv)) { + LOG("%p Failed to WriteEncodedTrack!", this); + return rv; + } + } + return NS_OK; +} + +nsresult +OggWriter::WriteEncodedData(const nsTArray& aBuffer, int aDuration, + uint32_t aFlags) { if (!mInitialized) { LOG("[OggWriter] OggWriter has not initialized!"); @@ -87,14 +109,49 @@ OggWriter::WriteEncodedTrack(const nsTArray& aBuffer, int aDuration, return NS_OK; } +void +OggWriter::ProduceOggPage(nsTArray >* aOutputBufs) +{ + aOutputBufs->AppendElement(); + aOutputBufs->LastElement().SetLength(mOggPage.header_len + + mOggPage.body_len); + memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header, + mOggPage.header_len); + memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len, + mOggPage.body, mOggPage.body_len); +} + nsresult OggWriter::GetContainerData(nsTArray >* aOutputBufs, uint32_t aFlags) { int rc = -1; + // Generate the oggOpus Header + if (aFlags & ContainerWriter::GET_HEADER) { + OpusMetadata* meta = static_cast(mMetadata.get()); + NS_ASSERTION(meta, "should have meta data"); + NS_ASSERTION(meta->GetKind() == TrackMetadataBase::METADATA_OPUS, + "should have Opus meta data"); + + nsresult rv = WriteEncodedData(meta->mIdHeader, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rc = ogg_stream_flush(&mOggStreamState, &mOggPage); + NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE); + ProduceOggPage(aOutputBufs); + + rv = WriteEncodedData(meta->mCommentHeader, 0); + NS_ENSURE_SUCCESS(rv, rv); + + rc = ogg_stream_flush(&mOggStreamState, &mOggPage); + NS_ENSURE_TRUE(rc > 0, NS_ERROR_FAILURE); + + ProduceOggPage(aOutputBufs); + return NS_OK; + // Force generate a page even if the amount of packet data is not enough. // Usually do so after a header packet. - if (aFlags & ContainerWriter::FLUSH_NEEDED) { + } else if (aFlags & ContainerWriter::FLUSH_NEEDED) { // rc = 0 means no packet to put into a page, or an internal error. rc = ogg_stream_flush(&mOggStreamState, &mOggPage); } else { @@ -104,16 +161,32 @@ OggWriter::GetContainerData(nsTArray >* aOutputBufs, } if (rc) { - aOutputBufs->AppendElement(); - aOutputBufs->LastElement().SetLength(mOggPage.header_len + - mOggPage.body_len); - memcpy(aOutputBufs->LastElement().Elements(), mOggPage.header, - mOggPage.header_len); - memcpy(aOutputBufs->LastElement().Elements() + mOggPage.header_len, - mOggPage.body, mOggPage.body_len); + ProduceOggPage(aOutputBufs); } return (rc > 0) ? NS_OK : NS_ERROR_FAILURE; } +nsresult +OggWriter::SetMetadata(nsRefPtr aMetadata) +{ + if (aMetadata->GetKind() != TrackMetadataBase::METADATA_OPUS) { + LOG("wrong meta data type!"); + return NS_ERROR_FAILURE; + } + // Validate each field of METADATA + OpusMetadata* meta = static_cast(aMetadata.get()); + if (meta->mIdHeader.Length() == 0) { + LOG("miss mIdHeader!"); + return NS_ERROR_FAILURE; + } + if (meta->mCommentHeader.Length() == 0) { + LOG("miss mCommentHeader!"); + return NS_ERROR_FAILURE; + } + + mMetadata = aMetadata; + return NS_OK; +} + } diff --git a/content/media/ogg/OggWriter.h b/content/media/ogg/OggWriter.h index dd19e968e69..85458440da8 100644 --- a/content/media/ogg/OggWriter.h +++ b/content/media/ogg/OggWriter.h @@ -22,15 +22,23 @@ class OggWriter : public ContainerWriter public: OggWriter(); - nsresult WriteEncodedTrack(const nsTArray& aBuffer, int aDuration, + nsresult WriteEncodedTrack(const EncodedFrameContainer &aData, uint32_t aFlags = 0) MOZ_OVERRIDE; nsresult GetContainerData(nsTArray >* aOutputBufs, uint32_t aFlags = 0) MOZ_OVERRIDE; + // Check metadata type integrity and reject unacceptable track encoder. + nsresult SetMetadata(nsRefPtr aMetadata) MOZ_OVERRIDE; + private: nsresult Init(); + nsresult WriteEncodedData(const nsTArray& aBuffer, int aDuration, + uint32_t aFlags = 0); + + void ProduceOggPage(nsTArray >* aOutputBufs); + ogg_stream_state mOggStreamState; ogg_page mOggPage; ogg_packet mPacket; diff --git a/content/xbl/src/nsBindingManager.cpp b/content/xbl/src/nsBindingManager.cpp index 4dc4eb23486..da99a64e2cf 100644 --- a/content/xbl/src/nsBindingManager.cpp +++ b/content/xbl/src/nsBindingManager.cpp @@ -33,7 +33,6 @@ #include "nsRuleProcessorData.h" #include "nsIWeakReference.h" -#include "jsapi.h" #include "nsIXPConnect.h" #include "nsDOMCID.h" #include "nsIDOMScriptObjectFactory.h" diff --git a/content/xul/document/src/nsXULContentSink.cpp b/content/xul/document/src/nsXULContentSink.cpp index e6e33f1abeb..83fe0dccb55 100644 --- a/content/xul/document/src/nsXULContentSink.cpp +++ b/content/xul/document/src/nsXULContentSink.cpp @@ -14,7 +14,6 @@ #include "nsXULContentSink.h" -#include "jsapi.h" #include "jsfriendapi.h" #include "nsCOMPtr.h" diff --git a/content/xul/document/src/nsXULPrototypeCache.cpp b/content/xul/document/src/nsXULPrototypeCache.cpp index 20724f1a69c..bbf8646965a 100644 --- a/content/xul/document/src/nsXULPrototypeCache.cpp +++ b/content/xul/document/src/nsXULPrototypeCache.cpp @@ -22,7 +22,6 @@ #include "nsNetUtil.h" #include "nsAppDirectoryServiceDefs.h" -#include "jsapi.h" #include "js/Tracer.h" #include "mozilla/Preferences.h" diff --git a/content/xul/templates/src/nsXULContentBuilder.cpp b/content/xul/templates/src/nsXULContentBuilder.cpp index 1210df10abc..22af8a804dd 100644 --- a/content/xul/templates/src/nsXULContentBuilder.cpp +++ b/content/xul/templates/src/nsXULContentBuilder.cpp @@ -33,7 +33,6 @@ #include "mozAutoDocUpdate.h" #include "nsTextNode.h" -#include "jsapi.h" #include "pldhash.h" #include "rdf.h" diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index d48c83a80f7..180ca9cd555 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -8751,7 +8751,7 @@ nsDocShell::InternalLoad(nsIURI * aURI, nsISupports* context = requestingElement; if (!context) { - context = nsGlobalWindow::ToSupports(mScriptGlobal); + context = ToSupports(mScriptGlobal); } // XXXbz would be nice to know the loading principal here... but we don't diff --git a/dom/apps/src/PermissionsTable.jsm b/dom/apps/src/PermissionsTable.jsm index 0ffd5327a38..db98999ebb8 100644 --- a/dom/apps/src/PermissionsTable.jsm +++ b/dom/apps/src/PermissionsTable.jsm @@ -302,6 +302,11 @@ this.PermissionsTable = { geolocation: { privileged: PROMPT_ACTION, certified: PROMPT_ACTION }, + "video-capture": { + app: PROMPT_ACTION, + privileged: PROMPT_ACTION, + certified: PROMPT_ACTION + }, }; /** diff --git a/dom/apps/tests/chrome.ini b/dom/apps/tests/chrome.ini index b0638b1dd77..dd0ca60d379 100644 --- a/dom/apps/tests/chrome.ini +++ b/dom/apps/tests/chrome.ini @@ -3,3 +3,5 @@ [test_apps_service.xul] [test_operator_app_install.js] [test_operator_app_install.xul] +# bug 928262 + skip-if = os == "win" diff --git a/dom/base/DOMException.cpp b/dom/base/DOMException.cpp index 0d2f02b9e9d..af705fa47f7 100644 --- a/dom/base/DOMException.cpp +++ b/dom/base/DOMException.cpp @@ -5,7 +5,6 @@ #include "mozilla/dom/DOMException.h" -#include "jsapi.h" #include "jsprf.h" #include "js/OldDebugAPI.h" #include "mozilla/HoldDropJSObjects.h" diff --git a/dom/base/WindowNamedPropertiesHandler.cpp b/dom/base/WindowNamedPropertiesHandler.cpp index ad7d0da5806..3b24f9d3c5a 100644 --- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -91,7 +91,7 @@ WindowNamedPropertiesHandler::getOwnPropertyDescriptor(JSContext* aCx, nsCOMPtr piWin = do_QueryWrappedNative(wrapper); MOZ_ASSERT(piWin); nsGlobalWindow* win = static_cast(piWin.get()); - if (win->GetLength() > 0) { + if (win->Length() > 0) { nsCOMPtr childWin = win->GetChildWindow(str); if (childWin && ShouldExposeChildWindow(str, childWin)) { // We found a subframe of the right name. Shadowing via |var foo| in diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp index b4b3c87dca5..50510512970 100644 --- a/dom/base/nsContentPermissionHelper.cpp +++ b/dom/base/nsContentPermissionHelper.cpp @@ -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; // using namespace mozilla::dom; +namespace mozilla { +namespace dom { + +class ContentPermissionRequestParent : public PContentPermissionRequestParent +{ + public: + ContentPermissionRequestParent(const nsTArray& aRequests, + Element* element, + const IPC::Principal& principal); + virtual ~ContentPermissionRequestParent(); + + bool IsBeingDestroyed(); + + nsCOMPtr mPrincipal; + nsCOMPtr mElement; + nsCOMPtr mProxy; + nsTArray mRequests; + + private: + virtual bool Recvprompt(); + virtual void ActorDestroy(ActorDestroyReason why); +}; + +ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray& 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(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& aSrcArray, + nsIMutableArray* aDesArray) +{ + uint32_t len = aSrcArray.Length(); + for (uint32_t i = 0; i < len; i++) { + nsRefPtr 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 types = do_CreateInstance(NS_ARRAY_CONTRACTID); + nsRefPtr permType = new ContentPermissionType(aType, + aAccess); + types->AppendElement(permType, false); + types.forget(aTypesArray); + + return NS_OK; +} + +PContentPermissionRequestParent* +CreateContentPermissionRequestParent(const nsTArray& 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& requests, ContentPermissionRequestParent* parent) { NS_ASSERTION(parent, "null parent"); mParent = parent; - mType = type; - mAccess = access; + mPermissionRequests = requests; nsCOMPtr 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 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(Manager()); - return tabParent->IsDestroyed(); -} - -} // namespace dom -} // namespace mozilla diff --git a/dom/base/nsContentPermissionHelper.h b/dom/base/nsContentPermissionHelper.h index 9a750c6efb5..1baf2768b92 100644 --- a/dom/base/nsContentPermissionHelper.h +++ b/dom/base/nsContentPermissionHelper.h @@ -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 mPrincipal; - nsCOMPtr mElement; - nsCOMPtr mProxy; +protected: nsCString mType; nsCString mAccess; - - private: - virtual bool Recvprompt(); - virtual void ActorDestroy(ActorDestroyReason why); }; +uint32_t ConvertPermissionRequestToArray(nsTArray& aSrcArray, + nsIMutableArray* aDesArray); + +nsresult CreatePermissionArray(const nsACString& aType, + const nsACString& aAccess, + nsIArray** aTypesArray); + +PContentPermissionRequestParent* +CreateContentPermissionRequestParent(const nsTArray& 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& 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 mPermissionRequests; }; -#endif // nsContentPermissionHelper_h +#endif // nsContentPermissionHelper_h diff --git a/dom/base/nsDOMClassInfoID.h b/dom/base/nsDOMClassInfoID.h index 7679fc5cc7a..917c95e1800 100644 --- a/dom/base/nsDOMClassInfoID.h +++ b/dom/base/nsDOMClassInfoID.h @@ -63,7 +63,8 @@ DOMCI_CASTABLE_INTERFACE(nsStyledElement, nsStyledElement, 8, _extra) \ DOMCI_CASTABLE_INTERFACE(nsSVGElement, nsIContent, 9, _extra) \ /* NOTE: When removing the casts below, remove the nsDOMEventBase class */ \ DOMCI_CASTABLE_INTERFACE(nsDOMMouseEvent, nsDOMEventBase, 10, _extra) \ -DOMCI_CASTABLE_INTERFACE(nsDOMUIEvent, nsDOMEventBase, 11, _extra) +DOMCI_CASTABLE_INTERFACE(nsDOMUIEvent, nsDOMEventBase, 11, _extra) \ +DOMCI_CASTABLE_INTERFACE(nsGlobalWindow, nsIDOMEventTarget, 12, _extra) // Make sure all classes mentioned in DOMCI_CASTABLE_INTERFACES // have been declared. diff --git a/dom/base/nsDOMJSUtils.h b/dom/base/nsDOMJSUtils.h index bac8aff6598..5917064a1b9 100644 --- a/dom/base/nsDOMJSUtils.h +++ b/dom/base/nsDOMJSUtils.h @@ -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/. */ - #ifndef nsDOMJSUtils_h__ #define nsDOMJSUtils_h__ #include "nsIScriptContext.h" +#include "jsapi.h" class nsIJSArgArray; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 5ffbea275dc..f7027094a75 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 @@ -50,7 +50,6 @@ #include "nsJSUtils.h" #include "jsapi.h" // for JSAutoRequest #include "js/OldDebugAPI.h" // for JS_ClearWatchPointsForObject -#include "jsfriendapi.h" // for JS_GetGlobalForFrame #include "jswrapper.h" #include "nsReadableUtils.h" #include "nsDOMClassInfo.h" @@ -219,6 +218,7 @@ #include "mozilla/dom/FunctionBinding.h" #include "mozilla/dom/WindowBinding.h" #include "mozilla/dom/TabChild.h" +#include "nsIDOMMediaQueryList.h" #ifdef MOZ_WEBSPEECH #include "mozilla/dom/SpeechSynthesis.h" @@ -316,6 +316,19 @@ nsGlobalWindow::DOMMinTimeoutValue() const { } \ PR_END_MACRO +#define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \ + PR_BEGIN_MACRO \ + if (IsInnerWindow()) { \ + nsGlobalWindow *outer = GetOuterWindowInternal(); \ + if (!outer) { \ + NS_WARNING("No outer window available!"); \ + errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ + return err_rval; \ + } \ + return outer->method args; \ + } \ + PR_END_MACRO + #define FORWARD_TO_OUTER_VOID(method, args) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ @@ -375,6 +388,18 @@ nsGlobalWindow::DOMMinTimeoutValue() const { } \ PR_END_MACRO +#define FORWARD_TO_INNER_OR_THROW(method, args, errorresult, err_rval) \ + PR_BEGIN_MACRO \ + if (IsOuterWindow()) { \ + if (!mInnerWindow) { \ + NS_WARNING("No inner window available!"); \ + errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ + return err_rval; \ + } \ + return GetCurrentInnerWindowInternal()->method args; \ + } \ + PR_END_MACRO + #define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsOuterWindow()) { \ @@ -926,7 +951,7 @@ bool nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, JS::AutoIdVector &props) { - uint32_t length = GetWindow(proxy)->GetLength(); + uint32_t length = GetWindow(proxy)->Length(); MOZ_ASSERT(int32_t(length) >= 0); if (!props.reserve(props.length() + length)) { return false; @@ -3314,91 +3339,133 @@ nsPIDOMWindow::UnmuteAudioContexts() NS_IMETHODIMP nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument) { - nsCOMPtr document = do_QueryInterface(GetDoc()); + nsCOMPtr document = do_QueryInterface(GetDocument()); document.forget(aDocument); return NS_OK; } +nsIDOMWindow* +nsGlobalWindow::GetWindow(ErrorResult& aError) +{ + FORWARD_TO_OUTER_OR_THROW(GetWindow, (aError), aError, nullptr); + + return this; +} + NS_IMETHODIMP nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow) { - FORWARD_TO_OUTER(GetWindow, (aWindow), NS_ERROR_NOT_INITIALIZED); + ErrorResult rv; + nsCOMPtr window = GetWindow(rv); + window.forget(aWindow); - *aWindow = static_cast(this); - NS_ADDREF(*aWindow); - return NS_OK; + return rv.ErrorCode(); +} + +nsIDOMWindow* +nsGlobalWindow::GetSelf(ErrorResult& aError) +{ + FORWARD_TO_OUTER_OR_THROW(GetSelf, (aError), aError, nullptr); + + return this; } NS_IMETHODIMP nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow) { - FORWARD_TO_OUTER(GetSelf, (aWindow), NS_ERROR_NOT_INITIALIZED); + ErrorResult rv; + nsCOMPtr window = GetSelf(rv); + window.forget(aWindow); - *aWindow = static_cast(this); - NS_ADDREF(*aWindow); - return NS_OK; + return rv.ErrorCode(); } -NS_IMETHODIMP -nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator) +Navigator* +nsGlobalWindow::GetNavigator(ErrorResult& aError) { - FORWARD_TO_INNER(GetNavigator, (aNavigator), NS_ERROR_NOT_INITIALIZED); - - *aNavigator = nullptr; + FORWARD_TO_INNER_OR_THROW(GetNavigator, (aError), aError, nullptr); if (!mNavigator) { mNavigator = new Navigator(this); } - NS_ADDREF(*aNavigator = mNavigator); + return mNavigator; +} - return NS_OK; +NS_IMETHODIMP +nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator) +{ + ErrorResult rv; + nsCOMPtr navigator = GetNavigator(rv); + navigator.forget(aNavigator); + + return rv.ErrorCode(); +} + +nsScreen* +nsGlobalWindow::GetScreen(ErrorResult& aError) +{ + FORWARD_TO_INNER_OR_THROW(GetScreen, (aError), aError, nullptr); + + if (!mScreen) { + mScreen = nsScreen::Create(this); + if (!mScreen) { + aError.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + } + + return mScreen; } NS_IMETHODIMP nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen) { - FORWARD_TO_INNER(GetScreen, (aScreen), NS_ERROR_NOT_INITIALIZED); + ErrorResult rv; + nsRefPtr screen = GetScreen(rv); + screen.forget(aScreen); - *aScreen = nullptr; + return rv.ErrorCode(); +} - if (!mScreen) { - mScreen = nsScreen::Create(this); - if (!mScreen) { - return NS_ERROR_UNEXPECTED; - } +nsHistory* +nsGlobalWindow::GetHistory(ErrorResult& aError) +{ + FORWARD_TO_INNER_OR_THROW(GetHistory, (aError), aError, nullptr); + + if (!mHistory) { + mHistory = new nsHistory(this); } - NS_IF_ADDREF(*aScreen = mScreen); - - return NS_OK; + return mHistory; } NS_IMETHODIMP nsGlobalWindow::GetHistory(nsISupports** aHistory) { - FORWARD_TO_INNER(GetHistory, (aHistory), NS_ERROR_NOT_INITIALIZED); + ErrorResult rv; + nsCOMPtr history = GetHistory(rv); + history.forget(aHistory); - *aHistory = nullptr; + return rv.ErrorCode(); +} - if (!mHistory) { - mHistory = new nsHistory(this); - if (!mHistory) { - return NS_ERROR_OUT_OF_MEMORY; - } - } +nsPerformance* +nsGlobalWindow::GetPerformance(ErrorResult& aError) +{ + FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr); - NS_IF_ADDREF(*aHistory = mHistory); - return NS_OK; + return nsPIDOMWindow::GetPerformance(); } NS_IMETHODIMP nsGlobalWindow::GetPerformance(nsISupports** aPerformance) { - FORWARD_TO_INNER(GetPerformance, (aPerformance), NS_ERROR_NOT_INITIALIZED); + ErrorResult rv; + nsCOMPtr performance = GetPerformance(rv); + performance.forget(aPerformance); - NS_IF_ADDREF(*aPerformance = nsPIDOMWindow::GetPerformance()); - return NS_OK; + return rv.ErrorCode(); } nsPerformance* @@ -3431,23 +3498,10 @@ nsPIDOMWindow::CreatePerformanceObjectIfNeeded() // nsISpeechSynthesisGetter #ifdef MOZ_WEBSPEECH -NS_IMETHODIMP -nsGlobalWindow::GetSpeechSynthesis(nsISupports** aSpeechSynthesis) -{ - FORWARD_TO_INNER(GetSpeechSynthesis, (aSpeechSynthesis), NS_ERROR_NOT_INITIALIZED); - - NS_IF_ADDREF(*aSpeechSynthesis = GetSpeechSynthesisInternal()); - return NS_OK; -} - SpeechSynthesis* -nsGlobalWindow::GetSpeechSynthesisInternal() +nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError) { - MOZ_ASSERT(IsInnerWindow()); - - if (!SpeechSynthesis::PrefEnabled()) { - return nullptr; - } + FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr); if (!mSpeechSynthesis) { mSpeechSynthesis = new SpeechSynthesis(this); @@ -3455,8 +3509,40 @@ nsGlobalWindow::GetSpeechSynthesisInternal() return mSpeechSynthesis; } + +NS_IMETHODIMP +nsGlobalWindow::GetSpeechSynthesis(nsISupports** aSpeechSynthesis) +{ + ErrorResult rv; + nsCOMPtr speechSynthesis; + if (SpeechSynthesis::PrefEnabled()) { + speechSynthesis = GetSpeechSynthesis(rv); + } + speechSynthesis.forget(aSpeechSynthesis); + + return rv.ErrorCode(); +} #endif +already_AddRefed +nsGlobalWindow::GetParent(ErrorResult& aError) +{ + FORWARD_TO_OUTER_OR_THROW(GetParent, (aError), aError, nullptr); + + if (!mDocShell) { + return nullptr; + } + + nsCOMPtr parent; + if (mDocShell->GetIsBrowserOrApp()) { + parent = this; + } else { + aError = GetRealParent(getter_AddRefs(parent)); + } + + return parent.forget(); +} + /** * GetScriptableParent is called when script reads window.parent. * @@ -3467,20 +3553,11 @@ nsGlobalWindow::GetSpeechSynthesisInternal() NS_IMETHODIMP nsGlobalWindow::GetScriptableParent(nsIDOMWindow** aParent) { - FORWARD_TO_OUTER(GetScriptableParent, (aParent), NS_ERROR_NOT_INITIALIZED); + ErrorResult rv; + nsCOMPtr parent = GetParent(rv); + parent.forget(aParent); - *aParent = NULL; - if (!mDocShell) { - return NS_OK; - } - - if (mDocShell->GetIsBrowserOrApp()) { - nsCOMPtr parent = static_cast(this); - parent.swap(*aParent); - return NS_OK; - } - - return GetRealParent(aParent); + return rv.ErrorCode(); } /** @@ -3573,24 +3650,67 @@ nsGlobalWindow::GetTopImpl(nsIDOMWindow** aTop, bool aScriptable) return NS_OK; } -NS_IMETHODIMP -nsGlobalWindow::GetContent(nsIDOMWindow** aContent) +JSObject* +nsGlobalWindow::GetContent(JSContext* aCx, ErrorResult& aError) { - FORWARD_TO_OUTER(GetContent, (aContent), NS_ERROR_NOT_INITIALIZED); - *aContent = nullptr; + FORWARD_TO_OUTER_OR_THROW(GetContent, (aCx, aError), aError, nullptr); + nsCOMPtr content = GetContentInternal(aError); + if (aError.Failed()) { + return nullptr; + } + + if (content) { + JS::Rooted global(aCx, JS::CurrentGlobalOrNull(aCx)); + if (!global) { + aError.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + JS::Rooted val(aCx); + aError = nsContentUtils::WrapNative(aCx, global, content, &val); + if (aError.Failed()) { + return nullptr; + } + + return &val.toObject(); + } + + if (!nsContentUtils::IsCallerChrome() || !IsChromeWindow()) { + aError.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + // Something tries to get .content on a ChromeWindow, try to fetch the CPOW. + nsCOMPtr treeOwner = GetTreeOwner(); + if (!treeOwner) { + aError.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + JS::Rooted val(aCx); + aError = treeOwner->GetContentWindow(aCx, val.address()); + if (aError.Failed()) { + return nullptr; + } + + return &val.toObject(); +} + +already_AddRefed +nsGlobalWindow::GetContentInternal(ErrorResult& aError) +{ // First check for a named frame named "content" nsCOMPtr domWindow = GetChildWindow(NS_LITERAL_STRING("content")); if (domWindow) { - domWindow.forget(aContent); - return NS_OK; + return domWindow.forget(); } // If we're contained in