From cb06edf308151e81c9c5ed8a45b0875b8c00aff2 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Thu, 14 Jan 2016 21:17:13 +0100 Subject: [PATCH 01/41] Bug 1239743: Do not allow windows to be resized to sizes above the maximum texture size. We don't know how to draw to these anyway. r=jimm --HG-- extra : rebase_source : f1ad0237a72178fb1870bd23ae1aacd098ad995f --- widget/windows/nsWindow.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index b76ce885ca2c..9da3993ac9d0 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -189,6 +189,7 @@ #include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/InputAPZContext.h" +#include "ClientLayerManager.h" #include "InputData.h" #include "mozilla/Telemetry.h" @@ -1410,6 +1411,20 @@ nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) c.mMinSize.width = std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width); c.mMinSize.height = std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height); } + ClientLayerManager *clientLayerManager = + (GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) + ? static_cast(GetLayerManager()) + : nullptr; + + if (clientLayerManager) { + int32_t maxSize = clientLayerManager->GetMaxTextureSize(); + // We can't make ThebesLayers bigger than this anyway.. no point it letting + // a window grow bigger as we won't be able to draw content there in + // general. + c.mMaxSize.width = std::min(c.mMaxSize.width, maxSize); + c.mMaxSize.height = std::min(c.mMaxSize.height, maxSize); + } + mSizeConstraintsScale = GetDefaultScale().scale; nsBaseWidget::SetSizeConstraints(c); From 5132ceb11bad7dbaec07315a32830e73ae3e7bc4 Mon Sep 17 00:00:00 2001 From: Yura Zenevich Date: Thu, 14 Jan 2016 16:12:13 -0500 Subject: [PATCH 02/41] Bug 1238744 - centralizing interactions into its own file. r=ato --- testing/marionette/accessibility.js | 335 ++++++++++++++++++++++++++++ testing/marionette/driver.js | 40 ++-- testing/marionette/elements.js | 205 +---------------- testing/marionette/interactions.js | 317 ++++++++++++++++++++++++++ testing/marionette/jar.mn | 2 + testing/marionette/listener.js | 188 +++------------- testing/marionette/sendkeys.js | 6 +- 7 files changed, 701 insertions(+), 392 deletions(-) create mode 100644 testing/marionette/accessibility.js create mode 100644 testing/marionette/interactions.js diff --git a/testing/marionette/accessibility.js b/testing/marionette/accessibility.js new file mode 100644 index 000000000000..1cd204479558 --- /dev/null +++ b/testing/marionette/accessibility.js @@ -0,0 +1,335 @@ +/* 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/. */ + +/* global Accessibility, Components, Log, ElementNotAccessibleError, + XPCOMUtils */ + +var {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Log.jsm'); + +XPCOMUtils.defineLazyModuleGetter(this, 'setInterval', + 'resource://gre/modules/Timer.jsm'); +XPCOMUtils.defineLazyModuleGetter(this, 'clearInterval', + 'resource://gre/modules/Timer.jsm'); +XPCOMUtils.defineLazyModuleGetter(this, 'ElementNotAccessibleError', + 'chrome://marionette/content/error.js'); + +this.EXPORTED_SYMBOLS = ['Accessibility']; + +/** + * Accessible states used to check element's state from the accessiblity API + * perspective. + */ +const states = { + unavailable: Ci.nsIAccessibleStates.STATE_UNAVAILABLE, + focusable: Ci.nsIAccessibleStates.STATE_FOCUSABLE, + selectable: Ci.nsIAccessibleStates.STATE_SELECTABLE, + selected: Ci.nsIAccessibleStates.STATE_SELECTED +}; + +var logger = Log.repository.getLogger('Marionette'); + +/** + * Component responsible for interacting with platform accessibility API. Its + * methods serve as wrappers for testing content and chrome accessibility as + * well as accessibility of user interactions. + * + * @param {Function} getCapabilies + * Session capabilities getter. + */ +this.Accessibility = function Accessibility(getCapabilies = () => {}) { + // A flag indicating whether the accessibility issue should be logged or cause + // an exception. Default: log to stdout. + Object.defineProperty(this, 'strict', { + configurable: true, + get: function() { + let capabilies = getCapabilies(); + return !!capabilies.raisesAccessibilityExceptions; + } + }); + // An interface for in-process accessibility clients + // Note: we access it lazily to not enable accessibility when it is not needed + Object.defineProperty(this, 'retrieval', { + configurable: true, + get: function() { + delete this.retrieval; + this.retrieval = Cc[ + '@mozilla.org/accessibleRetrieval;1'].getService( + Ci.nsIAccessibleRetrieval); + return this.retrieval; + } + }); +}; + +Accessibility.prototype = { + + /** + * Number of attempts to get an accessible object for an element. We attempt + * more than once because accessible tree can be out of sync with the DOM tree + * for a short period of time. + * @type {Number} + */ + GET_ACCESSIBLE_ATTEMPTS: 100, + + /** + * An interval between attempts to retrieve an accessible object for an + * element. + * @type {Number} ms + */ + GET_ACCESSIBLE_ATTEMPT_INTERVAL: 10, + + /** + * Accessible object roles that support some action + * @type Object + */ + ACTIONABLE_ROLES: new Set([ + 'pushbutton', + 'checkbutton', + 'combobox', + 'key', + 'link', + 'menuitem', + 'check menu item', + 'radio menu item', + 'option', + 'listbox option', + 'listbox rich option', + 'check rich option', + 'combobox option', + 'radiobutton', + 'rowheader', + 'switch', + 'slider', + 'spinbutton', + 'pagetab', + 'entry', + 'outlineitem' + ]), + + /** + * Get an accessible object for a DOM element + * @param nsIDOMElement element + * @param Boolean mustHaveAccessible a flag indicating that the element must + * have an accessible object + * @return nsIAccessible object for the element + */ + getAccessibleObject(element, mustHaveAccessible = false) { + return new Promise((resolve, reject) => { + let acc = this.retrieval.getAccessibleFor(element); + + if (acc || !mustHaveAccessible) { + // If accessible object is found, return it. If it is not required, + // also resolve. + resolve(acc); + } else { + // If we require an accessible object, we need to poll for it because + // accessible tree might be out of sync with DOM tree for a short time. + let attempts = this.GET_ACCESSIBLE_ATTEMPTS; + let intervalId = setInterval(() => { + let acc = this.retrieval.getAccessibleFor(element); + if (acc || --attempts <= 0) { + clearInterval(intervalId); + if (acc) { resolve(acc); } + else { reject(); } + } + }, this.GET_ACCESSIBLE_ATTEMPT_INTERVAL); + } + }).catch(() => this.error( + 'Element does not have an accessible object', element)); + }, + + /** + * Check if the accessible has a role that supports some action + * @param nsIAccessible object + * @return Boolean an indicator of role being actionable + */ + isActionableRole(accessible) { + return this.ACTIONABLE_ROLES.has( + this.retrieval.getStringRole(accessible.role)); + }, + + /** + * Determine if an accessible has at least one action that it supports + * @param nsIAccessible object + * @return Boolean an indicator of supporting at least one accessible action + */ + hasActionCount(accessible) { + return accessible.actionCount > 0; + }, + + /** + * Determine if an accessible has a valid name + * @param nsIAccessible object + * @return Boolean an indicator that the element has a non empty valid name + */ + hasValidName(accessible) { + return accessible.name && accessible.name.trim(); + }, + + /** + * Check if an accessible has a set hidden attribute + * @param nsIAccessible object + * @return Boolean an indicator that the element has a hidden accessible + * attribute set to true + */ + hasHiddenAttribute(accessible) { + let hidden = false; + try { + hidden = accessible.attributes.getStringProperty('hidden'); + } finally { + // If the property is missing, exception will be thrown. + return hidden && hidden === 'true'; + } + }, + + /** + * Verify if an accessible has a given state + * @param nsIAccessible object + * @param Number stateToMatch the state to match + * @return Boolean accessible has a state + */ + matchState(accessible, stateToMatch) { + let state = {}; + accessible.getState(state, {}); + return !!(state.value & stateToMatch); + }, + + /** + * Check if an accessible is hidden from the user of the accessibility API + * @param nsIAccessible object + * @return Boolean an indicator that the element is hidden from the user + */ + isHidden(accessible) { + while (accessible) { + if (this.hasHiddenAttribute(accessible)) { + return true; + } + accessible = accessible.parent; + } + return false; + }, + + /** + * Send an error message or log the error message in the log + * @param String message + * @param DOMElement element that caused an error + */ + error(message, element) { + if (!message) { + return; + } + if (element) { + let {id, tagName, className} = element; + message += `: id: ${id}, tagName: ${tagName}, className: ${className}`; + } + if (this.strict) { + throw new ElementNotAccessibleError(message); + } + logger.error(message); + }, + + /** + * Check if the element's visible state corresponds to its accessibility API + * visibility + * @param nsIAccessible object + * @param WebElement corresponding to nsIAccessible object + * @param Boolean visible element's visibility state + */ + checkVisible(accessible, element, visible) { + if (!accessible) { + return; + } + let hiddenAccessibility = this.isHidden(accessible); + let message; + if (visible && hiddenAccessibility) { + message = 'Element is not currently visible via the accessibility API ' + + 'and may not be manipulated by it'; + } else if (!visible && !hiddenAccessibility) { + message = 'Element is currently only visible via the accessibility API ' + + 'and can be manipulated by it'; + } + this.error(message, element); + }, + + /** + * Check if the element's unavailable accessibility state matches the enabled + * state + * @param nsIAccessible object + * @param WebElement corresponding to nsIAccessible object + * @param Boolean enabled element's enabled state + * @param Object container frame and optional ShadowDOM + */ + checkEnabled(accessible, element, enabled, container) { + if (!accessible) { + return; + } + let disabledAccessibility = this.matchState(accessible, states.unavailable); + let explorable = container.frame.document.defaultView.getComputedStyle( + element).getPropertyValue('pointer-events') !== 'none'; + let message; + + if (!explorable && !disabledAccessibility) { + message = 'Element is enabled but is not explorable via the ' + + 'accessibility API'; + } else if (enabled && disabledAccessibility) { + message = 'Element is enabled but disabled via the accessibility API'; + } else if (!enabled && !disabledAccessibility) { + message = 'Element is disabled but enabled via the accessibility API'; + } + this.error(message, element); + }, + + /** + * Check if it is possible to activate an element with the accessibility API + * @param nsIAccessible object + * @param WebElement corresponding to nsIAccessible object + */ + checkActionable(accessible, element) { + if (!accessible) { + return; + } + let message; + if (!this.hasActionCount(accessible)) { + message = 'Element does not support any accessible actions'; + } else if (!this.isActionableRole(accessible)) { + message = 'Element does not have a correct accessibility role ' + + 'and may not be manipulated via the accessibility API'; + } else if (!this.hasValidName(accessible)) { + message = 'Element is missing an accessible name'; + } else if (!this.matchState(accessible, states.focusable)) { + message = 'Element is not focusable via the accessibility API'; + } + this.error(message, element); + }, + + /** + * Check if element's selected state corresponds to its accessibility API + * selected state. + * @param nsIAccessible object + * @param WebElement corresponding to nsIAccessible object + * @param Boolean selected element's selected state + */ + checkSelected(accessible, element, selected) { + if (!accessible) { + return; + } + if (!this.matchState(accessible, states.selectable)) { + // Element is not selectable via the accessibility API + return; + } + + let selectedAccessibility = this.matchState(accessible, states.selected); + let message; + if (selected && !selectedAccessibility) { + message = + 'Element is selected but not selected via the accessibility API'; + } else if (!selected && selectedAccessibility) { + message = + 'Element is not selected but selected via the accessibility API'; + } + this.error(message, element); + } +}; diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index 74278d6775c7..589cf7fb7424 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -23,6 +23,7 @@ XPCOMUtils.defineLazyServiceGetter( this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2"); Cu.import("chrome://marionette/content/actions.js"); +Cu.import("chrome://marionette/content/interactions.js"); Cu.import("chrome://marionette/content/elements.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/modal.js"); @@ -164,6 +165,8 @@ this.GeckoDriver = function(appName, device, stopSignal, emulator) { "version": Services.appinfo.version, }; + this.interactions = new Interactions(utils, () => this.sessionCapabilities); + this.mm = globalMessageManager; this.listener = proxy.toListener(() => this.mm, this.sendAsync.bind(this)); @@ -1981,10 +1984,9 @@ GeckoDriver.prototype.clickElement = function(cmd, resp) { switch (this.context) { case Context.CHROME: - // click atom fails, fall back to click() action let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); - el.click(); + yield this.interactions.clickElement({ frame: win }, + this.curBrowser.elementManager, id) break; case Context.CONTENT: @@ -2082,8 +2084,8 @@ GeckoDriver.prototype.isElementDisplayed = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win}); - resp.body.value = utils.isElementDisplayed(el); + resp.body.value = yield this.interactions.isElementDisplayed( + {frame: win}, this.curBrowser.elementManager, id); break; case Context.CONTENT: @@ -2130,8 +2132,8 @@ GeckoDriver.prototype.isElementEnabled = function(cmd, resp) { case Context.CHROME: // Selenium atom doesn't quite work here let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, {frame: win}); - resp.body.value = !(!!el.disabled); + resp.body.value = yield this.interactions.isElementEnabled( + {frame: win}, this.curBrowser.elementManager, id); break; case Context.CONTENT: @@ -2153,14 +2155,8 @@ GeckoDriver.prototype.isElementSelected = function(cmd, resp) { case Context.CHROME: // Selenium atom doesn't quite work here let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); - if (typeof el.checked != "undefined") { - resp.body.value = !!el.checked; - } else if (typeof el.selected != "undefined") { - resp.body.value = !!el.selected; - } else { - resp.body.value = true; - } + resp.body.value = yield this.interactions.isElementSelected( + { frame: win }, this.curBrowser.elementManager, id); break; case Context.CONTENT: @@ -2209,15 +2205,8 @@ GeckoDriver.prototype.sendKeysToElement = function(cmd, resp) { switch (this.context) { case Context.CHROME: let win = this.getCurrentWindow(); - let el = this.curBrowser.elementManager.getKnownElement(id, { frame: win }); - utils.sendKeysToElement( - win, - el, - value, - () => {}, - e => { throw e; }, - cmd.id, - true /* ignore visibility check */); + yield this.interactions.sendKeysToElement( + { frame: win }, this.curBrowser.elementManager, id, value, true); break; case Context.CONTENT: @@ -2806,9 +2795,6 @@ GeckoDriver.prototype.sendKeysToDialog = function(cmd, resp) { win, loginTextbox, cmd.parameters.value, - () => {}, - e => { throw e; }, - this.command_id, true /* ignore visibility check */); }; diff --git a/testing/marionette/elements.js b/testing/marionette/elements.js index 07589ea8aede..e5dc6242d1c4 100644 --- a/testing/marionette/elements.js +++ b/testing/marionette/elements.js @@ -5,12 +5,6 @@ let {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("chrome://marionette/content/error.js"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, 'setInterval', - 'resource://gre/modules/Timer.jsm'); -XPCOMUtils.defineLazyModuleGetter(this, 'clearInterval', - 'resource://gre/modules/Timer.jsm'); /** * The ElementManager manages DOM element references and @@ -30,7 +24,6 @@ XPCOMUtils.defineLazyModuleGetter(this, 'clearInterval', */ this.EXPORTED_SYMBOLS = [ - "Accessibility", "elements", "ElementManager", "CLASS_NAME", @@ -47,8 +40,8 @@ this.EXPORTED_SYMBOLS = [ const DOCUMENT_POSITION_DISCONNECTED = 1; -const uuidGen = Components.classes["@mozilla.org/uuid-generator;1"] - .getService(Components.interfaces.nsIUUIDGenerator); +const uuidGen = Cc["@mozilla.org/uuid-generator;1"] + .getService(Ci.nsIUUIDGenerator); this.CLASS_NAME = "class name"; this.SELECTOR = "css selector"; @@ -61,192 +54,6 @@ this.XPATH = "xpath"; this.ANON= "anon"; this.ANON_ATTRIBUTE = "anon attribute"; -this.Accessibility = function Accessibility() { - // A flag indicating whether the accessibility issue should be logged or cause - // an exception. Default: log to stdout. - this.strict = false; - // An interface for in-process accessibility clients - // Note: we access it lazily to not enable accessibility when it is not needed - Object.defineProperty(this, 'accessibleRetrieval', { - configurable: true, - get: function() { - delete this.accessibleRetrieval; - this.accessibleRetrieval = Components.classes[ - '@mozilla.org/accessibleRetrieval;1'].getService( - Components.interfaces.nsIAccessibleRetrieval); - return this.accessibleRetrieval; - } - }); -}; - -Accessibility.prototype = { - - /** - * Number of attempts to get an accessible object for an element. We attempt - * more than once because accessible tree can be out of sync with the DOM tree - * for a short period of time. - * @type {Number} - */ - GET_ACCESSIBLE_ATTEMPTS: 100, - - /** - * An interval between attempts to retrieve an accessible object for an - * element. - * @type {Number} ms - */ - GET_ACCESSIBLE_ATTEMPT_INTERVAL: 10, - - /** - * Accessible object roles that support some action - * @type Object - */ - actionableRoles: new Set([ - 'pushbutton', - 'checkbutton', - 'combobox', - 'key', - 'link', - 'menuitem', - 'check menu item', - 'radio menu item', - 'option', - 'listbox option', - 'listbox rich option', - 'check rich option', - 'combobox option', - 'radiobutton', - 'rowheader', - 'switch', - 'slider', - 'spinbutton', - 'pagetab', - 'entry', - 'outlineitem' - ]), - - /** - * Get an accessible object for a DOM element - * @param nsIDOMElement element - * @param Boolean mustHaveAccessible a flag indicating that the element must - * have an accessible object - * @return nsIAccessible object for the element - */ - getAccessibleObject(element, mustHaveAccessible = false) { - return new Promise((resolve, reject) => { - let acc = this.accessibleRetrieval.getAccessibleFor(element); - - if (acc || !mustHaveAccessible) { - // If accessible object is found, return it. If it is not required, - // also resolve. - resolve(acc); - } else { - // If we require an accessible object, we need to poll for it because - // accessible tree might be out of sync with DOM tree for a short time. - let attempts = this.GET_ACCESSIBLE_ATTEMPTS; - let intervalId = setInterval(() => { - let acc = this.accessibleRetrieval.getAccessibleFor(element); - if (acc || --attempts <= 0) { - clearInterval(intervalId); - if (acc) { resolve(acc); } - else { reject(); } - } - }, this.GET_ACCESSIBLE_ATTEMPT_INTERVAL); - } - }).catch(() => this.handleErrorMessage( - 'Element does not have an accessible object', element)); - }, - - /** - * Check if the accessible has a role that supports some action - * @param nsIAccessible object - * @return Boolean an indicator of role being actionable - */ - isActionableRole(accessible) { - return this.actionableRoles.has( - this.accessibleRetrieval.getStringRole(accessible.role)); - }, - - /** - * Determine if an accessible has at least one action that it supports - * @param nsIAccessible object - * @return Boolean an indicator of supporting at least one accessible action - */ - hasActionCount(accessible) { - return accessible.actionCount > 0; - }, - - /** - * Determine if an accessible has a valid name - * @param nsIAccessible object - * @return Boolean an indicator that the element has a non empty valid name - */ - hasValidName(accessible) { - return accessible.name && accessible.name.trim(); - }, - - /** - * Check if an accessible has a set hidden attribute - * @param nsIAccessible object - * @return Boolean an indicator that the element has a hidden accessible - * attribute set to true - */ - hasHiddenAttribute(accessible) { - let hidden; - try { - hidden = accessible.attributes.getStringProperty('hidden'); - } finally { - // If the property is missing, exception will be thrown. - return hidden && hidden === 'true'; - } - }, - - /** - * Verify if an accessible has a given state - * @param nsIAccessible object - * @param String stateName name of the state to match - * @return Boolean accessible has a state - */ - matchState(accessible, stateName) { - let stateToMatch = Components.interfaces.nsIAccessibleStates[stateName]; - let state = {}; - accessible.getState(state, {}); - return !!(state.value & stateToMatch); - }, - - /** - * Check if an accessible is hidden from the user of the accessibility API - * @param nsIAccessible object - * @return Boolean an indicator that the element is hidden from the user - */ - isHidden(accessible) { - while (accessible) { - if (this.hasHiddenAttribute(accessible)) { - return true; - } - accessible = accessible.parent; - } - return false; - }, - - /** - * Send an error message or log the error message in the log - * @param String message - * @param DOMElement element that caused an error - */ - handleErrorMessage(message, element) { - if (!message) { - return; - } - if (element) { - message += ` -> id: ${element.id}, tagName: ${element.tagName}, className: ${element.className}\n`; - } - if (this.strict) { - throw new ElementNotAccessibleError(message); - } - dump(Date.now() + " Marionette: " + message); - } -}; - this.ElementManager = function ElementManager(notSupported) { this.seenItems = {}; this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); @@ -291,7 +98,7 @@ ElementManager.prototype = { } } let id = elements.generateUUID(); - this.seenItems[id] = Components.utils.getWeakReference(element); + this.seenItems[id] = Cu.getWeakReference(element); return id; }, @@ -562,7 +369,7 @@ ElementManager.prototype = { on_success, on_error, command_id), 100, - Components.interfaces.nsITimer.TYPE_ONE_SHOT); + Ci.nsITimer.TYPE_ONE_SHOT); } } else { if (isArrayLike) { @@ -598,7 +405,7 @@ ElementManager.prototype = { */ findByXPath: function EM_findByXPath(root, value, node) { return root.evaluate(value, node, null, - Components.interfaces.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + Ci.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; }, /** @@ -616,7 +423,7 @@ ElementManager.prototype = { */ findByXPathAll: function EM_findByXPathAll(root, value, node) { let values = root.evaluate(value, node, null, - Components.interfaces.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + Ci.nsIDOMXPathResult.ORDERED_NODE_ITERATOR_TYPE, null); let elements = []; let element = values.iterateNext(); while (element) { diff --git a/testing/marionette/interactions.js b/testing/marionette/interactions.js new file mode 100644 index 000000000000..1679fb9e2f23 --- /dev/null +++ b/testing/marionette/interactions.js @@ -0,0 +1,317 @@ +/* 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/. */ + +/* global Components, Accessibility, ElementNotVisibleError, + InvalidElementStateError, Interactions */ + +var {utils: Cu} = Components; + +this.EXPORTED_SYMBOLS = ['Interactions']; + +Cu.import('chrome://marionette/content/accessibility.js'); +Cu.import('chrome://marionette/content/error.js'); + +/** + * XUL elements that support disabled attribtue. + */ +const DISABLED_ATTRIBUTE_SUPPORTED_XUL = new Set([ + 'ARROWSCROLLBOX', + 'BUTTON', + 'CHECKBOX', + 'COLORPICKER', + 'COMMAND', + 'DATEPICKER', + 'DESCRIPTION', + 'KEY', + 'KEYSET', + 'LABEL', + 'LISTBOX', + 'LISTCELL', + 'LISTHEAD', + 'LISTHEADER', + 'LISTITEM', + 'MENU', + 'MENUITEM', + 'MENULIST', + 'MENUSEPARATOR', + 'PREFERENCE', + 'RADIO', + 'RADIOGROUP', + 'RICHLISTBOX', + 'RICHLISTITEM', + 'SCALE', + 'TAB', + 'TABS', + 'TEXTBOX', + 'TIMEPICKER', + 'TOOLBARBUTTON', + 'TREE' +]); + +/** + * XUL elements that support checked property. + */ +const CHECKED_PROPERTY_SUPPORTED_XUL = new Set([ + 'BUTTON', + 'CHECKBOX', + 'LISTITEM', + 'TOOLBARBUTTON' +]); + +/** + * XUL elements that support selected property. + */ +const SELECTED_PROPERTY_SUPPORTED_XUL = new Set([ + 'LISTITEM', + 'MENU', + 'MENUITEM', + 'MENUSEPARATOR', + 'RADIO', + 'RICHLISTITEM', + 'TAB' +]); + +/** + * This function generates a pair of coordinates relative to the viewport given + * a target element and coordinates relative to that element's top-left corner. + * @param 'x', and 'y' are the relative to the target. + * If they are not specified, then the center of the target is used. + */ +function coordinates(target, x, y) { + let box = target.getBoundingClientRect(); + if (typeof x === 'undefined') { + x = box.width / 2; + } + if (typeof y === 'undefined') { + y = box.height / 2; + } + return { + x: box.left + x, + y: box.top + y + }; +} + +/** + * A collection of interactions available in marionette. + * @type {Object} + */ +this.Interactions = function(utils, getCapabilies) { + this.utils = utils; + this.accessibility = new Accessibility(getCapabilies); +}; + +Interactions.prototype = { + /** + * Send click event to element. + * + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element + * + * @param ElementManager elementManager + * + * @param String id + * The DOM reference ID + */ + clickElement(container, elementManager, id) { + let el = elementManager.getKnownElement(id, container); + let visible = this.checkVisible(container, el); + if (!visible) { + throw new ElementNotVisibleError('Element is not visible'); + } + return this.accessibility.getAccessibleObject(el, true).then(acc => { + this.accessibility.checkVisible(acc, el, visible); + if (this.utils.isElementEnabled(el)) { + this.accessibility.checkEnabled(acc, el, true, container); + this.accessibility.checkActionable(acc, el); + if (this.isXULElement(el)) { + el.click(); + } else { + let rects = el.getClientRects(); + this.utils.synthesizeMouseAtPoint(rects[0].left + rects[0].width/2, + rects[0].top + rects[0].height/2, + {}, el.ownerDocument.defaultView); + } + } else { + throw new InvalidElementStateError('Element is not enabled'); + } + }); + }, + + /** + * Send keys to element + * + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element + * + * @param ElementManager elementManager + * + * @param String id + * The DOM reference ID + * + * @param String?Array value + * Value to send to an element + * + * @param Boolean ignoreVisibility + * A flag to check element visibility + */ + sendKeysToElement(container, elementManager, id, value, ignoreVisibility) { + let el = elementManager.getKnownElement(id, container); + return this.accessibility.getAccessibleObject(el, true).then(acc => { + this.accessibility.checkActionable(acc, el); + this.utils.sendKeysToElement( + container.frame, el, value, ignoreVisibility); + }); + }, + + /** + * Determine the element displayedness of the given web element. + * + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element + * + * @param ElementManager elementManager + * + * @param {WebElement} id + * Reference to web element. + * + * Also performs additional accessibility checks if enabled by session + * capability. + */ + isElementDisplayed(container, elementManager, id) { + let el = elementManager.getKnownElement(id, container); + let displayed = this.utils.isElementDisplayed(el); + return this.accessibility.getAccessibleObject(el).then(acc => { + this.accessibility.checkVisible(acc, el, displayed); + return displayed; + }); + }, + + /** + * Check if element is enabled. + * + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element + * + * @param ElementManager elementManager + * + * @param {WebElement} id + * Reference to web element. + * + * @return {boolean} + * True if enabled, false otherwise. + */ + isElementEnabled(container, elementManager, id) { + let el = elementManager.getKnownElement(id, container); + let enabled = true; + if (this.isXULElement(el)) { + // Check if XUL element supports disabled attribute + if (DISABLED_ATTRIBUTE_SUPPORTED_XUL.has(el.tagName.toUpperCase())) { + let disabled = this.utils.getElementAttribute(el, 'disabled'); + if (disabled && disabled === 'true') { + enabled = false; + } + } + } else { + enabled = this.utils.isElementEnabled(el); + } + return this.accessibility.getAccessibleObject(el).then(acc => { + this.accessibility.checkEnabled(acc, el, enabled, container); + return enabled; + }); + }, + + /** + * Determines if the referenced element is selected or not. + * + * This operation only makes sense on input elements of the Checkbox- + * and Radio Button states, or option elements. + * + * @param nsIDOMWindow, ShadowRoot container + * The window and an optional shadow root that contains the element + * + * @param ElementManager elementManager + * + * @param {WebElement} id + * Reference to web element. + */ + isElementSelected(container, elementManager, id) { + let el = elementManager.getKnownElement(id, container); + let selected = true; + if (this.isXULElement(el)) { + let tagName = el.tagName.toUpperCase(); + if (CHECKED_PROPERTY_SUPPORTED_XUL.has(tagName)) { + selected = el.checked; + } + if (SELECTED_PROPERTY_SUPPORTED_XUL.has(tagName)) { + selected = el.selected; + } + } else { + selected = this.utils.isElementSelected(el); + } + return this.accessibility.getAccessibleObject(el).then(acc => { + this.accessibility.checkSelected(acc, el, selected); + return selected; + }); + }, + + /** + * This function throws the visibility of the element error if the element is + * not displayed or the given coordinates are not within the viewport. + * + * @param 'x', and 'y' are the coordinates relative to the target. + * If they are not specified, then the center of the target is used. + */ + checkVisible(container, el, x, y) { + // Bug 1094246 - Webdriver's isShown doesn't work with content xul + if (!this.isXULElement(el)) { + //check if the element is visible + let visible = this.utils.isElementDisplayed(el); + if (!visible) { + return false; + } + } + + if (el.tagName.toLowerCase() === 'body') { + return true; + } + if (!this.elementInViewport(container, el, x, y)) { + //check if scroll function exist. If so, call it. + if (el.scrollIntoView) { + el.scrollIntoView(false); + if (!this.elementInViewport(container, el)) { + return false; + } + } + else { + return false; + } + } + return true; + }, + + isXULElement(el) { + return this.utils.getElementAttribute(el, 'namespaceURI').indexOf( + 'there.is.only.xul') >= 0; + }, + + /** + * This function returns true if the given coordinates are in the viewport. + * @param 'x', and 'y' are the coordinates relative to the target. + * If they are not specified, then the center of the target is used. + */ + elementInViewport(container, el, x, y) { + let c = coordinates(el, x, y); + let win = container.frame; + let viewPort = { + top: win.pageYOffset, + left: win.pageXOffset, + bottom: win.pageYOffset + win.innerHeight, + right: win.pageXOffset + win.innerWidth + }; + return (viewPort.left <= c.x + win.pageXOffset && + c.x + win.pageXOffset <= viewPort.right && + viewPort.top <= c.y + win.pageYOffset && + c.y + win.pageYOffset <= viewPort.bottom); + } +}; diff --git a/testing/marionette/jar.mn b/testing/marionette/jar.mn index c41414f21d55..3d3b34358c82 100644 --- a/testing/marionette/jar.mn +++ b/testing/marionette/jar.mn @@ -7,6 +7,8 @@ marionette.jar: content/server.js (server.js) content/driver.js (driver.js) content/actions.js (actions.js) + content/interactions.js (interactions.js) + content/accessibility.js (accessibility.js) content/listener.js (listener.js) content/elements.js (elements.js) content/sendkeys.js (sendkeys.js) diff --git a/testing/marionette/listener.js b/testing/marionette/listener.js index 80837a30762d..6189f465caba 100644 --- a/testing/marionette/listener.js +++ b/testing/marionette/listener.js @@ -18,6 +18,7 @@ Cu.import("chrome://marionette/content/cookies.js"); Cu.import("chrome://marionette/content/elements.js"); Cu.import("chrome://marionette/content/error.js"); Cu.import("chrome://marionette/content/proxy.js"); +Cu.import("chrome://marionette/content/interactions.js"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); @@ -43,7 +44,11 @@ var curContainer = { frame: content, shadowRoot: null }; var isRemoteBrowser = () => curContainer.frame.contentWindow !== null; var previousContainer = null; var elementManager = new ElementManager([]); -var accessibility = new Accessibility(); + +// Holds session capabilities. +var capabilities = {}; +var interactions = new Interactions(utils, () => capabilities); + var actions = new ActionChain(utils, checkForInterrupted); var importedScripts = null; @@ -108,9 +113,8 @@ function registerSelf() { if (register[0]) { let {id, remotenessChange} = register[0][0]; - let {B2G, raisesAccessibilityExceptions} = register[0][2]; - isB2G = B2G; - accessibility.strict = raisesAccessibilityExceptions; + capabilities = register[0][2]; + isB2G = capabilities.B2G; listenerId = id; if (typeof id != "undefined") { // check if we're the main process @@ -299,8 +303,8 @@ function waitForReady() { * current environment, and resets all values */ function newSession(msg) { - isB2G = msg.json.B2G; - accessibility.strict = msg.json.raisesAccessibilityExceptions; + capabilities = msg.json; + isB2G = capabilities.B2G; resetValues(); if (isB2G) { readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT); @@ -938,9 +942,9 @@ function singleTap(id, corx, cory) { if (!visible) { throw new ElementNotVisibleError("Element is not currently visible and may not be manipulated"); } - return accessibility.getAccessibleObject(el, true).then(acc => { - checkVisibleAccessibility(acc, el, visible); - checkActionableAccessibility(acc, el); + return interactions.accessibility.getAccessibleObject(el, true).then(acc => { + interactions.accessibility.checkVisible(acc, el, visible); + interactions.accessibility.checkActionable(acc, el); if (!curContainer.frame.document.createTouch) { actions.mouseEventsOnly = true; } @@ -955,108 +959,6 @@ function singleTap(id, corx, cory) { }); } -/** - * Check if the element's unavailable accessibility state matches the enabled - * state - * @param nsIAccessible object - * @param WebElement corresponding to nsIAccessible object - * @param Boolean enabled element's enabled state - */ -function checkEnabledAccessibility(accesible, element, enabled) { - if (!accesible) { - return; - } - let disabledAccessibility = accessibility.matchState( - accesible, 'STATE_UNAVAILABLE'); - let explorable = curContainer.frame.document.defaultView.getComputedStyle( - element, null).getPropertyValue('pointer-events') !== 'none'; - let message; - - if (!explorable && !disabledAccessibility) { - message = 'Element is enabled but is not explorable via the ' + - 'accessibility API'; - } else if (enabled && disabledAccessibility) { - message = 'Element is enabled but disabled via the accessibility API'; - } else if (!enabled && !disabledAccessibility) { - message = 'Element is disabled but enabled via the accessibility API'; - } - accessibility.handleErrorMessage(message, element); -} - -/** - * Check if the element's visible state corresponds to its accessibility API - * visibility - * @param nsIAccessible object - * @param WebElement corresponding to nsIAccessible object - * @param Boolean visible element's visibility state - */ -function checkVisibleAccessibility(accesible, element, visible) { - if (!accesible) { - return; - } - let hiddenAccessibility = accessibility.isHidden(accesible); - let message; - if (visible && hiddenAccessibility) { - message = 'Element is not currently visible via the accessibility API ' + - 'and may not be manipulated by it'; - } else if (!visible && !hiddenAccessibility) { - message = 'Element is currently only visible via the accessibility API ' + - 'and can be manipulated by it'; - } - accessibility.handleErrorMessage(message, element); -} - -/** - * Check if it is possible to activate an element with the accessibility API - * @param nsIAccessible object - * @param WebElement corresponding to nsIAccessible object - */ -function checkActionableAccessibility(accesible, element) { - if (!accesible) { - return; - } - let message; - if (!accessibility.hasActionCount(accesible)) { - message = 'Element does not support any accessible actions'; - } else if (!accessibility.isActionableRole(accesible)) { - message = 'Element does not have a correct accessibility role ' + - 'and may not be manipulated via the accessibility API'; - } else if (!accessibility.hasValidName(accesible)) { - message = 'Element is missing an accesible name'; - } else if (!accessibility.matchState(accesible, 'STATE_FOCUSABLE')) { - message = 'Element is not focusable via the accessibility API'; - } - accessibility.handleErrorMessage(message, element); -} - -/** - * Check if element's selected state corresponds to its accessibility API - * selected state. - * @param nsIAccessible object - * @param WebElement corresponding to nsIAccessible object - * @param Boolean selected element's selected state - */ -function checkSelectedAccessibility(accessible, element, selected) { - if (!accessible) { - return; - } - if (!accessibility.matchState(accessible, 'STATE_SELECTABLE')) { - // Element is not selectable via the accessibility API - return; - } - - let selectedAccessibility = accessibility.matchState( - accessible, 'STATE_SELECTED'); - let message; - if (selected && !selectedAccessibility) { - message = 'Element is selected but not selected via the accessibility API'; - } else if (!selected && selectedAccessibility) { - message = 'Element is not selected but selected via the accessibility API'; - } - accessibility.handleErrorMessage(message, element); -} - - /** * Function to create a touch based on the element * corx and cory are relative to the viewport, id is the touchId @@ -1460,24 +1362,7 @@ function getActiveElement() { * Reference to the web element to click. */ function clickElement(id) { - let el = elementManager.getKnownElement(id, curContainer); - let visible = checkVisible(el); - if (!visible) { - throw new ElementNotVisibleError("Element is not visible"); - } - return accessibility.getAccessibleObject(el, true).then(acc => { - checkVisibleAccessibility(acc, el, visible); - if (utils.isElementEnabled(el)) { - checkEnabledAccessibility(acc, el, true); - checkActionableAccessibility(acc, el); - let rects = el.getClientRects(); - utils.synthesizeMouseAtPoint(rects[0].left + rects[0].width/2, - rects[0].top + rects[0].height/2, - {}, el.ownerDocument.defaultView); - } else { - throw new InvalidElementStateError("Element is not Enabled"); - } - }); + return interactions.clickElement(curContainer, elementManager, id); } /** @@ -1531,12 +1416,7 @@ function getElementTagName(id) { * capability. */ function isElementDisplayed(id) { - let el = elementManager.getKnownElement(id, curContainer); - let displayed = utils.isElementDisplayed(el); - return accessibility.getAccessibleObject(el).then(acc => { - checkVisibleAccessibility(acc, el, displayed); - return displayed; - }); + return interactions.isElementDisplayed(curContainer, elementManager, id); } /** @@ -1587,12 +1467,7 @@ function getElementRect(id) { * True if enabled, false otherwise. */ function isElementEnabled(id) { - let el = elementManager.getKnownElement(id, curContainer); - let enabled = utils.isElementEnabled(el); - return accessibility.getAccessibleObject(el).then(acc => { - checkEnabledAccessibility(acc, el, enabled); - return enabled; - }); + return interactions.isElementEnabled(curContainer, elementManager, id); } /** @@ -1602,12 +1477,7 @@ function isElementEnabled(id) { * and Radio Button states, or option elements. */ function isElementSelected(id) { - let el = elementManager.getKnownElement(id, curContainer); - let selected = utils.isElementSelected(el); - return accessibility.getAccessibleObject(el).then(acc => { - checkSelectedAccessibility(acc, el, selected); - return selected; - }); + return interactions.isElementSelected(curContainer, elementManager, id); } /** @@ -1616,28 +1486,22 @@ function isElementSelected(id) { function sendKeysToElement(msg) { let command_id = msg.json.command_id; let val = msg.json.value; - let el; + let id = msg.json.id; + let el = elementManager.getKnownElement(id, curContainer); - return Promise.resolve(elementManager.getKnownElement(msg.json.id, curContainer)) - .then(knownEl => { - el = knownEl; - // Element should be actionable from the accessibility standpoint to be able - // to send keys to it. - return accessibility.getAccessibleObject(el, true) - }).then(acc => { - checkActionableAccessibility(acc, el); - if (el.type == "file") { - let p = val.join(""); + if (el.type == "file") { + let p = val.join(""); fileInputElement = el; // In e10s, we can only construct File objects in the parent process, // so pass the filename to driver.js, which in turn passes them back // to this frame script in receiveFiles. sendSyncMessage("Marionette:getFiles", {value: p, command_id: command_id}); - } else { - utils.sendKeysToElement(curContainer.frame, el, val, sendOk, sendError, command_id); - } - }).catch(e => sendError(e, command_id)); + } else { + interactions.sendKeysToElement(curContainer, elementManager, id, val) + .then(() => sendOk(command_id)) + .catch(e => sendError(e, command_id)); + } } /** diff --git a/testing/marionette/sendkeys.js b/testing/marionette/sendkeys.js index 163a091a9b60..2475fd7839aa 100644 --- a/testing/marionette/sendkeys.js +++ b/testing/marionette/sendkeys.js @@ -144,7 +144,7 @@ function focusElement(el) { el.focus(); } -function sendKeysToElement(document, element, keysToSend, successCallback, errorCallback, command_id, ignoreVisibility) { +function sendKeysToElement(document, element, keysToSend, ignoreVisibility) { if (ignoreVisibility || checkVisible(element)) { focusElement(element); @@ -159,9 +159,7 @@ function sendKeysToElement(document, element, keysToSend, successCallback, error var c = value.charAt(i); sendSingleKey(c, modifiers, document); } - - successCallback(command_id); } else { - errorCallback(new ElementNotVisibleError("Element is not visible"), command_id); + throw new ElementNotVisibleError("Element is not visible"); } }; From 2ae35fec9a339aeb655071715229af5381dbdf42 Mon Sep 17 00:00:00 2001 From: Thomas Kuyper Date: Thu, 14 Jan 2016 08:57:00 -0500 Subject: [PATCH 03/41] Bug 1184550 - Move the check for bodyUsed before the check for a null body so subsequent fetches with the same Request fail. r=bkelly --- dom/fetch/Request.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/fetch/Request.cpp b/dom/fetch/Request.cpp index 36eea47eae4e..a6e874fdf00b 100644 --- a/dom/fetch/Request.cpp +++ b/dom/fetch/Request.cpp @@ -231,11 +231,11 @@ Request::Constructor(const GlobalObject& aGlobal, RefPtr inputReq = &aInput.GetAsRequest(); nsCOMPtr body; inputReq->GetBody(getter_AddRefs(body)); + if (inputReq->BodyUsed()) { + aRv.ThrowTypeError(); + return nullptr; + } if (body) { - if (inputReq->BodyUsed()) { - aRv.ThrowTypeError(); - return nullptr; - } temporaryBody = body; } From 5244c3a2d48632a9dba7b32bddb8e1db933d320d Mon Sep 17 00:00:00 2001 From: Thomas Kuyper Date: Thu, 14 Jan 2016 08:59:00 -0500 Subject: [PATCH 04/41] Bug 1184550 - Add a mochitest for the Request constructor that tests that the body is set to used after being fetched and then fails on the second fetch with the same Request. r=bkelly --- dom/tests/mochitest/fetch/test_request.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dom/tests/mochitest/fetch/test_request.js b/dom/tests/mochitest/fetch/test_request.js index ec88a752f3a3..e7202caefecb 100644 --- a/dom/tests/mochitest/fetch/test_request.js +++ b/dom/tests/mochitest/fetch/test_request.js @@ -124,6 +124,19 @@ function testBug1109574() { var r3 = new Request(r1); } +// Bug 1184550 - Request constructor should always throw if used flag is set, +// even if body is null +function testBug1184550() { + var req = new Request("", { method: 'post', body: "Test" }); + fetch(req); + ok(req.bodyUsed, "Request body should be used immediately after fetch()"); + return fetch(req).then(function(resp) { + ok(false, "Second fetch with same request should fail."); + }).catch(function(err) { + is(err.name, 'TypeError', "Second fetch with same request should fail."); + }); +} + function testHeaderGuard() { var headers = { "Cookie": "Custom cookie", @@ -500,6 +513,7 @@ function runTest() { testUrlMalformed(); testMethod(); testBug1109574(); + testBug1184550(); testHeaderGuard(); testModeCorsPreflightEnumValue(); testBug1154268(); From 2959708ab538c06b33789fad1d11f95a284382d6 Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Mon, 11 Jan 2016 07:40:00 -0500 Subject: [PATCH 05/41] Bug 1234548 - Don't send network change events if routes are changed. r=mcmanus, r=bagder --- .../linux/nsNotifyAddrListener_Linux.cpp | 195 +++++++----------- 1 file changed, 71 insertions(+), 124 deletions(-) diff --git a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp index 17c137063290..8254a1c8a026 100644 --- a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp +++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp @@ -181,131 +181,79 @@ void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket) break; } - switch(nlh->nlmsg_type) { - case RTM_DELROUTE: - LOG(("nsNotifyAddrListener::OnNetlinkMessage deleted route")); - case RTM_NEWROUTE: - LOG(("nsNotifyAddrListener::OnNetlinkMessage new/deleted route")); - // Get the route data - route_entry = static_cast(NLMSG_DATA(nlh)); + LOG(("nsNotifyAddrListener::OnNetlinkMessage: new/deleted address\n")); + newifam = reinterpret_cast(NLMSG_DATA(nlh)); - // We are just intrested in main routing table - if (route_entry->rtm_table != RT_TABLE_MAIN) - continue; - - if ((route_entry->rtm_family != AF_INET) && - (route_entry->rtm_family != AF_INET6)) { - continue; - } - - attr = (struct rtattr *) RTM_RTA(route_entry); - attr_len = RTM_PAYLOAD(nlh); - link_local = false; - - /* Loop through all attributes */ - for ( ; RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) { - if (attr->rta_type == RTA_GATEWAY) { - if (route_entry->rtm_family == AF_INET6) { - unsigned char *g = (unsigned char *) - RTA_DATA(attr); - if ((g[0] == 0xFE) && ((g[1] & 0xc0) == 0x80)) { - link_local = true; - break; - } - } - } - } - - if (!link_local) { - LOG(("OnNetlinkMessage: route update\n")); - networkChange = true; - } else { - LOG(("OnNetlinkMessage: ignored link-local route update\n")); - } - break; - - case RTM_DELADDR: - LOG(("nsNotifyAddrListener::OnNetlinkMessage deleted address")); - case RTM_NEWADDR: - LOG(("nsNotifyAddrListener::OnNetlinkMessage: new/deleted address" - "\n")); - newifam = reinterpret_cast(NLMSG_DATA(nlh)); - - if ((newifam->ifa_family != AF_INET) && - (newifam->ifa_family != AF_INET6)) { - continue; - } - - attr = IFA_RTA (newifam); - attr_len = IFA_PAYLOAD (nlh); - for (;attr_len && RTA_OK (attr, attr_len); - attr = RTA_NEXT (attr, attr_len)) { - if (attr->rta_type == IFA_ADDRESS) { - if (newifam->ifa_family == AF_INET) { - struct in_addr* in = (struct in_addr*)RTA_DATA(attr); - addr = (char*)malloc(INET_ADDRSTRLEN); - inet_ntop(AF_INET, in, addr.get(), INET_ADDRSTRLEN); - } else { - struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr); - addr = (char*)malloc(INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, in, addr.get(), INET6_ADDRSTRLEN); - } - } else if (attr->rta_type == IFA_LOCAL) { - if (newifam->ifa_family == AF_INET) { - struct in_addr* in = (struct in_addr*)RTA_DATA(attr); - localaddr = (char*)malloc(INET_ADDRSTRLEN); - inet_ntop(AF_INET, in, localaddr.get(), INET_ADDRSTRLEN); - } else { - struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr); - localaddr = (char*)malloc(INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, in, localaddr.get(), INET6_ADDRSTRLEN); - } - } - } - if (localaddr) { - addr = localaddr; - } - if (!addr) { - continue; - } - if (nlh->nlmsg_type == RTM_NEWADDR) { - LOG(("nsNotifyAddrListener::OnNetlinkMessage: a new address " - "- %s.", addr.get())); - struct ifaddrmsg* ifam; - nsCString addrStr; - addrStr.Assign(addr); - if (mAddressInfo.Get(addrStr, &ifam)) { - LOG(("nsNotifyAddrListener::OnNetlinkMessage: the address " - "already known.")); - if (memcmp(ifam, newifam, sizeof(struct ifaddrmsg))) { - LOG(("nsNotifyAddrListener::OnNetlinkMessage: but " - "the address info has been changed.")); - networkChange = true; - memcpy(ifam, newifam, sizeof(struct ifaddrmsg)); - } - } else { - networkChange = true; - ifam = (struct ifaddrmsg*)malloc(sizeof(struct ifaddrmsg)); - memcpy(ifam, newifam, sizeof(struct ifaddrmsg)); - mAddressInfo.Put(addrStr,ifam); - } - } else { - LOG(("nsNotifyAddrListener::OnNetlinkMessage: an address " - "has been deleted - %s.", addr.get())); - networkChange = true; - nsCString addrStr; - addrStr.Assign(addr); - mAddressInfo.Remove(addrStr); - } - - // clean it up. - localaddr = nullptr; - addr = nullptr; - break; - - default: + if ((newifam->ifa_family != AF_INET) && + (newifam->ifa_family != AF_INET6)) { continue; } + + attr = IFA_RTA (newifam); + attr_len = IFA_PAYLOAD (nlh); + for (;attr_len && RTA_OK (attr, attr_len); + attr = RTA_NEXT (attr, attr_len)) { + if (attr->rta_type == IFA_ADDRESS) { + if (newifam->ifa_family == AF_INET) { + struct in_addr* in = (struct in_addr*)RTA_DATA(attr); + addr = (char*)malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, in, addr.get(), INET_ADDRSTRLEN); + } else { + struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr); + addr = (char*)malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, in, addr.get(), INET6_ADDRSTRLEN); + } + } else if (attr->rta_type == IFA_LOCAL) { + if (newifam->ifa_family == AF_INET) { + struct in_addr* in = (struct in_addr*)RTA_DATA(attr); + localaddr = (char*)malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, in, localaddr.get(), INET_ADDRSTRLEN); + } else { + struct in6_addr* in = (struct in6_addr*)RTA_DATA(attr); + localaddr = (char*)malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, in, localaddr.get(), INET6_ADDRSTRLEN); + } + } + } + if (localaddr) { + addr = localaddr; + } + if (!addr) { + continue; + } + if (nlh->nlmsg_type == RTM_NEWADDR) { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: a new address " + "- %s.", addr.get())); + struct ifaddrmsg* ifam; + nsCString addrStr; + addrStr.Assign(addr); + if (mAddressInfo.Get(addrStr, &ifam)) { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: the address " + "already known.")); + if (memcmp(ifam, newifam, sizeof(struct ifaddrmsg))) { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: but " + "the address info has been changed.")); + networkChange = true; + memcpy(ifam, newifam, sizeof(struct ifaddrmsg)); + } + } else { + networkChange = true; + ifam = (struct ifaddrmsg*)malloc(sizeof(struct ifaddrmsg)); + memcpy(ifam, newifam, sizeof(struct ifaddrmsg)); + mAddressInfo.Put(addrStr,ifam); + } + } else { + LOG(("nsNotifyAddrListener::OnNetlinkMessage: an address " + "has been deleted - %s.", addr.get())); + networkChange = true; + nsCString addrStr; + addrStr.Assign(addr); + mAddressInfo.Remove(addrStr); + } + + // clean it up. + localaddr = nullptr; + addr = nullptr; } if (networkChange && mAllowChangedEvent) { @@ -329,8 +277,7 @@ nsNotifyAddrListener::Run() memset(&addr, 0, sizeof(addr)); // clear addr addr.nl_family = AF_NETLINK; - addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR | - RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE; + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; if (bind(netlinkSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { // failure! From 4c0263a3ede747fed58fa55b1b44986b28beb697 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Sat, 16 Jan 2016 19:02:00 -0500 Subject: [PATCH 06/41] Bug 1096774 - Part 1: Implement Animation Constructor. r=birtles, r=smaug --- dom/animation/Animation.cpp | 26 ++++++++++++++++++++++++++ dom/animation/Animation.h | 5 +++++ dom/webidl/Animation.webidl | 4 +++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index 379e26a58240..8725b5e18495 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -83,6 +83,32 @@ namespace { // Animation interface: // // --------------------------------------------------------------------------- +/* static */ already_AddRefed +Animation::Constructor(const GlobalObject& aGlobal, + KeyframeEffectReadOnly* aEffect, + AnimationTimeline* aTimeline, + ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + RefPtr animation = new Animation(global); + + if (!aTimeline) { + // Bug 1096776: We do not support null timeline yet. + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (!aEffect) { + // Bug 1049975: We do not support null effect yet. + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + animation->SetTimeline(aTimeline); + animation->SetEffect(aEffect); + + return animation.forget(); +} + void Animation::SetId(const nsAString& aId) { diff --git a/dom/animation/Animation.h b/dom/animation/Animation.h index b2bf52e9c93f..d6877fc93f1d 100644 --- a/dom/animation/Animation.h +++ b/dom/animation/Animation.h @@ -90,6 +90,11 @@ public: }; // Animation interface methods + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + KeyframeEffectReadOnly* aEffect, + AnimationTimeline* aTimeline, + ErrorResult& aRv); void GetId(nsAString& aResult) const { aResult = mId; } void SetId(const nsAString& aId); KeyframeEffectReadOnly* GetEffect() const { return mEffect; } diff --git a/dom/webidl/Animation.webidl b/dom/webidl/Animation.webidl index afe5dc41a7ab..7264db154963 100644 --- a/dom/webidl/Animation.webidl +++ b/dom/webidl/Animation.webidl @@ -12,7 +12,9 @@ enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" }; -[Func="nsDocument::IsWebAnimationsEnabled"] +[Func="nsDocument::IsWebAnimationsEnabled", + Constructor (optional KeyframeEffectReadOnly? effect = null, + optional AnimationTimeline? timeline = null)] interface Animation : EventTarget { attribute DOMString id; // Bug 1049975: Make 'effect' writeable From 54e83307e389dab69dbd1c34c039eec7f02b2c84 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Thu, 14 Jan 2016 19:27:00 -0500 Subject: [PATCH 07/41] Bug 1096774 - Part 2: Fix crash if animation has no timeline. r=birtles --- dom/animation/PendingAnimationTracker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/animation/PendingAnimationTracker.cpp b/dom/animation/PendingAnimationTracker.cpp index 75033ec552b0..a97814a7c088 100644 --- a/dom/animation/PendingAnimationTracker.cpp +++ b/dom/animation/PendingAnimationTracker.cpp @@ -63,6 +63,7 @@ PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(const TimeStamp& // itself on the next tick where it has a timeline. if (!timeline) { iter.Remove(); + continue; } // When the timeline's refresh driver is under test control, its values From d748989a1981573dbcabdd9f51347f3c47bfb5c9 Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Sat, 16 Jan 2016 19:02:00 -0500 Subject: [PATCH 08/41] Bug 1096774 - Part 3: Tests for Animation Contructor. r=birtles --- testing/web-platform/meta/MANIFEST.json | 4 ++ .../animation/constructor.html.ini | 13 ++++ .../web-animations/animation/constructor.html | 60 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 testing/web-platform/meta/web-animations/animation/constructor.html.ini create mode 100644 testing/web-platform/tests/web-animations/animation/constructor.html diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 9dbcc89be0a0..da12c1ceeaef 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -26579,6 +26579,10 @@ "path": "vibration/silent-ignore.html", "url": "/vibration/silent-ignore.html" }, + { + "path": "web-animations/animation/constructor.html", + "url": "/web-animations/animation/constructor.html" + }, { "path": "web-animations/animation-node/animation-node-after.html", "url": "/web-animations/animation-node/animation-node-after.html" diff --git a/testing/web-platform/meta/web-animations/animation/constructor.html.ini b/testing/web-platform/meta/web-animations/animation/constructor.html.ini new file mode 100644 index 000000000000..7ad2742a8e86 --- /dev/null +++ b/testing/web-platform/meta/web-animations/animation/constructor.html.ini @@ -0,0 +1,13 @@ +[constructor.html] + type: testharness + [Animation can be constructed with null effect and null timeline] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1049975 + + [Animation can be constructed with null effect and non-null timeline] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1049975 + + [Animation can be constructed with non-null effect and null timeline] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1096776 diff --git a/testing/web-platform/tests/web-animations/animation/constructor.html b/testing/web-platform/tests/web-animations/animation/constructor.html new file mode 100644 index 000000000000..dcb09a21360e --- /dev/null +++ b/testing/web-platform/tests/web-animations/animation/constructor.html @@ -0,0 +1,60 @@ + + +Animation constructor tests + + + + + + + +
+
+ + From 874fe574037a78d965a36f48435562db8c229340 Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Sat, 16 Jan 2016 01:19:00 -0500 Subject: [PATCH 09/41] Bug 1240269 - Do not open UDP socket during shutdown. r=mcmanus --- netwerk/base/nsIOService.h | 4 ++-- netwerk/base/nsUDPSocket.cpp | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/netwerk/base/nsIOService.h b/netwerk/base/nsIOService.h index 0838570cb516..75cdcb35d4c2 100644 --- a/netwerk/base/nsIOService.h +++ b/netwerk/base/nsIOService.h @@ -138,7 +138,7 @@ private: private: bool mOffline; - bool mOfflineForProfileChange; + mozilla::Atomic mOfflineForProfileChange; bool mManageLinkStatus; bool mConnectivity; // If true, the connectivity state will be mirrored by IOService.offline @@ -150,7 +150,7 @@ private: bool mSettingOffline; bool mSetOfflineValue; - bool mShutdown; + mozilla::Atomic mShutdown; nsCOMPtr mSocketTransportService; nsCOMPtr mDNSService; diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp index cff5d2b7a433..03e9d5e325fe 100644 --- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -16,6 +16,7 @@ #include "nsError.h" #include "nsNetCID.h" #include "nsNetUtil.h" +#include "nsIOService.h" #include "prnetdb.h" #include "prio.h" #include "nsNetAddr.h" @@ -332,6 +333,10 @@ nsUDPSocket::TryAttach() if (!gSocketTransportService) return NS_ERROR_FAILURE; + if (gIOService->IsNetTearingDown()) { + return NS_ERROR_FAILURE; + } + // // find out if it is going to be ok to attach another socket to the STS. // if not then we have to wait for the STS to tell us that it is ok. @@ -581,6 +586,10 @@ nsUDPSocket::InitWithAddress(const NetAddr *aAddr, nsIPrincipal *aPrincipal, { NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); + if (gIOService->IsNetTearingDown()) { + return NS_ERROR_FAILURE; + } + bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true; // From 20da6fc699f81d90604fc9ef0947f94bd208e1f3 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Sun, 17 Jan 2016 13:07:38 -0500 Subject: [PATCH 10/41] Bug 1234548 - Remove unused variables. r=bustage --- netwerk/system/linux/nsNotifyAddrListener_Linux.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp index 8254a1c8a026..23d4f5edbb9a 100644 --- a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp +++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp @@ -149,7 +149,6 @@ void nsNotifyAddrListener::checkLink(void) void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket) { struct nlmsghdr *nlh; - struct rtmsg *route_entry; // The buffer size below, (4095) was chosen partly based on testing and // partly on existing sample source code using this size. It needs to be @@ -158,7 +157,6 @@ void nsNotifyAddrListener::OnNetlinkMessage(int aNetlinkSocket) struct rtattr *attr; int attr_len; const struct ifaddrmsg* newifam; - bool link_local; // inspired by check_pf.c. nsAutoPtr addr; From 737f45f2c0dd4c1a15788132c81a33f44ad2834a Mon Sep 17 00:00:00 2001 From: Dragana Damjanovic Date: Fri, 8 Jan 2016 10:20:00 -0500 Subject: [PATCH 11/41] Bug 1238017 - Remove ClosingService. r=mcmanus --- netwerk/base/ClosingService.cpp | 308 ------------------ netwerk/base/ClosingService.h | 71 ---- netwerk/base/moz.build | 1 - netwerk/base/nsIOService.cpp | 9 - netwerk/base/nsIncrementalDownload.cpp | 5 +- netwerk/base/nsSocketTransport2.cpp | 4 - netwerk/base/nsUDPSocket.cpp | 2 - netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp | 4 - netwerk/protocol/rtsp/rtsp/ARTPSession.cpp | 2 - netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp | 2 - .../protocol/rtsp/rtsp/ARTSPConnection.cpp | 2 - netwerk/protocol/rtsp/rtsp/UDPPusher.cpp | 2 - 12 files changed, 3 insertions(+), 409 deletions(-) delete mode 100644 netwerk/base/ClosingService.cpp delete mode 100644 netwerk/base/ClosingService.h diff --git a/netwerk/base/ClosingService.cpp b/netwerk/base/ClosingService.cpp deleted file mode 100644 index 61f9df1549bf..000000000000 --- a/netwerk/base/ClosingService.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "ClosingService.h" -#include "nsIOService.h" -#ifdef MOZ_NUWA_PROCESS -#include "ipc/Nuwa.h" -#endif - -class ClosingLayerSecret -{ -public: - explicit ClosingLayerSecret(mozilla::net::ClosingService *aClosingService) - : mClosingService(aClosingService) - { - } - - ~ClosingLayerSecret() - { - mClosingService = nullptr; - } - - RefPtr mClosingService; -}; - -namespace mozilla { -namespace net { - -static PRIOMethods sTcpUdpPRCloseLayerMethods; -static PRIOMethods *sTcpUdpPRCloseLayerMethodsPtr = nullptr; -static PRDescIdentity sTcpUdpPRCloseLayerId; - -static PRStatus -TcpUdpPRCloseLayerClose(PRFileDesc *aFd) -{ - if (!aFd) { - return PR_FAILURE; - } - - PRFileDesc* layer = PR_PopIOLayer(aFd, PR_TOP_IO_LAYER); - MOZ_RELEASE_ASSERT(layer && - layer->identity == sTcpUdpPRCloseLayerId, - "Closing Layer not on top of stack"); - - ClosingLayerSecret *closingLayerSecret = - reinterpret_cast(layer->secret); - - PRStatus status = PR_SUCCESS; - - if (aFd) { - // If this is called during shutdown do not call ..method->close(fd) and - // let it leak. - if (gIOService->IsNetTearingDown()) { - // If the ClosingService layer is the first layer above PR_NSPR_IO_LAYER - // we are not going to leak anything, but the PR_Close will not be called. - PR_Free(aFd); - } else if (closingLayerSecret->mClosingService) { - closingLayerSecret->mClosingService->PostRequest(aFd); - } else { - // Socket is created before closing service has been started or there was - // a problem with starting it. - PR_Close(aFd); - } - } - - layer->secret = nullptr; - layer->dtor(layer); - delete closingLayerSecret; - return status; -} - -ClosingService* ClosingService::sInstance = nullptr; - -ClosingService::ClosingService() - : mShutdown(false) - , mMonitor("ClosingService.mMonitor") -{ - MOZ_ASSERT(!sInstance, - "multiple ClosingService instances!"); -} - -// static -void -ClosingService::Start() -{ - if (!sTcpUdpPRCloseLayerMethodsPtr) { - sTcpUdpPRCloseLayerId = PR_GetUniqueIdentity("TCP and UDP PRClose layer"); - PR_ASSERT(PR_INVALID_IO_LAYER != sTcpUdpPRCloseLayerId); - - sTcpUdpPRCloseLayerMethods = *PR_GetDefaultIOMethods(); - sTcpUdpPRCloseLayerMethods.close = TcpUdpPRCloseLayerClose; - sTcpUdpPRCloseLayerMethodsPtr = &sTcpUdpPRCloseLayerMethods; - } - - if (!sInstance) { - ClosingService* service = new ClosingService(); - if (NS_SUCCEEDED(service->StartInternal())) { - NS_ADDREF(service); - sInstance = service; - } else { - delete service; - } - } -} - -nsresult -ClosingService::StartInternal() -{ - mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, this, - PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, - PR_JOINABLE_THREAD, 32 * 1024); - if (!mThread) { - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -// static -nsresult -ClosingService::AttachIOLayer(PRFileDesc *aFd) -{ - // We are going to remove ClosingService soon. - // This change is going to turn it off, so ClosingService is not used. - // Bug 1238010. - return NS_OK; - - if (!sTcpUdpPRCloseLayerMethodsPtr) { - return NS_OK; - } - - PRFileDesc * layer; - PRStatus status; - - layer = PR_CreateIOLayerStub(sTcpUdpPRCloseLayerId, - sTcpUdpPRCloseLayerMethodsPtr); - - if (!layer) { - return NS_OK; - } - - ClosingLayerSecret *secret = new ClosingLayerSecret(sInstance); - layer->secret = reinterpret_cast(secret); - - status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer); - - if (status == PR_FAILURE) { - delete secret; - PR_DELETE(layer); - } - return NS_OK; -} - -void -ClosingService::PostRequest(PRFileDesc *aFd) -{ - mozilla::MonitorAutoLock mon(mMonitor); - - // Check if shutdown is called. - if (mShutdown) { - // Let the socket leak. We are in shutdown and some PRClose can take a long - // time. To prevent shutdown crash (bug 1152046) do not accept sockets any - // more. - // If the ClosingService layer is the first layer above PR_NSPR_IO_LAYER - // we are not going to leak anything, but PR_Close will not be called. - PR_Free(aFd); - return; - } - - mQueue.AppendElement(aFd); - if (mQueue.Length() == 1) { - mon.Notify(); - } -} - -// static -void -ClosingService::Shutdown() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (sInstance) { - sInstance->ShutdownInternal(); - NS_RELEASE(sInstance); - } -} - -void -ClosingService::ShutdownInternal() -{ - { - mozilla::MonitorAutoLock mon(mMonitor); - if (mShutdown) { - // This should not happen. - return; - } - - mShutdown = true; - // If it is waiting on the empty queue, wake it up. - if (mQueue.Length() == 0) { - mon.Notify(); - } - } - - if (mThread) { - PR_JoinThread(mThread); - mThread = nullptr; - } -} - -void -ClosingService::ThreadFunc() -{ - PR_SetCurrentThreadName("Closing Service"); -#ifdef MOZ_NUWA_PROCESS - if (IsNuwaProcess()) { - NuwaMarkCurrentThread(nullptr, nullptr); - } -#endif - - for (;;) { - PRFileDesc *fd; - { - mozilla::MonitorAutoLock mon(mMonitor); - while (!mShutdown && (mQueue.Length() == 0)) { - mon.Wait(); - } - - if (mShutdown) { - // If we are in shutdown leak the rest of the sockets. - for (uint32_t i = 0; i < mQueue.Length(); i++) { - fd = mQueue[i]; - // If the ClosingService layer is the first layer above - // PR_NSPR_IO_LAYER we are not going to leak anything, but PR_Close - // will not be called. - PR_Free(fd); - } - mQueue.Clear(); - return; - } - - fd = mQueue[0]; - mQueue.RemoveElementAt(0); - } - // Leave lock before closing socket. It can block for a long time and in - // case we accidentally attach this layer twice this would cause deadlock. - - bool tcp = (PR_GetDescType(PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER)) == - PR_DESC_SOCKET_TCP); - - PRIntervalTime closeStarted = PR_IntervalNow(); - fd->methods->close(fd); - - // Post telemetry. - if (tcp) { - SendPRCloseTelemetry(closeStarted, - Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL, - Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN, - Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE, - Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE, - Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE); - } else { - SendPRCloseTelemetry(closeStarted, - Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL, - Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN, - Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE, - Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE, - Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE); - } - } -} - -void -ClosingService::SendPRCloseTelemetry(PRIntervalTime aStart, - mozilla::Telemetry::ID aIDNormal, - mozilla::Telemetry::ID aIDShutdown, - mozilla::Telemetry::ID aIDConnectivityChange, - mozilla::Telemetry::ID aIDLinkChange, - mozilla::Telemetry::ID aIDOffline) -{ - PRIntervalTime now = PR_IntervalNow(); - if (gIOService->IsNetTearingDown()) { - Telemetry::Accumulate(aIDShutdown, - PR_IntervalToMilliseconds(now - aStart)); - - } else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange()) - < 60) { - Telemetry::Accumulate(aIDConnectivityChange, - PR_IntervalToMilliseconds(now - aStart)); - } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange()) - < 60) { - Telemetry::Accumulate(aIDLinkChange, - PR_IntervalToMilliseconds(now - aStart)); - - } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange()) - < 60) { - Telemetry::Accumulate(aIDOffline, - PR_IntervalToMilliseconds(now - aStart)); - } else { - Telemetry::Accumulate(aIDNormal, - PR_IntervalToMilliseconds(now - aStart)); - } -} - -} //namwspacw mozilla -} //namespace net diff --git a/netwerk/base/ClosingService.h b/netwerk/base/ClosingService.h deleted file mode 100644 index 3e7f941230bc..000000000000 --- a/netwerk/base/ClosingService.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef ClosingService_h__ -#define ClosingService_h__ - -#include "nsTArray.h" -#include "nspr.h" -#include "mozilla/Telemetry.h" -#include "mozilla/Monitor.h" - -//----------------------------------------------------------------------------- -// ClosingService -//----------------------------------------------------------------------------- - -// A helper class carrying call to PR_Close on FD to a separate thread - -// closingThread. This may be a workaround for shutdown blocks that are caused -// by serial calls to close on UDP and TCP sockets. -// This service is started by nsIOService and also the class adds itself as an -// observer to "xpcom-shutdown-threads" notification where we join the thread -// and remove reference. -// During worktime of the thread the class is also self-referenced, -// since observer service might throw the reference away sooner than the thread -// is actually done. - -namespace mozilla { -namespace net { - -class ClosingService final -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ClosingService) - - ClosingService(); - - // Attaching this layer on tcp or udp sockets PRClose will be send to the - // closingThread. - static nsresult AttachIOLayer(PRFileDesc *aFd); - static void Start(); - static void Shutdown(); - void PostRequest(PRFileDesc *aFd); - -private: - ~ClosingService() {} - nsresult StartInternal(); - void ShutdownInternal(); - void ThreadFunc(); - static void ThreadFunc(void *aClosure) - { static_cast(aClosure)->ThreadFunc(); } - - void SendPRCloseTelemetry(PRIntervalTime aStart, - mozilla::Telemetry::ID aIDNormal, - mozilla::Telemetry::ID aIDShutdown, - mozilla::Telemetry::ID aIDConnectivityChange, - mozilla::Telemetry::ID aIDLinkChange, - mozilla::Telemetry::ID aIDOffline); - - static ClosingService* sInstance; - Atomic mShutdown; - nsTArray mQueue; - mozilla::Monitor mMonitor; - PRThread *mThread; -}; - -} // namespace net -} // namespace mozilla - -#endif // ClosingService_h_ diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build index 3444092bd2f8..39d76323905e 100644 --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -193,7 +193,6 @@ UNIFIED_SOURCES += [ 'CaptivePortalService.cpp', 'ChannelDiverterChild.cpp', 'ChannelDiverterParent.cpp', - 'ClosingService.cpp', 'Dashboard.cpp', 'EventTokenBucket.cpp', 'LoadContextInfo.cpp', diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index bc616d71abad..a72e71579328 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -49,7 +49,6 @@ #include "mozilla/Telemetry.h" #include "mozilla/net/DNS.h" #include "CaptivePortalService.h" -#include "ClosingService.h" #include "ReferrerPolicy.h" #include "nsContentSecurityManager.h" #include "nsHttpHandler.h" @@ -65,7 +64,6 @@ using namespace mozilla; using mozilla::net::IsNeckoChild; -using mozilla::net::ClosingService; using mozilla::net::CaptivePortalService; using mozilla::net::gHttpHandler; @@ -260,10 +258,6 @@ nsIOService::Init() InitializeNetworkLinkService(); - // Start the closing service. Actual PR_Close() will be carried out on - // a separate "closing" thread. Start the closing servicee here since this - // point is executed only once per session. - ClosingService::Start(); SetOffline(false); return NS_OK; @@ -1077,9 +1071,6 @@ nsIOService::SetOffline(bool offline) DebugOnly rv = mSocketTransportService->Shutdown(); NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service shutdown failed"); } - if (mShutdown) { - ClosingService::Shutdown(); - } } mSettingOffline = false; diff --git a/netwerk/base/nsIncrementalDownload.cpp b/netwerk/base/nsIncrementalDownload.cpp index 554eeb1eef3b..42cd6faa5e0c 100644 --- a/netwerk/base/nsIncrementalDownload.cpp +++ b/netwerk/base/nsIncrementalDownload.cpp @@ -6,6 +6,7 @@ #include "mozilla/Attributes.h" #include "mozilla/UniquePtrExtensions.h" +#include "mozilla/UniquePtr.h" #include "nsIIncrementalDownload.h" #include "nsIRequestObserver.h" @@ -150,7 +151,7 @@ private: nsCOMPtr mDest; nsCOMPtr mChannel; nsCOMPtr mTimer; - UniquePtr mChunk; + mozilla::UniquePtr mChunk; int32_t mChunkLen; int32_t mChunkSize; int32_t mInterval; @@ -712,7 +713,7 @@ nsIncrementalDownload::OnStartRequest(nsIRequest *request, if (diff < int64_t(mChunkSize)) mChunkSize = uint32_t(diff); - mChunk = MakeUniqueFallible(mChunkSize); + mChunk = mozilla::MakeUniqueFallible(mChunkSize); if (!mChunk) rv = NS_ERROR_OUT_OF_MEMORY; diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp index 58720508119d..7199985b0953 100644 --- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -16,7 +16,6 @@ #include "nsProxyInfo.h" #include "nsNetCID.h" #include "nsNetUtil.h" -#include "ClosingService.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "plstr.h" @@ -1325,9 +1324,6 @@ nsSocketTransport::InitiateSocket() // Attach network activity monitor mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd); - // Attach closing service. - ClosingService::AttachIOLayer(fd); - PRStatus status; // Make the socket non-blocking... diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp index 03e9d5e325fe..375f00ff5b0b 100644 --- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -30,7 +30,6 @@ #include "nsIDNSRecord.h" #include "nsIDNSService.h" #include "nsICancelable.h" -#include "ClosingService.h" #ifdef MOZ_WIDGET_GONK #include "NetStatistics.h" @@ -666,7 +665,6 @@ nsUDPSocket::InitWithAddress(const NetAddr *aAddr, nsIPrincipal *aPrincipal, // create proxy via NetworkActivityMonitor NetworkActivityMonitor::AttachIOLayer(mFD); - ClosingService::AttachIOLayer(mFD); // wait until AsyncListen is called before polling the socket for // client connections. diff --git a/netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp b/netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp index 410954b60429..e4ce82ffb180 100644 --- a/netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp +++ b/netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp @@ -36,7 +36,6 @@ #include "prerr.h" #include "prerror.h" #include "NetworkActivityMonitor.h" -#include "ClosingService.h" using namespace mozilla::net; @@ -145,9 +144,6 @@ void ARTPConnection::MakePortPair( NetworkActivityMonitor::AttachIOLayer(*rtpSocket); NetworkActivityMonitor::AttachIOLayer(*rtcpSocket); - ClosingService::AttachIOLayer(*rtpSocket); - ClosingService::AttachIOLayer(*rtcpSocket); - // Reduce the chance of using duplicate port numbers. srand(time(NULL)); // rand() * 1000 may overflow int type, use long long. diff --git a/netwerk/protocol/rtsp/rtsp/ARTPSession.cpp b/netwerk/protocol/rtsp/rtsp/ARTPSession.cpp index 9af10065da91..b052adb464e8 100644 --- a/netwerk/protocol/rtsp/rtsp/ARTPSession.cpp +++ b/netwerk/protocol/rtsp/rtsp/ARTPSession.cpp @@ -34,7 +34,6 @@ #include "prnetdb.h" #include "prerr.h" #include "NetworkActivityMonitor.h" -#include "ClosingService.h" using namespace mozilla::net; @@ -110,7 +109,6 @@ void ARTPSession::MakeUDPSocket(PRFileDesc **s, unsigned port) { } NetworkActivityMonitor::AttachIOLayer(*s); - ClosingService::AttachIOLayer(*s); PRNetAddr addr; addr.inet.family = PR_AF_INET; diff --git a/netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp b/netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp index 62ec35ebab75..c075d90d6f51 100644 --- a/netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp +++ b/netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp @@ -31,7 +31,6 @@ #include #include -#include "ClosingService.h" #include "NetworkActivityMonitor.h" using namespace mozilla::net; @@ -65,7 +64,6 @@ ARTPWriter::ARTPWriter(int fd) } NetworkActivityMonitor::AttachIOLayer(mSocket); - ClosingService::AttachIOLayer(mSocket); mRTPAddr.inet.family = PR_AF_INET; diff --git a/netwerk/protocol/rtsp/rtsp/ARTSPConnection.cpp b/netwerk/protocol/rtsp/rtsp/ARTSPConnection.cpp index 3a5f84cb8c22..2a15973aa7da 100644 --- a/netwerk/protocol/rtsp/rtsp/ARTSPConnection.cpp +++ b/netwerk/protocol/rtsp/rtsp/ARTSPConnection.cpp @@ -34,7 +34,6 @@ #include "nsCOMPtr.h" #include "nsString.h" #include "nsNetCID.h" -#include "ClosingService.h" #include "nsIServiceManager.h" #include "nsICryptoHash.h" @@ -284,7 +283,6 @@ void ARTSPConnection::onConnect(const sp &msg) { } NetworkActivityMonitor::AttachIOLayer(mSocket); - ClosingService::AttachIOLayer(mSocket); MakeSocketBlocking(mSocket, false); diff --git a/netwerk/protocol/rtsp/rtsp/UDPPusher.cpp b/netwerk/protocol/rtsp/rtsp/UDPPusher.cpp index 4b29dec64e35..1f3350894c2b 100644 --- a/netwerk/protocol/rtsp/rtsp/UDPPusher.cpp +++ b/netwerk/protocol/rtsp/rtsp/UDPPusher.cpp @@ -27,7 +27,6 @@ #include "prnetdb.h" #include "prerr.h" #include "NetworkActivityMonitor.h" -#include "ClosingService.h" using namespace mozilla::net; @@ -45,7 +44,6 @@ UDPPusher::UDPPusher(const char *filename, unsigned port) } NetworkActivityMonitor::AttachIOLayer(mSocket); - ClosingService::AttachIOLayer(mSocket); PRNetAddr addr; addr.inet.family = PR_AF_INET; From 6abe3c833c8516ac0d8638c27d9e188913708564 Mon Sep 17 00:00:00 2001 From: Michael Froman Date: Thu, 14 Jan 2016 10:56:17 -0600 Subject: [PATCH 12/41] Bug 1166832 - Add test for canvas capture on multiple streams. r=bwc --HG-- extra : transplant_source : M%EF%1B%05%EC%E7%E7%D3%A0%3A%E3C4%17%C3%2B%8B%3D%2B%C3 extra : histedit_source : d018f58a2c171558932911bde42025b3184f5611 --- dom/canvas/test/captureStream_common.js | 1 + dom/media/tests/mochitest/mochitest.ini | 2 + ...tion_multiple_captureStream_canvas_2d.html | 100 ++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html diff --git a/dom/canvas/test/captureStream_common.js b/dom/canvas/test/captureStream_common.js index cd4133fa5def..e6ad14dd88b6 100644 --- a/dom/canvas/test/captureStream_common.js +++ b/dom/canvas/test/captureStream_common.js @@ -28,6 +28,7 @@ CaptureStreamTestHelper.prototype = { blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" }, green: { data: [0, 255, 0, 255], name: "green" }, red: { data: [255, 0, 0, 255], name: "red" }, + blue: { data: [0, 0, 255, 255], name: "blue"}, grey: { data: [128, 128, 128, 255], name: "grey" }, /* Default element size for createAndAppendElement() */ diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index d6b26e15fa88..cf8f39f00e0b 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -112,6 +112,8 @@ tags=capturestream skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_captureStream_canvas_2d.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) +[test_peerConnection_multiple_captureStream_canvas_2d.html] +skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_captureStream_canvas_webgl.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) # [test_peerConnection_certificates.html] # bug 1180968 diff --git a/dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html b/dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html new file mode 100644 index 000000000000..0a1c8feee1e7 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_multiple_captureStream_canvas_2d.html @@ -0,0 +1,100 @@ + + + + + + + +
+
+
+ + From 13a82c11bf77725ddd5c707a9fd9c3331ae6501a Mon Sep 17 00:00:00 2001 From: Michael Froman Date: Wed, 13 Jan 2016 20:59:52 -0600 Subject: [PATCH 13/41] Bug 1166832 - Add test to verify video (using capture stream) after renegotiation. r=bwc --HG-- extra : transplant_source : I%AB%28%AA%D6%91%8F%CA5%8A%D9%0C%F2%8A%CD%B8m%DE%D7%05 extra : histedit_source : 1d30b92a3c6d128c24cfbb1f8778c665a6bb9eca --- dom/media/tests/mochitest/mochitest.ini | 7 +- ...nection_verifyVideoAfterRenegotiation.html | 100 ++++++++++++++++++ .../src/media-conduit/VideoConduit.cpp | 10 ++ 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 dom/media/tests/mochitest/test_peerConnection_verifyVideoAfterRenegotiation.html diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index cf8f39f00e0b..f71c05f0d5df 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -209,10 +209,11 @@ skip-if = toolkit == 'gonk' || android_version == '18' skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly [test_peerConnection_addDataChannelNoBundle.html] skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # b2g(emulator seems to be so slow that DTLS cannot establish properly), android(bug 1240256, intermittent ICE failures starting w/bug 1232082, possibly from timeout) - - # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) - +[test_peerConnection_verifyVideoAfterRenegotiation.html] +# B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator) +skip-if = toolkit == 'gonk' || android_version == '18' [test_peerConnection_webAudio.html] +# b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) tags = webaudio webrtc skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) [test_peerConnection_localRollback.html] diff --git a/dom/media/tests/mochitest/test_peerConnection_verifyVideoAfterRenegotiation.html b/dom/media/tests/mochitest/test_peerConnection_verifyVideoAfterRenegotiation.html new file mode 100644 index 000000000000..96542d226fc8 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_verifyVideoAfterRenegotiation.html @@ -0,0 +1,100 @@ + + + + + + + +
+
+
+ + diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp index 03d4f09b9f90..4798eaa9267e 100755 --- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp +++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp @@ -1144,6 +1144,8 @@ WebrtcVideoConduit::SelectSendResolution(unsigned short width, bool changed = false; if (mSendingWidth != width || mSendingHeight != height) { + CSFLogDebug(logTag, "%s: resolution changing to %ux%u (from %ux%u)", + __FUNCTION__, width, height, mSendingWidth, mSendingHeight); // This will avoid us continually retrying this operation if it fails. // If the resolution changes, we'll try again. In the meantime, we'll // keep using the old size in the encoder. @@ -1155,6 +1157,8 @@ WebrtcVideoConduit::SelectSendResolution(unsigned short width, // uses mSendingWidth/Height unsigned int framerate = SelectSendFrameRate(mSendingFramerate); if (mSendingFramerate != framerate) { + CSFLogDebug(logTag, "%s: framerate changing to %u (from %u)", + __FUNCTION__, framerate, mSendingFramerate); mSendingFramerate = framerate; changed = true; } @@ -1221,6 +1225,10 @@ WebrtcVideoConduit::ReconfigureSendCodec(unsigned short width, CSFLogError(logTag, "%s: GetSendCodec failed, err %d", __FUNCTION__, err); return NS_ERROR_FAILURE; } + + CSFLogDebug(logTag, + "%s: Requesting resolution change to %ux%u (from %ux%u)", + __FUNCTION__, width, height, vie_codec.width, vie_codec.height); // Likely spurious unless there was some error, but rarely checked if (vie_codec.width != width || vie_codec.height != height || vie_codec.maxFramerate != mSendingFramerate) @@ -1399,6 +1407,8 @@ WebrtcVideoConduit::SendVideoFrame(webrtc::I420VideoFrame& frame) return kMediaConduitNoError; } if (frame.width() != mLastWidth || frame.height() != mLastHeight) { + CSFLogDebug(logTag, "%s: call SelectSendResolution with %ux%u", + __FUNCTION__, frame.width(), frame.height()); if (SelectSendResolution(frame.width(), frame.height(), &frame)) { // SelectSendResolution took ownership of the data in i420_frame. // Submit the frame after reconfig is done From 030aef859caca75f344f778ee89fc5a772e20849 Mon Sep 17 00:00:00 2001 From: Michael Froman Date: Wed, 13 Jan 2016 17:48:34 -0600 Subject: [PATCH 14/41] Bug 1166832 - Add test to verify audio (using AudioStreamAnalyser) after renegotiation. r=bwc --HG-- extra : transplant_source : %D1/%A9%C3%BBj%CF%28%E4AJ%86%40%F0%AB%2A%0B%DFR%40 extra : histedit_source : 92a5470e09a583ef5e0656d370edb7f0280817bf --- dom/media/tests/mochitest/mochitest.ini | 2 + ...nection_verifyAudioAfterRenegotiation.html | 123 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index f71c05f0d5df..0ed7228c1efc 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -209,6 +209,8 @@ skip-if = toolkit == 'gonk' || android_version == '18' skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly [test_peerConnection_addDataChannelNoBundle.html] skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # b2g(emulator seems to be so slow that DTLS cannot establish properly), android(bug 1240256, intermittent ICE failures starting w/bug 1232082, possibly from timeout) +[test_peerConnection_verifyAudioAfterRenegotiation.html] +skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_verifyVideoAfterRenegotiation.html] # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator) skip-if = toolkit == 'gonk' || android_version == '18' diff --git a/dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html b/dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html new file mode 100644 index 000000000000..c003a5e78cd5 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_verifyAudioAfterRenegotiation.html @@ -0,0 +1,123 @@ + + + + + + +
+
+
+ + From 0d1f6a74d46f25d95b1518b95cbd2a154bbe7efe Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Sun, 17 Jan 2016 13:37:07 -0500 Subject: [PATCH 15/41] No bug - Keep comment with the intended test. r=DONTBUILD --- dom/media/tests/mochitest/mochitest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 0ed7228c1efc..10fccd9d566f 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -208,6 +208,7 @@ skip-if = toolkit == 'gonk' || android_version == '18' [test_peerConnection_addDataChannel.html] skip-if = toolkit == 'gonk' # B2G emulator seems to be so slow that DTLS cannot establish properly [test_peerConnection_addDataChannelNoBundle.html] +# b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # b2g(emulator seems to be so slow that DTLS cannot establish properly), android(bug 1240256, intermittent ICE failures starting w/bug 1232082, possibly from timeout) [test_peerConnection_verifyAudioAfterRenegotiation.html] skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, android(Bug 1189784, timeouts on 4.3 emulator) @@ -215,7 +216,6 @@ skip-if = toolkit == 'gonk' || (android_version == '18' && debug) # B2G emulator # B2G emulator is too slow to finish a renegotiation test in under 5 minutes, Bug 1180000 for Linux debug e10s, android(Bug 1189784, timeouts on 4.3 emulator) skip-if = toolkit == 'gonk' || android_version == '18' [test_peerConnection_webAudio.html] -# b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) tags = webaudio webrtc skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) [test_peerConnection_localRollback.html] From 36a12be95a2a98ae52b964a586bdf56ea2f89fb7 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sun, 17 Jan 2016 19:44:05 +0100 Subject: [PATCH 16/41] Bug 1237754 part 1 - [css-grid][css-align] Make 'align-content:normal' behave as 'stretch' for Grid containers. r=dholbert Change due to CSSWG decision: https://lists.w3.org/Archives/Public/www-style/2016Jan/0031.html --- layout/generic/nsGridContainerFrame.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/layout/generic/nsGridContainerFrame.cpp b/layout/generic/nsGridContainerFrame.cpp index deaf71683f1f..cc881cf54fce 100644 --- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -2936,15 +2936,17 @@ nsGridContainerFrame::Tracks::AlignJustifyContent( const bool isAlign = mAxis == eLogicalAxisBlock; auto stylePos = aReflowState.mStylePosition; - const auto valueAndFallback = isAlign ? - stylePos->ComputedAlignContent() : - stylePos->ComputedJustifyContent(); + auto valueAndFallback = isAlign ? stylePos->ComputedAlignContent() : + stylePos->ComputedJustifyContent(); WritingMode wm = aReflowState.GetWritingMode(); bool overflowSafe; auto alignment = ::GetAlignJustifyValue(valueAndFallback, wm, isAlign, &overflowSafe); if (alignment == NS_STYLE_ALIGN_NORMAL) { - alignment = NS_STYLE_ALIGN_START; + MOZ_ASSERT(valueAndFallback == NS_STYLE_ALIGN_NORMAL, + "*-content:normal cannot be specified with explicit fallback"); + alignment = isAlign ? NS_STYLE_ALIGN_STRETCH : NS_STYLE_ALIGN_START; + valueAndFallback = alignment; // we may need a fallback for 'stretch' below } // Compute the free space and count auto-sized tracks. From eb0781b9b9a94f46c3c64799c029e6bae68d6a08 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sun, 17 Jan 2016 19:44:05 +0100 Subject: [PATCH 17/41] Bug 1237754 part 2 - [css-grid][css-align] Add reftest for 'align-content:normal'. --- layout/reftests/css-grid/grid-align-content-001-ref.html | 3 ++- layout/reftests/css-grid/grid-align-content-001.html | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/layout/reftests/css-grid/grid-align-content-001-ref.html b/layout/reftests/css-grid/grid-align-content-001-ref.html index b2a2656f8ced..d2e03fb9fa27 100644 --- a/layout/reftests/css-grid/grid-align-content-001-ref.html +++ b/layout/reftests/css-grid/grid-align-content-001-ref.html @@ -24,6 +24,7 @@ separator { clear:both; display:block; height:6px; } margin-right: 4px; width: 40px; height: 40px; + align-content: stretch; } item1,item2,item3 { @@ -71,7 +72,7 @@ item3 { grid-area: 3 / 3; } `; +function promisePopupShown(popup) { + return new Promise(resolve => { + if (popup.popupOpen) { + resolve(); + } else { + let onPopupShown = event => { + popup.removeEventListener("popupshown", onPopupShown); + resolve(); + }; + popup.addEventListener("popupshown", onPopupShown); + } + }); +} +add_task(function* testPageActionPopup() { let extension = ExtensionTestUtils.loadExtension({ manifest: { "background": { @@ -16,17 +28,17 @@ add_task(function* testPageActionPopup() { }, files: { - "popup-a.html": scriptPage("popup-a.js"), + "popup-a.html": ``, "popup-a.js": function() { browser.runtime.sendMessage("from-popup-a"); }, - "data/popup-b.html": scriptPage("popup-b.js"), + "data/popup-b.html": ``, "data/popup-b.js": function() { browser.runtime.sendMessage("from-popup-b"); }, - "data/background.html": scriptPage("background.js"), + "data/background.html": ``, "data/background.js": function() { let tabId; diff --git a/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js b/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js index e28c1ccb6709..a04e41a2346a 100644 --- a/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js +++ b/browser/components/extensions/test/browser/browser_ext_popup_api_injection.js @@ -42,6 +42,19 @@ add_task(function* testPageActionPopup() { }, }); + let browserActionId = makeWidgetId(extension.id) + "-browser-action"; + let pageActionId = makeWidgetId(extension.id) + "-page-action"; + + function openPopup(buttonId) { + let button = document.getElementById(buttonId); + if (buttonId == pageActionId) { + // TODO: I don't know why a proper synthesized event doesn't work here. + button.dispatchEvent(new MouseEvent("click", {})); + } else { + EventUtils.synthesizeMouseAtCenter(button, {}, window); + } + } + let promiseConsoleMessage = pattern => new Promise(resolve => { Services.console.registerListener(function listener(msg) { if (pattern.test(msg.message)) { @@ -59,25 +72,21 @@ add_task(function* testPageActionPopup() { // BrowserAction: let awaitMessage = promiseConsoleMessage(/WebExt Privilege Escalation: BrowserAction/); SimpleTest.expectUncaughtException(); - yield clickBrowserAction(extension); + openPopup(browserActionId); let message = yield awaitMessage; ok(message.includes("WebExt Privilege Escalation: BrowserAction: typeof(browser) = undefined"), `No BrowserAction API injection`); - yield closeBrowserAction(extension); - // PageAction awaitMessage = promiseConsoleMessage(/WebExt Privilege Escalation: PageAction/); SimpleTest.expectUncaughtException(); - yield clickPageAction(extension); + openPopup(pageActionId); message = yield awaitMessage; ok(message.includes("WebExt Privilege Escalation: PageAction: typeof(browser) = undefined"), `No PageAction API injection: ${message}`); - yield closePageAction(extension); - SimpleTest.expectUncaughtException(false); @@ -86,13 +95,12 @@ add_task(function* testPageActionPopup() { yield extension.awaitMessage("ok"); - yield clickBrowserAction(extension); + // Check that unprivileged documents don't get the API. + openPopup(browserActionId); yield extension.awaitMessage("from-popup-a"); - yield closeBrowserAction(extension); - yield clickPageAction(extension); + openPopup(pageActionId); yield extension.awaitMessage("from-popup-b"); - yield closePageAction(extension); yield extension.unload(); }); diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js index 4f30172cf4fb..b8f5d8fb5fe2 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js @@ -48,11 +48,6 @@ function* testHasPermission(params) { extension.sendMessage("execute-script"); yield extension.awaitFinish("executeScript"); - - if (params.tearDown) { - yield params.tearDown(extension); - } - yield extension.unload(); } @@ -87,7 +82,6 @@ add_task(function* testGoodPermissions() { return Promise.resolve(); }, setup: clickBrowserAction, - tearDown: closeBrowserAction, }); info("Test activeTab permission with a page action click"); @@ -105,7 +99,6 @@ add_task(function* testGoodPermissions() { }); }, setup: clickPageAction, - tearDown: closePageAction, }); info("Test activeTab permission with a browser action w/popup click"); @@ -115,7 +108,6 @@ add_task(function* testGoodPermissions() { "browser_action": { "default_popup": "_blank.html" }, }, setup: clickBrowserAction, - tearDown: closeBrowserAction, }); info("Test activeTab permission with a page action w/popup click"); @@ -133,7 +125,6 @@ add_task(function* testGoodPermissions() { }); }, setup: clickPageAction, - tearDown: closePageAction, }); info("Test activeTab permission with a context menu click"); diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js b/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js index 6894ccd834e1..7be509d24d9c 100644 --- a/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js @@ -63,7 +63,6 @@ add_task(function* () { clickBrowserAction(extension); yield extension.awaitMessage("popup-finished"); - yield closeBrowserAction(extension); yield extension.unload(); }); diff --git a/browser/components/extensions/test/browser/head.js b/browser/components/extensions/test/browser/head.js index a43e1fcf6b00..38abd72d60c0 100644 --- a/browser/components/extensions/test/browser/head.js +++ b/browser/components/extensions/test/browser/head.js @@ -2,13 +2,7 @@ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; -/* exported CustomizableUI makeWidgetId focusWindow forceGC - * getBrowserActionWidget - * clickBrowserAction clickPageAction - * getBrowserActionPopup getPageActionPopup - * closeBrowserAction closePageAction - * promisePopupShown - */ +/* exported AppConstants CustomizableUI forceGC makeWidgetId focusWindow clickBrowserAction clickPageAction */ var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm"); var {CustomizableUI} = Cu.import("resource:///modules/CustomizableUI.jsm"); @@ -45,58 +39,12 @@ var focusWindow = Task.async(function* focusWindow(win) { yield promise; }); -function promisePopupShown(popup) { - return new Promise(resolve => { - if (popup.state == "open") { - resolve(); - } else { - let onPopupShown = event => { - popup.removeEventListener("popupshown", onPopupShown); - resolve(); - }; - popup.addEventListener("popupshown", onPopupShown); - } - }); -} +function clickBrowserAction(extension, win = window) { + let browserActionId = makeWidgetId(extension.id) + "-browser-action"; + let elem = win.document.getElementById(browserActionId); -function getBrowserActionWidget(extension) { - return CustomizableUI.getWidget(makeWidgetId(extension.id) + "-browser-action"); -} - -function getBrowserActionPopup(extension, win = window) { - let group = getBrowserActionWidget(extension); - - if (group.areaType == CustomizableUI.TYPE_TOOLBAR) { - return win.document.getElementById("customizationui-widget-panel"); - } - return null; -} - -var clickBrowserAction = Task.async(function* (extension, win = window) { - let group = getBrowserActionWidget(extension); - let widget = group.forWindow(win); - - if (group.areaType == CustomizableUI.TYPE_TOOLBAR) { - ok(!widget.overflowed, "Expect widget not to be overflowed"); - } else if (group.areaType == CustomizableUI.TYPE_MENU_PANEL) { - yield win.PanelUI.show(); - } - - EventUtils.synthesizeMouseAtCenter(widget.node, {}, win); -}); - -function closeBrowserAction(extension, win = window) { - let group = getBrowserActionWidget(extension); - - let node = win.document.getElementById(group.viewId); - CustomizableUI.hidePanelForNode(node); - - return Promise.resolve(); -} - -function getPageActionPopup(extension, win = window) { - let panelId = makeWidgetId(extension.id) + "-panel"; - return win.document.getElementById(panelId); + EventUtils.synthesizeMouseAtCenter(elem, {}, win); + return new Promise(SimpleTest.executeSoon); } function clickPageAction(extension, win = window) { @@ -115,13 +63,3 @@ function clickPageAction(extension, win = window) { EventUtils.synthesizeMouseAtCenter(elem, {}, win); return new Promise(SimpleTest.executeSoon); } - -function closePageAction(extension, win = window) { - let node = getPageActionPopup(extension, win); - if (node) { - node.hidePopup(); - } - - return Promise.resolve(); -} - diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index fbe62e8e350a..2d2f30e4225b 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -250,11 +250,6 @@ panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .pan max-width: @standaloneSubviewWidth@; } -/* Give WebExtension stand-alone panels extra width for Chrome compatibility */ -.cui-widget-panel[viewId^=PanelUI-webext-] .panel-mainview { - max-width: 800px; -} - panelview:not([mainview]) .toolbarbutton-text, .cui-widget-panel toolbarbutton > .toolbarbutton-text { text-align: start;