зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound
This commit is contained in:
Коммит
9566b1145f
|
@ -10,7 +10,8 @@
|
|||
#include "HyperTextAccessible.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextPoint
|
||||
|
@ -294,3 +295,6 @@ TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -20,4 +20,4 @@ DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom']
|
|||
if CONFIG['MOZ_XUL']:
|
||||
DIRS += ['xul']
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
TEST_DIRS += ['tests/mochitest']
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -4,36 +4,33 @@
|
|||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
'actions',
|
||||
'attributes',
|
||||
'bounds',
|
||||
'editabletext',
|
||||
'elm',
|
||||
'focus',
|
||||
'hittest',
|
||||
'hyperlink',
|
||||
'hypertext',
|
||||
'jsat',
|
||||
'name',
|
||||
'pivot',
|
||||
'relations',
|
||||
'role',
|
||||
'scroll',
|
||||
'selectable',
|
||||
'states',
|
||||
'table',
|
||||
'text',
|
||||
'textattrs',
|
||||
'textcaret',
|
||||
'textselection',
|
||||
'treeupdate',
|
||||
'value',
|
||||
]
|
||||
|
||||
A11Y_MANIFESTS += [
|
||||
'a11y.ini',
|
||||
'actions/a11y.ini',
|
||||
'attributes/a11y.ini',
|
||||
'bounds/a11y.ini',
|
||||
'editabletext/a11y.ini',
|
||||
'elm/a11y.ini',
|
||||
'events/a11y.ini',
|
||||
'focus/a11y.ini',
|
||||
'hittest/a11y.ini',
|
||||
'hyperlink/a11y.ini',
|
||||
'hypertext/a11y.ini',
|
||||
'jsat/a11y.ini',
|
||||
'name/a11y.ini',
|
||||
'pivot/a11y.ini',
|
||||
'relations/a11y.ini',
|
||||
'role/a11y.ini',
|
||||
'scroll/a11y.ini',
|
||||
'selectable/a11y.ini',
|
||||
'states/a11y.ini',
|
||||
'table/a11y.ini',
|
||||
'text/a11y.ini',
|
||||
'textattrs/a11y.ini',
|
||||
'textcaret/a11y.ini',
|
||||
'textrange/a11y.ini',
|
||||
'textselection/a11y.ini',
|
||||
'tree/a11y.ini',
|
||||
'treeupdate/a11y.ini',
|
||||
'value/a11y.ini',
|
||||
]
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
A11Y_MANIFESTS += ['a11y.ini']
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += ['mochitest']
|
||||
|
|
@ -346,6 +346,7 @@ var shell = {
|
|||
window.addEventListener('sizemodechange', this);
|
||||
window.addEventListener('unload', this);
|
||||
this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
|
||||
this.contentBrowser.addEventListener('mozbrowserselectionchange', this, true);
|
||||
|
||||
CustomEventManager.init();
|
||||
WebappsHelper.init();
|
||||
|
@ -372,6 +373,7 @@ var shell = {
|
|||
window.removeEventListener('mozfullscreenchange', this);
|
||||
window.removeEventListener('sizemodechange', this);
|
||||
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
|
||||
this.contentBrowser.removeEventListener('mozbrowserselectionchange', this, true);
|
||||
ppmm.removeMessageListener("content-handler", this);
|
||||
|
||||
UserAgentOverrides.uninit();
|
||||
|
@ -514,6 +516,30 @@ var shell = {
|
|||
this.notifyContentStart();
|
||||
break;
|
||||
|
||||
case 'mozbrowserselectionchange':
|
||||
// The mozbrowserselectionchange event, may have crossed the chrome-content boundary.
|
||||
// This event always dispatch to shell.js. But the offset we got from this event is
|
||||
// based on tab's coordinate. So get the actual offsets between shell and evt.target.
|
||||
let elt = evt.target;
|
||||
let win = elt.ownerDocument.defaultView;
|
||||
let offsetX = win.mozInnerScreenX;
|
||||
let offsetY = win.mozInnerScreenY;
|
||||
|
||||
let rect = elt.getBoundingClientRect();
|
||||
offsetX += rect.left;
|
||||
offsetY += rect.top;
|
||||
|
||||
let data = evt.detail;
|
||||
data.offsetX = offsetX;
|
||||
data.offsetY = offsetY;
|
||||
|
||||
DoCommandHelper.setEvent(evt);
|
||||
shell.sendChromeEvent({
|
||||
type: 'selectionchange',
|
||||
detail: data,
|
||||
});
|
||||
break;
|
||||
|
||||
case 'MozApplicationManifest':
|
||||
try {
|
||||
if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
|
||||
|
@ -713,6 +739,23 @@ var CustomEventManager = {
|
|||
case 'inputmethod-update-layouts':
|
||||
KeyboardHelper.handleEvent(detail);
|
||||
break;
|
||||
case 'do-command':
|
||||
DoCommandHelper.handleEvent(detail.cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let DoCommandHelper = {
|
||||
_event: null,
|
||||
setEvent: function docommand_setEvent(evt) {
|
||||
this._event = evt;
|
||||
},
|
||||
|
||||
handleEvent: function docommand_handleEvent(cmd) {
|
||||
if (this._event) {
|
||||
shell.sendEvent(this._event.target, 'mozdocommand', { cmd: cmd });
|
||||
this._event = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -256,6 +256,7 @@ pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/
|
|||
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
|
||||
pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
|
||||
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
|
||||
pref("browser.uitour.whitelist.add.340", "about:home");
|
||||
|
||||
pref("browser.customizemode.tip0.shown", false);
|
||||
pref("browser.customizemode.tip0.learnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/customize");
|
||||
|
|
|
@ -1162,10 +1162,25 @@ var gPluginHandler = {
|
|||
let submitReports = true; // XXX followup for .getPropertyAsBool("submitReports");
|
||||
let pluginName = propBag.getPropertyAsAString("pluginName");
|
||||
let pluginDumpID = propBag.getPropertyAsAString("pluginDumpID");
|
||||
let browserDumpID = propBag.getPropertyAsAString("browserDumpID");
|
||||
let browserDumpID = null;
|
||||
let gmpPlugin = false;
|
||||
|
||||
// Remap the plugin name to a more user-presentable form.
|
||||
pluginName = this.makeNicePluginName(pluginName);
|
||||
try {
|
||||
browserDumpID = propBag.getPropertyAsAString("browserDumpID");
|
||||
} catch (e) {
|
||||
// For GMP crashes we don't get a browser dump.
|
||||
}
|
||||
|
||||
try {
|
||||
gmpPlugin = propBag.getPropertyAsBool("gmpPlugin");
|
||||
} catch (e) {
|
||||
// This property is only set for GMP plugins.
|
||||
}
|
||||
|
||||
// For non-GMP plugins, remap the plugin name to a more user-presentable form.
|
||||
if (!gmpPlugin) {
|
||||
pluginName = this.makeNicePluginName(pluginName);
|
||||
}
|
||||
|
||||
let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
|
||||
|
||||
|
|
|
@ -241,7 +241,7 @@ let MozLoopServiceInternal = {
|
|||
* @param {Boolean} noRetry Optional, don't retry if authentication fails.
|
||||
*/
|
||||
registerWithLoopServer: function(pushUrl, noRetry) {
|
||||
this.hawkRequest("/registration", "POST", { simple_push_url: pushUrl})
|
||||
this.hawkRequest("/registration", "POST", { simplePushURL: pushUrl})
|
||||
.then((response) => {
|
||||
// If this failed we got an invalid token. storeSessionToken rejects
|
||||
// the gRegisteredDeferred promise for us, so here we just need to
|
||||
|
|
|
@ -117,12 +117,6 @@ loop.Client = (function($) {
|
|||
try {
|
||||
var urlData = JSON.parse(responseText);
|
||||
|
||||
// XXX Support an alternate call_url property for
|
||||
// backwards compatibility whilst we switch over servers.
|
||||
// Bug 1033988 will want to remove these two lines.
|
||||
if (urlData.call_url)
|
||||
urlData.callUrl = urlData.call_url;
|
||||
|
||||
cb(null, this._validate(urlData, expectedCallUrlProperties));
|
||||
|
||||
this.mozLoop.noteCallUrlExpiry(urlData.expiresAt);
|
||||
|
|
|
@ -24,7 +24,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.func.isRequired
|
||||
model: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
|
|
@ -24,7 +24,7 @@ loop.conversation = (function(OT, mozL10n) {
|
|||
var IncomingCallView = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.func.isRequired
|
||||
model: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
|
|
@ -77,22 +77,22 @@ loop.panel = (function(_, mozL10n) {
|
|||
__("display_name_available_status");
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "footer component-spacer"},
|
||||
React.DOM.div({className: "do-not-disturb"},
|
||||
React.DOM.p({className: "dnd-status", onClick: this.showDropdownMenu},
|
||||
React.DOM.span(null, availabilityText),
|
||||
React.DOM.i({className: availabilityStatus})
|
||||
),
|
||||
React.DOM.ul({className: availabilityDropdown,
|
||||
onMouseLeave: this.hideDropdownMenu},
|
||||
React.DOM.li({onClick: this.changeAvailability("available"),
|
||||
className: "dnd-menu-item dnd-make-available"},
|
||||
React.DOM.i({className: "status status-available"}),
|
||||
React.DOM.div( {className:"footer component-spacer"},
|
||||
React.DOM.div( {className:"do-not-disturb"},
|
||||
React.DOM.p( {className:"dnd-status", onClick:this.showDropdownMenu},
|
||||
React.DOM.span(null, availabilityText),
|
||||
React.DOM.i( {className:availabilityStatus})
|
||||
),
|
||||
React.DOM.ul( {className:availabilityDropdown,
|
||||
onMouseLeave:this.hideDropdownMenu},
|
||||
React.DOM.li( {onClick:this.changeAvailability("available"),
|
||||
className:"dnd-menu-item dnd-make-available"},
|
||||
React.DOM.i( {className:"status status-available"}),
|
||||
React.DOM.span(null, __("display_name_available_status"))
|
||||
),
|
||||
React.DOM.li({onClick: this.changeAvailability("do-not-disturb"),
|
||||
className: "dnd-menu-item dnd-make-unavailable"},
|
||||
React.DOM.i({className: "status status-dnd"}),
|
||||
),
|
||||
React.DOM.li( {onClick:this.changeAvailability("do-not-disturb"),
|
||||
className:"dnd-menu-item dnd-make-unavailable"},
|
||||
React.DOM.i( {className:"status status-dnd"}),
|
||||
React.DOM.span(null, __("display_name_dnd_status"))
|
||||
)
|
||||
)
|
||||
|
@ -115,10 +115,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
|
||||
if (this.state.seenToS == "unseen") {
|
||||
navigator.mozLoop.setLoopCharPref('seenToS', 'seen');
|
||||
return React.DOM.p({className: "terms-service",
|
||||
dangerouslySetInnerHTML: {__html: tosHTML}});
|
||||
return React.DOM.p( {className:"terms-service",
|
||||
dangerouslySetInnerHTML:{__html: tosHTML}});
|
||||
} else {
|
||||
return React.DOM.div(null);
|
||||
return React.DOM.div(null );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -130,11 +130,11 @@ loop.panel = (function(_, mozL10n) {
|
|||
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.div({className: "component-spacer share generate-url"},
|
||||
React.DOM.div({className: "description"},
|
||||
React.DOM.p({className: "description-content"}, this.props.summary)
|
||||
),
|
||||
React.DOM.div({className: "action"},
|
||||
React.DOM.div( {className:"component-spacer share generate-url"},
|
||||
React.DOM.div( {className:"description"},
|
||||
React.DOM.p( {className:"description-content"}, this.props.summary)
|
||||
),
|
||||
React.DOM.div( {className:"action"},
|
||||
this.props.children
|
||||
)
|
||||
)
|
||||
|
@ -178,8 +178,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
this.setState({pending: false});
|
||||
} else {
|
||||
try {
|
||||
var callUrl = new window.URL(callUrlData.callUrl ||
|
||||
callUrlData.call_url);
|
||||
var callUrl = new window.URL(callUrlData.callUrl);
|
||||
// XXX the current server vers does not implement the callToken field
|
||||
// but it exists in the API. This workaround should be removed in the future
|
||||
var token = callUrlData.callToken ||
|
||||
|
@ -202,10 +201,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
PanelLayout({summary: __("share_link_header_text")},
|
||||
React.DOM.div({className: "invite"},
|
||||
React.DOM.input({type: "url", value: this.state.callUrl, readOnly: "true",
|
||||
className: cx({'pending': this.state.pending})})
|
||||
PanelLayout( {summary:__("share_link_header_text")},
|
||||
React.DOM.div( {className:"invite"},
|
||||
React.DOM.input( {type:"url", value:this.state.callUrl, readOnly:"true",
|
||||
className:cx({'pending': this.state.pending})} )
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -224,10 +223,10 @@ loop.panel = (function(_, mozL10n) {
|
|||
render: function() {
|
||||
return (
|
||||
React.DOM.div(null,
|
||||
CallUrlResult({client: this.props.client,
|
||||
notifier: this.props.notifier}),
|
||||
ToSView(null),
|
||||
AvailabilityDropdown(null)
|
||||
CallUrlResult( {client:this.props.client,
|
||||
notifier:this.props.notifier} ),
|
||||
ToSView(null ),
|
||||
AvailabilityDropdown(null )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -294,8 +293,8 @@ loop.panel = (function(_, mozL10n) {
|
|||
var client = new loop.Client({
|
||||
baseServerUrl: navigator.mozLoop.serverUrl
|
||||
});
|
||||
this.loadReactComponent(PanelView({client: client,
|
||||
notifier: this._notifier}));
|
||||
this.loadReactComponent(PanelView( {client:client,
|
||||
notifier:this._notifier} ));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -178,8 +178,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
this.setState({pending: false});
|
||||
} else {
|
||||
try {
|
||||
var callUrl = new window.URL(callUrlData.callUrl ||
|
||||
callUrlData.call_url);
|
||||
var callUrl = new window.URL(callUrlData.callUrl);
|
||||
// XXX the current server vers does not implement the callToken field
|
||||
// but it exists in the API. This workaround should be removed in the future
|
||||
var token = callUrlData.callToken ||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.conversation .controls {
|
||||
.conversation-toolbar {
|
||||
position: absolute;
|
||||
z-index: 999; /* required to have it superimposed to the video element */
|
||||
left: 0;
|
||||
|
@ -20,13 +20,13 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.conversation .controls li {
|
||||
.conversation-toolbar li {
|
||||
float: left;
|
||||
font-size: 0px; /* prevents vertical bottom padding added to buttons in google
|
||||
chrome */
|
||||
}
|
||||
|
||||
.conversation .controls .btn {
|
||||
.conversation-toolbar .btn {
|
||||
width: 40px;
|
||||
height: 30px;
|
||||
background: transparent;
|
||||
|
@ -38,66 +38,66 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.conversation .controls .btn:hover {
|
||||
.conversation-toolbar .btn:hover {
|
||||
background-color: rgba(255, 255, 255, .35);
|
||||
}
|
||||
|
||||
/* Hangup button */
|
||||
.conversation .controls .btn-hangup {
|
||||
.conversation-toolbar .btn-hangup {
|
||||
background-color: #D74345;
|
||||
background-image: url(../img/hangup-inverse-14x14.png);
|
||||
}
|
||||
.conversation .controls .btn-hangup:hover {
|
||||
.conversation-toolbar .btn-hangup:hover {
|
||||
background-color: #C53436;
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.conversation .controls .btn-hangup {
|
||||
.conversation-toolbar .btn-hangup {
|
||||
background-image: url(../img/hangup-inverse-14x14@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
/* Common media control buttons behavior */
|
||||
.conversation .controls .media-control {
|
||||
.conversation-toolbar .media-control {
|
||||
background-color: transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
.conversation .controls .media-control:hover {
|
||||
.conversation-toolbar .media-control:hover {
|
||||
background-color: rgba(255, 255, 255, .35);
|
||||
opacity: 1;
|
||||
}
|
||||
.conversation .controls .media-control.muted {
|
||||
.conversation-toolbar .media-control.muted {
|
||||
background-color: #0096DD;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Audio mute button */
|
||||
.conversation .controls .local-media.btn-mute-audio {
|
||||
.conversation-toolbar .local-media.btn-mute-audio {
|
||||
background-image: url(../img/audio-inverse-14x14.png);
|
||||
}
|
||||
.conversation .controls .local-media.btn-mute-audio.muted {
|
||||
.conversation-toolbar .local-media.btn-mute-audio.muted {
|
||||
background-image: url(../img/mute-inverse-14x14.png);
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.conversation .controls .local-media.btn-mute-audio {
|
||||
.conversation-toolbar .local-media.btn-mute-audio {
|
||||
background-image: url(../img/audio-inverse-14x14@2x.png);
|
||||
}
|
||||
.conversation .controls .local-media.btn-mute-audio.muted {
|
||||
.conversation-toolbar .local-media.btn-mute-audio.muted {
|
||||
background-image: url(../img/mute-inverse-14x14@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
/* Video mute button */
|
||||
.conversation .controls .local-media.btn-mute-video {
|
||||
.conversation-toolbar .local-media.btn-mute-video {
|
||||
background-image: url(../img/video-inverse-14x14.png);
|
||||
}
|
||||
.conversation .controls .local-media.btn-mute-video.muted {
|
||||
.conversation-toolbar .local-media.btn-mute-video.muted {
|
||||
background-image: url(../img/facemute-14x14.png);
|
||||
}
|
||||
@media (min-resolution: 2dppx) {
|
||||
.conversation .controls .local-media.btn-mute-video {
|
||||
.conversation-toolbar .local-media.btn-mute-video {
|
||||
background-image: url(../img/video-inverse-14x14@2x.png);
|
||||
}
|
||||
.conversation .controls .local-media.btn-mute-video.muted {
|
||||
.conversation-toolbar .local-media.btn-mute-video.muted {
|
||||
background-image: url(../img/facemute-14x14@2x.png);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,274 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- 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/. -->
|
||||
|
||||
<!-- This file is intended to help frontend developers to easily identify what
|
||||
are the available styles for the Loop UI components. -->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Loop UI shared CSS information/demo</title>
|
||||
<link type="text/css" rel="stylesheet" href="common.css">
|
||||
<link type="text/css" rel="stylesheet" href="panel.css">
|
||||
<link type="text/css" rel="stylesheet" href="conversation.css">
|
||||
<style>
|
||||
body {
|
||||
width: 600px;
|
||||
margin: 1em auto;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 3em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Loop UI toolkit</h1>
|
||||
|
||||
<h2>Logo icons</h2>
|
||||
|
||||
<p>
|
||||
<img src="../img/icon_32.png"> 32x32 transparent PNG
|
||||
<img src="../img/icon_64.png"> 64x64 transparent PNG
|
||||
</p>
|
||||
|
||||
<p><em><strong>Note:</strong> these are temporary.</em></p>
|
||||
|
||||
<h2>Share panel</h2>
|
||||
|
||||
<h3>Simple</h3>
|
||||
|
||||
<div class="share">
|
||||
<form class="description">
|
||||
<p>This is a simple message.</p>
|
||||
</form>
|
||||
<div class="action">
|
||||
<p><input type="url" value="http://loop.im/plop75"></p>
|
||||
<p>Your name will appear as <a href="">Unnamed</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Featuring options</h3>
|
||||
|
||||
<div class="share">
|
||||
<form class="description">
|
||||
<p class="field">
|
||||
<label>Share this link with a friend to
|
||||
<select>
|
||||
<option>browse together</option>
|
||||
<option selected="selected">video chat</option>
|
||||
<option>audio chat</option>
|
||||
<option>text chat</option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p class="field">
|
||||
<label>
|
||||
Use webcam <select><option>Foo</option></select>
|
||||
</label>
|
||||
</p>
|
||||
<p class="field">
|
||||
<label>Use whatever
|
||||
<select><option>Long foo is long indeed</option></select>
|
||||
</label>
|
||||
</p>
|
||||
<p class="preview cf">
|
||||
Preview <video></video>
|
||||
</p>
|
||||
</form>
|
||||
<div class="action">
|
||||
<p><input type="url" value="http://loop.im/plop75"></p>
|
||||
<p>Your name will appear as <a href="">Unnamed</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Conversation window</h2>
|
||||
|
||||
<p><em>The conversation component adapts automatically to its container to
|
||||
occupy all the available space.</em></p>
|
||||
|
||||
<h3>Large</h3>
|
||||
|
||||
<div class="conversation">
|
||||
<div class="media nested">
|
||||
<div class="remote_wrapper">
|
||||
<video class="remote"></video>
|
||||
</div>
|
||||
<video class="local"></video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Large with controls</h3>
|
||||
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
<div class="media nested">
|
||||
<div class="remote_wrapper">
|
||||
<video class="remote"></video>
|
||||
</div>
|
||||
<video class="local"></video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Small (think chat window)</h3>
|
||||
|
||||
<div style="width: 204px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
<div class="media nested">
|
||||
<div class="remote_wrapper">
|
||||
<video class="remote"></video>
|
||||
</div>
|
||||
<video class="local"></video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Side by side</h3>
|
||||
|
||||
<div class="conversation">
|
||||
<div class="media side-by-side">
|
||||
<div class="remote_wrapper">
|
||||
<video class="remote"></video>
|
||||
</div>
|
||||
<video class="local"></video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Controls button variants</h2>
|
||||
|
||||
<h3>Nothing muted</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Local audio muted</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-audio muted" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Local video muted</h3>
|
||||
|
||||
<div style="width: 204px; min-height: 26px">
|
||||
<div class="conversation">
|
||||
<ul class="controls">
|
||||
<li><button class="btn btn-hangup" title="Hangup"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-video muted" title="Mute video"></button></li>
|
||||
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Expired call url view</h2>
|
||||
|
||||
<div class="expired-url-info">
|
||||
<div class="info-panel">
|
||||
<div class="firefox-logo"></div>
|
||||
<h1 >Oops!</h1>
|
||||
<h4 >This URL is unavailable.</h4>
|
||||
</div>
|
||||
<div class="promote-firefox">
|
||||
<h3>Download Firefox to make free audio and video calls!</h3>
|
||||
<p>
|
||||
<a class="btn btn-large btn-success" href="https://www.mozilla.org/firefox/" data-reactid=".0.1.1.0">Get Firefox</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Buttons</h2>
|
||||
|
||||
<h3>Using <code><a></code></h3>
|
||||
|
||||
<p>
|
||||
<a href="" class="btn">default</a>
|
||||
<a href="" class="btn btn-info">info</a>
|
||||
<a href="" class="btn btn-success">success</a>
|
||||
<a href="" class="btn btn-warning">warning</a>
|
||||
<a href="" class="btn btn-error">error</a>
|
||||
</p>
|
||||
|
||||
<h3>Inline</h3>
|
||||
|
||||
<p>Click <a href="" class="btn btn-info">here</a>.</p>
|
||||
|
||||
<h3>Using <code><button></code></h3>
|
||||
|
||||
<p>
|
||||
<button class="btn">default</button>
|
||||
<button class="btn btn-info">info</button>
|
||||
<button class="btn btn-success">success</button>
|
||||
<button class="btn btn-warning">warning</button>
|
||||
<button class="btn btn-error">error</button>
|
||||
</p>
|
||||
|
||||
<h3>Large buttons</h3>
|
||||
|
||||
<p>
|
||||
<a class="btn btn-large">default</a>
|
||||
<a class="btn btn-large btn-info">info</a>
|
||||
<a class="btn btn-large btn-success">success</a>
|
||||
<a class="btn btn-large btn-warning">warning</a>
|
||||
<a class="btn btn-large btn-error">error</a>
|
||||
</p>
|
||||
|
||||
<h2>Alerts</h2>
|
||||
|
||||
<div class="alert alert-error">
|
||||
<button class="close"></button>
|
||||
<p class="message">Oops! Something went really wrong.</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<button class="close"></button>
|
||||
<p class="message">Oops! This is a warning.</p>
|
||||
</div>
|
||||
|
||||
<h2>Logos</h2>
|
||||
|
||||
<h3>Centered Firefox logo</h3>
|
||||
|
||||
<div class="firefox-logo"></div>
|
||||
|
||||
<h2>Incoming call</h2>
|
||||
|
||||
<div class="incoming-call">
|
||||
<h2>Incoming call</h2>
|
||||
<p>
|
||||
<button class="btn btn-success btn-accept">Accept</button>
|
||||
<button class="btn btn-error btn-decline">Decline</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.onload = function() {
|
||||
[].forEach.call(document.querySelectorAll("video"), function(video) {
|
||||
video.setAttribute("src", "http://v2v.cc/~j/theora_testsuite/320x240.ogg");
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -180,7 +180,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
return (
|
||||
React.DOM.ul({className: "controls"},
|
||||
React.DOM.ul({className: "conversation-toolbar"},
|
||||
React.DOM.li(null, React.DOM.button({className: "btn btn-hangup",
|
||||
onClick: this.handleClickHangup,
|
||||
title: __("hangup_button_title")})),
|
||||
|
|
|
@ -180,7 +180,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
return (
|
||||
<ul className="controls">
|
||||
<ul className="conversation-toolbar">
|
||||
<li><button className="btn btn-hangup"
|
||||
onClick={this.handleClickHangup}
|
||||
title={__("hangup_button_title")}></button></li>
|
||||
|
|
|
@ -42,14 +42,14 @@ loop.webapp = (function($, _, OT, webL10n) {
|
|||
|
||||
render: function() {
|
||||
if (this.props.helper.isFirefox(navigator.userAgent)) {
|
||||
return React.DOM.div(null );
|
||||
return React.DOM.div(null);
|
||||
}
|
||||
return (
|
||||
React.DOM.div( {className:"promote-firefox"},
|
||||
React.DOM.h3(null, __("promote_firefox_hello_heading")),
|
||||
React.DOM.div({className: "promote-firefox"},
|
||||
React.DOM.h3(null, __("promote_firefox_hello_heading")),
|
||||
React.DOM.p(null,
|
||||
React.DOM.a( {className:"btn btn-large btn-success",
|
||||
href:"https://www.mozilla.org/firefox/"},
|
||||
React.DOM.a({className: "btn btn-large btn-success",
|
||||
href: "https://www.mozilla.org/firefox/"},
|
||||
__("get_firefox_button")
|
||||
)
|
||||
)
|
||||
|
@ -69,13 +69,13 @@ loop.webapp = (function($, _, OT, webL10n) {
|
|||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
return (
|
||||
React.DOM.div( {className:"expired-url-info"},
|
||||
React.DOM.div( {className:"info-panel"},
|
||||
React.DOM.div( {className:"firefox-logo"} ),
|
||||
React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
|
||||
React.DOM.div({className: "expired-url-info"},
|
||||
React.DOM.div({className: "info-panel"},
|
||||
React.DOM.div({className: "firefox-logo"}),
|
||||
React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
|
||||
React.DOM.h4(null, __("call_url_unavailable_notification_message"))
|
||||
),
|
||||
PromoteFirefoxView( {helper:this.props.helper} )
|
||||
),
|
||||
PromoteFirefoxView({helper: this.props.helper})
|
||||
)
|
||||
);
|
||||
/* jshint ignore:end */
|
||||
|
|
|
@ -200,7 +200,7 @@ describe("loop.panel", function() {
|
|||
|
||||
beforeEach(function() {
|
||||
callUrlData = {
|
||||
call_url: "http://call.invalid/",
|
||||
callUrl: "http://call.invalid/",
|
||||
expiresAt: 1000
|
||||
};
|
||||
|
||||
|
@ -229,7 +229,7 @@ describe("loop.panel", function() {
|
|||
|
||||
beforeEach(function() {
|
||||
callUrlData = {
|
||||
call_url: "http://call.invalid/fakeToken",
|
||||
callUrl: "http://call.invalid/fakeToken",
|
||||
expiresAt: 1000
|
||||
};
|
||||
|
||||
|
@ -272,7 +272,7 @@ describe("loop.panel", function() {
|
|||
|
||||
it("should update state with the call url received", function() {
|
||||
expect(view.state.pending).eql(false);
|
||||
expect(view.state.callUrl).eql(callUrlData.call_url);
|
||||
expect(view.state.callUrl).eql(callUrlData.callUrl);
|
||||
});
|
||||
|
||||
it("should clear the pending state when a response is received",
|
||||
|
@ -283,7 +283,7 @@ describe("loop.panel", function() {
|
|||
it("should update CallUrlResult with the call url", function() {
|
||||
var urlField = view.getDOMNode().querySelector("input[type='url']");
|
||||
|
||||
expect(urlField.value).eql(callUrlData.call_url);
|
||||
expect(urlField.value).eql(callUrlData.callUrl);
|
||||
});
|
||||
|
||||
it("should reset all pending notifications", function() {
|
||||
|
|
|
@ -47,12 +47,13 @@ let mockPushHandler = {
|
|||
// This sets the registration result to be returned when initialize
|
||||
// is called. By default, it is equivalent to success.
|
||||
registrationResult: null,
|
||||
registrationPushURL: undefined,
|
||||
|
||||
/**
|
||||
* MozLoopPushHandler API
|
||||
*/
|
||||
initialize: function(registerCallback, notificationCallback) {
|
||||
registerCallback(this.registrationResult);
|
||||
registerCallback(this.registrationResult, this.registrationPushURL);
|
||||
this._notificationCallback = notificationCallback;
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
|
||||
/**
|
||||
* This file is to test general registration. Note that once successful
|
||||
* registration has taken place, we can no longer test the server side
|
||||
|
@ -49,7 +51,14 @@ add_test(function test_register_websocket_success_loop_server_fail() {
|
|||
* registration are complete.
|
||||
*/
|
||||
add_test(function test_register_success() {
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
||||
let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
|
||||
let data = JSON.parse(body);
|
||||
Assert.equal(data.simplePushURL, kEndPointUrl,
|
||||
"Should send correct push url");
|
||||
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
Loop UI Components Showcase
|
||||
===========================
|
||||
|
||||
This app file showcases all Loop's view components.
|
||||
|
||||
If you want to modify the app, launch the following command:
|
||||
|
||||
browser/components/loop/build-jsx --watch
|
||||
|
||||
And start editing the `ui-showcase.jsx` file.
|
|
@ -0,0 +1,14 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
|
||||
* objects and makes them returning "fake string" for any requested string id.
|
||||
* @type {Object}
|
||||
*/
|
||||
document.webL10n = document.mozL10n = {
|
||||
get: function() {
|
||||
return "fake text";
|
||||
}
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Faking the mozLoop object which doesn't exist in regular web pages.
|
||||
* @type {Object}
|
||||
*/
|
||||
navigator.mozLoop = {
|
||||
getLoopCharPref: function() {}
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- 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/. -->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Loop UI Components Showcase</title>
|
||||
<link rel="stylesheet" type="text/css" href="../content/shared/css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="../content/shared/css/conversation.css">
|
||||
<link rel="stylesheet" type="text/css" href="../content/shared/css/panel.css">
|
||||
<link rel="stylesheet" type="text/css" href="ui-showcase.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
<script src="fake-mozLoop.js"></script>
|
||||
<script src="fake-l10n.js"></script>
|
||||
<script src="../content/libs/sdk.js"></script>
|
||||
<script src="../content/shared/libs/react-0.10.0.js"></script>
|
||||
<script src="../content/shared/libs/jquery-2.1.0.js"></script>
|
||||
<script src="../content/shared/libs/lodash-2.4.1.js"></script>
|
||||
<script src="../content/shared/libs/backbone-1.1.2.js"></script>
|
||||
<script src="../content/shared/js/models.js"></script>
|
||||
<script src="../content/shared/js/router.js"></script>
|
||||
<script src="../content/shared/js/views.js"></script>
|
||||
<script src="../content/js/client.js"></script>
|
||||
<script src="../content/js/desktopRouter.js"></script>
|
||||
<script src="../standalone/content/js/webapp.js"></script>
|
||||
<script src="../content/js/panel.js"></script>
|
||||
<script src="../content/js/conversation.js"></script>
|
||||
<script src="ui-showcase.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
/* 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/. */
|
||||
|
||||
.showcase {
|
||||
width: 730px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.showcase > header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
background-color: #fbfbfb;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.showcase .menu > a {
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.showcase > section {
|
||||
position: relative;
|
||||
padding-top: 10em;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.showcase > section > h1 {
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.showcase > section .comp {
|
||||
margin: 0 auto; /* width is usually set programmatically */
|
||||
}
|
||||
|
||||
.showcase > section .comp.dashed {
|
||||
border: 1px dashed #ccc;
|
||||
}
|
||||
|
||||
.showcase > section > .example {
|
||||
margin-bottom: 6em;
|
||||
}
|
||||
|
||||
.showcase > section .example > h3 {
|
||||
border-bottom: 1px dashed #aaa;
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/** @jsx React.DOM */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
/* jshint newcap:false */
|
||||
/* global loop:true, React */
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
// 1. Desktop components
|
||||
// 1.1 Panel
|
||||
var PanelView = loop.panel.PanelView;
|
||||
// 1.2. Conversation Window
|
||||
var IncomingCallView = loop.conversation.IncomingCallView;
|
||||
|
||||
// 2. Standalone webapp
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
|
||||
// 3. Shared components
|
||||
var ConversationToolbar = loop.shared.views.ConversationToolbar;
|
||||
var ConversationView = loop.shared.views.ConversationView;
|
||||
|
||||
// Local helpers
|
||||
function returnTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function returnFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
var Example = React.createClass({displayName: 'Example',
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
React.DOM.div({className: "example"},
|
||||
React.DOM.h3(null, this.props.summary),
|
||||
React.DOM.div({className: cx({comp: true, dashed: this.props.dashed}),
|
||||
style: this.props.style || {}},
|
||||
this.props.children
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Section = React.createClass({displayName: 'Section',
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.section({id: this.props.name},
|
||||
React.DOM.h1(null, this.props.name),
|
||||
this.props.children
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ShowCase = React.createClass({displayName: 'ShowCase',
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.div({className: "showcase"},
|
||||
React.DOM.header(null,
|
||||
React.DOM.h1(null, "Loop UI Components Showcase"),
|
||||
React.DOM.nav({className: "menu"},
|
||||
React.Children.map(this.props.children, function(section) {
|
||||
return (
|
||||
React.DOM.a({className: "btn btn-info", href: "#" + section.props.name},
|
||||
section.props.name
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
),
|
||||
this.props.children
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var App = React.createClass({displayName: 'App',
|
||||
render: function() {
|
||||
return (
|
||||
ShowCase(null,
|
||||
Section({name: "PanelView"},
|
||||
Example({summary: "332px wide", dashed: "true", style: {width: "332px"}},
|
||||
PanelView(null)
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "IncomingCallView"},
|
||||
Example({summary: "Default", dashed: "true", style: {width: "280px"}},
|
||||
IncomingCallView(null)
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "ConversationToolbar"},
|
||||
Example({summary: "Default"},
|
||||
ConversationToolbar({video: {enabled: true}, audio: {enabled: true}})
|
||||
),
|
||||
Example({summary: "Video muted"},
|
||||
ConversationToolbar({video: {enabled: false}, audio: {enabled: true}})
|
||||
),
|
||||
Example({summary: "Audio muted"},
|
||||
ConversationToolbar({video: {enabled: true}, audio: {enabled: false}})
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "ConversationView"},
|
||||
Example({summary: "Default"},
|
||||
ConversationView({video: {enabled: true}, audio: {enabled: true}})
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "CallUrlExpiredView"},
|
||||
Example({summary: "Firefox User"},
|
||||
CallUrlExpiredView({helper: {isFirefox: returnTrue}})
|
||||
),
|
||||
Example({summary: "Non-Firefox User"},
|
||||
CallUrlExpiredView({helper: {isFirefox: returnFalse}})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
React.renderComponent(App(null), document.body);
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,133 @@
|
|||
/** @jsx React.DOM */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
/* jshint newcap:false */
|
||||
/* global loop:true, React */
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
// 1. Desktop components
|
||||
// 1.1 Panel
|
||||
var PanelView = loop.panel.PanelView;
|
||||
// 1.2. Conversation Window
|
||||
var IncomingCallView = loop.conversation.IncomingCallView;
|
||||
|
||||
// 2. Standalone webapp
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
|
||||
// 3. Shared components
|
||||
var ConversationToolbar = loop.shared.views.ConversationToolbar;
|
||||
var ConversationView = loop.shared.views.ConversationView;
|
||||
|
||||
// Local helpers
|
||||
function returnTrue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function returnFalse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
var Example = React.createClass({
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<div className="example">
|
||||
<h3>{this.props.summary}</h3>
|
||||
<div className={cx({comp: true, dashed: this.props.dashed})}
|
||||
style={this.props.style || {}}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Section = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<section id={this.props.name}>
|
||||
<h1>{this.props.name}</h1>
|
||||
{this.props.children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var ShowCase = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="showcase">
|
||||
<header>
|
||||
<h1>Loop UI Components Showcase</h1>
|
||||
<nav className="menu">{
|
||||
React.Children.map(this.props.children, function(section) {
|
||||
return (
|
||||
<a className="btn btn-info" href={"#" + section.props.name}>
|
||||
{section.props.name}
|
||||
</a>
|
||||
);
|
||||
})
|
||||
}</nav>
|
||||
</header>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var App = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<ShowCase>
|
||||
<Section name="PanelView">
|
||||
<Example summary="332px wide" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView />
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="IncomingCallView">
|
||||
<Example summary="Default" dashed="true" style={{width: "280px"}}>
|
||||
<IncomingCallView />
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="ConversationToolbar">
|
||||
<Example summary="Default">
|
||||
<ConversationToolbar video={{enabled: true}} audio={{enabled: true}} />
|
||||
</Example>
|
||||
<Example summary="Video muted">
|
||||
<ConversationToolbar video={{enabled: false}} audio={{enabled: true}} />
|
||||
</Example>
|
||||
<Example summary="Audio muted">
|
||||
<ConversationToolbar video={{enabled: true}} audio={{enabled: false}} />
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="ConversationView">
|
||||
<Example summary="Default">
|
||||
<ConversationView video={{enabled: true}} audio={{enabled: true}} />
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="CallUrlExpiredView">
|
||||
<Example summary="Firefox User">
|
||||
<CallUrlExpiredView helper={{isFirefox: returnTrue}} />
|
||||
</Example>
|
||||
<Example summary="Non-Firefox User">
|
||||
<CallUrlExpiredView helper={{isFirefox: returnFalse}} />
|
||||
</Example>
|
||||
</Section>
|
||||
</ShowCase>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
React.renderComponent(<App />, document.body);
|
||||
});
|
||||
})();
|
|
@ -3,4 +3,4 @@
|
|||
# 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/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
|
|
@ -14,7 +14,8 @@ const TESTCASE_URI_SCSS = TEST_BASE + "sourcemap-sass/sourcemaps.scss";
|
|||
const TESTCASE_URI_MAP = TEST_BASE + "sourcemap-css/sourcemaps.css.map";
|
||||
const TESTCASE_SCSS_NAME = "sourcemaps.scss";
|
||||
|
||||
const PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
const SOURCE_MAP_PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
|
||||
|
||||
const CSS_TEXT = "* { color: blue }";
|
||||
|
||||
|
@ -31,7 +32,8 @@ function test()
|
|||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref(PREF, true);
|
||||
Services.prefs.setBoolPref(SOURCE_MAP_PREF, true);
|
||||
Services.prefs.setBoolPref(TRANSITIONS_PREF, false);
|
||||
|
||||
Task.spawn(function() {
|
||||
// copy all our files over so we don't screw them up for other tests
|
||||
|
@ -126,7 +128,8 @@ function pauseForTimeChange() {
|
|||
}
|
||||
|
||||
function finishUp() {
|
||||
Services.prefs.clearUserPref(PREF);
|
||||
Services.prefs.clearUserPref(SOURCE_MAP_PREF);
|
||||
Services.prefs.clearUserPref(TRANSITIONS_PREF);
|
||||
finish();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,11 +50,17 @@ let test = asyncTest(function*() {
|
|||
is(specificity, expected,
|
||||
'Selector "' + selectorText + '" has a specificity of ' + expected);
|
||||
}
|
||||
|
||||
info("Testing specificity of element.style");
|
||||
let colorProp = cssLogic.getPropertyInfo("background");
|
||||
is(colorProp.matchedSelectors[0].specificity, 0x01000000,
|
||||
"Element styles have specificity of 0x01000000 (16777216).");
|
||||
});
|
||||
|
||||
function createDocument() {
|
||||
let doc = content.document;
|
||||
doc.body.innerHTML = getStylesheetText();
|
||||
doc.body.style.background = "blue";
|
||||
doc.title = "Computed view specificity test";
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ window.addEventListener("load", function onLoad() {
|
|||
|
||||
// Buttons
|
||||
document.querySelector("#close").onclick = CloseUI;
|
||||
document.querySelector("#restoreButton").onclick = RestoreDefaults;
|
||||
document.querySelector("#restore").onclick = RestoreDefaults;
|
||||
document.querySelector("#manageSimulators").onclick = ShowAddons;
|
||||
|
||||
// Initialize the controls
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="restore">&prefs_restore;</a>
|
||||
<a id="manageSimulators">&prefs_simulators;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
|
@ -27,6 +29,12 @@
|
|||
<h2>&prefs_general_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_enablelocalruntime_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.enableLocalRuntime"/>
|
||||
|
@ -40,9 +48,9 @@
|
|||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_templatesurl_tooltip;">
|
||||
<span>&prefs_options_templatesurl;</span>
|
||||
<input data-pref="devtools.webide.templatesURL"/>
|
||||
<label title="&prefs_options_showeditor_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.showProjectEditor"/>
|
||||
<span>&prefs_options_showeditor;</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -50,36 +58,6 @@
|
|||
<h2>&prefs_editor_title;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label title="&prefs_options_showeditor_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.webide.showProjectEditor"/>
|
||||
<span>&prefs_options_showeditor;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoclosebrackets_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autoclosebrackets"/>
|
||||
<span>&prefs_options_autoclosebrackets;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autocomplete_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autocomplete"/>
|
||||
<span>&prefs_options_autocomplete;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_detectindentation_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.detectindentation"/>
|
||||
<span>&prefs_options_detectindentation;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_expandtab_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.expandtab"/>
|
||||
<span>&prefs_options_expandtab;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label><span>&prefs_options_tabsize;</span>
|
||||
<select data-pref="devtools.editor.tabsize">
|
||||
|
@ -89,10 +67,31 @@
|
|||
</select>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_expandtab_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.expandtab"/>
|
||||
<span>&prefs_options_expandtab;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_detectindentation_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.detectindentation"/>
|
||||
<span>&prefs_options_detectindentation;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autocomplete_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autocomplete"/>
|
||||
<span>&prefs_options_autocomplete;</span>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label title="&prefs_options_autoclosebrackets_tooltip;">
|
||||
<input type="checkbox" data-pref="devtools.editor.autoclosebrackets"/>
|
||||
<span>&prefs_options_autoclosebrackets;</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button id="manageSimulators">&prefs_simulators;</button>
|
||||
<button id="restoreButton">&prefs_restore;</button>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -108,15 +108,15 @@
|
|||
<!ENTITY prefs_options_templatesurl_tooltip "Index of available templates">
|
||||
<!ENTITY prefs_options_showeditor "Show editor">
|
||||
<!ENTITY prefs_options_showeditor_tooltip "Show internal editor">
|
||||
<!ENTITY prefs_options_detectindentation "Detect indentation">
|
||||
<!ENTITY prefs_options_tabsize "Tab size">
|
||||
<!ENTITY prefs_options_expandtab "Soft tabs">
|
||||
<!ENTITY prefs_options_expandtab_tooltip "Use spaces instead of the tab character">
|
||||
<!ENTITY prefs_options_detectindentation "Autoindent">
|
||||
<!ENTITY prefs_options_detectindentation_tooltip "Guess indentation based on source content">
|
||||
<!ENTITY prefs_options_autocomplete "Autocomplete">
|
||||
<!ENTITY prefs_options_autocomplete_tooltip "Enable code autocompletion">
|
||||
<!ENTITY prefs_options_autoclosebrackets "Autoclose brackets">
|
||||
<!ENTITY prefs_options_autoclosebrackets_tooltip "Automatically insert closing brackets">
|
||||
<!ENTITY prefs_options_expandtab "Indent using spaces">
|
||||
<!ENTITY prefs_options_expandtab_tooltip "Use spaces instead of the tab character">
|
||||
<!ENTITY prefs_options_autocomplete "Autocompletion">
|
||||
<!ENTITY prefs_options_autocomplete_tooltip "Enable code autocompletion">
|
||||
<!ENTITY prefs_options_tabsize "Tab size">
|
||||
|
||||
<!-- Permissions Table -->
|
||||
<!ENTITY permissionstable_title "Permissions Table">
|
||||
|
|
|
@ -37,9 +37,6 @@ h1 {
|
|||
font-size: small;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
#close {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,5 +4,4 @@
|
|||
# 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/.
|
||||
|
||||
TEST_DIRS += ['pdfjs/test']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['pdfjs/test/browser.ini']
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
|
|
@ -5,4 +5,4 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += ['public', 'src']
|
||||
TEST_DIRS += ['test']
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
|
|
@ -619,7 +619,7 @@ this.UITour = {
|
|||
},
|
||||
|
||||
isSafeScheme: function(aURI) {
|
||||
let allowedSchemes = new Set(["https"]);
|
||||
let allowedSchemes = new Set(["https", "about"]);
|
||||
if (!Services.prefs.getBoolPref("browser.uitour.requireSecure"))
|
||||
allowedSchemes.add("http");
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
# 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/.
|
||||
|
||||
TEST_DIRS += ['test']
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/unit/social/xpcshell.ini']
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'BrowserNewTabPreloader.jsm',
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += ['chrome', 'unit']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['social/xpcshell.ini']
|
|
@ -18,6 +18,8 @@
|
|||
#include "nsID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#include "nsIObjectInputStream.h"
|
||||
#include "nsIObjectOutputStream.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
@ -66,11 +68,24 @@ nsNullPrincipal::~nsNullPrincipal()
|
|||
{
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsNullPrincipal>
|
||||
nsNullPrincipal::CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom)
|
||||
{
|
||||
nsRefPtr<nsNullPrincipal> nullPrin = new nsNullPrincipal();
|
||||
nsresult rv = nullPrin->Init(aInheritFrom->GetAppId(),
|
||||
aInheritFrom->GetIsInBrowserElement());
|
||||
return NS_SUCCEEDED(rv) ? nullPrin.forget() : nullptr;
|
||||
}
|
||||
|
||||
#define NS_NULLPRINCIPAL_PREFIX NS_NULLPRINCIPAL_SCHEME ":"
|
||||
|
||||
nsresult
|
||||
nsNullPrincipal::Init()
|
||||
nsNullPrincipal::Init(uint32_t aAppId, bool aInMozBrowser)
|
||||
{
|
||||
MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
mAppId = aAppId;
|
||||
mInMozBrowser = aInMozBrowser;
|
||||
|
||||
// FIXME: bug 327161 -- make sure the uuid generator is reseeding-resistant.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
||||
|
@ -256,21 +271,21 @@ nsNullPrincipal::GetJarPrefix(nsACString& aJarPrefix)
|
|||
NS_IMETHODIMP
|
||||
nsNullPrincipal::GetAppStatus(uint16_t* aAppStatus)
|
||||
{
|
||||
*aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
*aAppStatus = nsScriptSecurityManager::AppStatusForPrincipal(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::GetAppId(uint32_t* aAppId)
|
||||
{
|
||||
*aAppId = nsIScriptSecurityManager::NO_APP_ID;
|
||||
*aAppId = mAppId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::GetIsInBrowserElement(bool* aIsInBrowserElement)
|
||||
{
|
||||
*aIsInBrowserElement = false;
|
||||
*aIsInBrowserElement = mInMozBrowser;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -301,16 +316,24 @@ nsNullPrincipal::GetBaseDomain(nsACString& aBaseDomain)
|
|||
NS_IMETHODIMP
|
||||
nsNullPrincipal::Read(nsIObjectInputStream* aStream)
|
||||
{
|
||||
// no-op: CID is sufficient to create a useful nsNullPrincipal, since the URI
|
||||
// is not really relevant.
|
||||
// Note - nsNullPrincipal use NS_GENERIC_FACTORY_CONSTRUCTOR_INIT, which means
|
||||
// that the Init() method has already been invoked by the time we deserialize.
|
||||
// This is in contrast to nsPrincipal, which uses NS_GENERIC_FACTORY_CONSTRUCTOR,
|
||||
// in which case ::Read needs to invoke Init().
|
||||
nsresult rv = aStream->Read32(&mAppId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aStream->ReadBoolean(&mInMozBrowser);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::Write(nsIObjectOutputStream* aStream)
|
||||
{
|
||||
// no-op: CID is sufficient to create a useful nsNullPrincipal, since the URI
|
||||
// is not really relevant.
|
||||
aStream->Write32(mAppId);
|
||||
aStream->WriteBoolean(mInMozBrowser);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,14 +14,15 @@
|
|||
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
#define NS_NULLPRINCIPAL_CID \
|
||||
{ 0xdd156d62, 0xd26f, 0x4441, \
|
||||
{ 0x9c, 0xdb, 0xe8, 0xf0, 0x91, 0x07, 0xc2, 0x73 } }
|
||||
{ 0xa0bd8b42, 0xf6bf, 0x4fb9, \
|
||||
{ 0x93, 0x42, 0x90, 0xbf, 0xc9, 0xb7, 0xa1, 0xab } }
|
||||
#define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
|
||||
|
||||
#define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
|
||||
|
@ -41,7 +42,10 @@ public:
|
|||
NS_DECL_NSIPRINCIPAL
|
||||
NS_DECL_NSISERIALIZABLE
|
||||
|
||||
nsresult Init();
|
||||
static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIPrincipal *aInheritFrom);
|
||||
|
||||
nsresult Init(uint32_t aAppId = nsIScriptSecurityManager::NO_APP_ID,
|
||||
bool aInMozBrowser = false);
|
||||
|
||||
virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -54,6 +58,8 @@ public:
|
|||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
uint32_t mAppId;
|
||||
bool mInMozBrowser;
|
||||
};
|
||||
|
||||
#endif // nsNullPrincipal_h__
|
||||
|
|
|
@ -577,48 +577,11 @@ nsPrincipal::Write(nsIObjectOutputStream* aStream)
|
|||
uint16_t
|
||||
nsPrincipal::GetAppStatus()
|
||||
{
|
||||
NS_WARN_IF_FALSE(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||
"Asking for app status on a principal with an unknown app id");
|
||||
// Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
|
||||
// and they are not inside a mozbrowser.
|
||||
if (mAppId == nsIScriptSecurityManager::NO_APP_ID ||
|
||||
mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID || mInMozBrowser) {
|
||||
if (mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
NS_WARNING("Asking for app status on a principal with an unknown app id");
|
||||
return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(appsService, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
appsService->GetAppByLocalId(mAppId, getter_AddRefs(app));
|
||||
NS_ENSURE_TRUE(app, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
uint16_t status = nsIPrincipal::APP_STATUS_INSTALLED;
|
||||
NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
nsAutoCString origin;
|
||||
NS_ENSURE_SUCCESS(GetOrigin(getter_Copies(origin)),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
nsString appOrigin;
|
||||
NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
// We go from string -> nsIURI -> origin to be sure we
|
||||
// compare two punny-encoded origins.
|
||||
nsCOMPtr<nsIURI> appURI;
|
||||
NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
nsAutoCString appOriginPunned;
|
||||
NS_ENSURE_SUCCESS(GetOriginForURI(appURI, getter_Copies(appOriginPunned)),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
if (!appOriginPunned.Equals(origin)) {
|
||||
return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
}
|
||||
|
||||
return status;
|
||||
return nsScriptSecurityManager::AppStatusForPrincipal(this);
|
||||
}
|
||||
|
||||
/************************************************************************************************************************/
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "js/OldDebugAPI.h"
|
||||
#include "xpcprivate.h"
|
||||
#include "XPCWrapper.h"
|
||||
#include "nsIAppsService.h"
|
||||
#include "nsILoadContext.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
|
@ -56,6 +57,7 @@
|
|||
#include "nsIChromeRegistry.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include <stdint.h>
|
||||
|
@ -252,6 +254,57 @@ nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
|
|||
return NS_SecurityHashURI(aURI);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin)
|
||||
{
|
||||
uint32_t appId = aPrin->GetAppId();
|
||||
bool inMozBrowser = aPrin->GetIsInBrowserElement();
|
||||
NS_WARN_IF_FALSE(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
|
||||
"Asking for app status on a principal with an unknown app id");
|
||||
// Installed apps have a valid app id (not NO_APP_ID or UNKNOWN_APP_ID)
|
||||
// and they are not inside a mozbrowser.
|
||||
if (appId == nsIScriptSecurityManager::NO_APP_ID ||
|
||||
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID || inMozBrowser)
|
||||
{
|
||||
return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(appsService, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
nsCOMPtr<mozIApplication> app;
|
||||
appsService->GetAppByLocalId(appId, getter_AddRefs(app));
|
||||
NS_ENSURE_TRUE(app, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
uint16_t status = nsIPrincipal::APP_STATUS_INSTALLED;
|
||||
NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
nsAutoCString origin;
|
||||
NS_ENSURE_SUCCESS(aPrin->GetOrigin(getter_Copies(origin)),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
nsString appOrigin;
|
||||
NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
// We go from string -> nsIURI -> origin to be sure we
|
||||
// compare two punny-encoded origins.
|
||||
nsCOMPtr<nsIURI> appURI;
|
||||
NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
nsAutoCString appOriginPunned;
|
||||
NS_ENSURE_SUCCESS(nsPrincipal::GetOriginForURI(appURI, getter_Copies(appOriginPunned)),
|
||||
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
|
||||
if (!appOriginPunned.Equals(origin)) {
|
||||
return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
|
||||
nsIPrincipal** aPrincipal)
|
||||
|
@ -271,7 +324,11 @@ nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
|
|||
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
if (loadInfo) {
|
||||
if (loadInfo->GetLoadingSandboxed()) {
|
||||
return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, aPrincipal);
|
||||
nsRefPtr<nsNullPrincipal> prin =
|
||||
nsNullPrincipal::CreateWithInheritedAttributes(loadInfo->LoadingPrincipal());
|
||||
NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
|
||||
prin.forget(aPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (loadInfo->GetForceInheritPrincipal()) {
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
static bool SecurityCompareURIs(nsIURI* aSourceURI, nsIURI* aTargetURI);
|
||||
static uint32_t SecurityHashURI(nsIURI* aURI);
|
||||
|
||||
static uint16_t AppStatusForPrincipal(nsIPrincipal *aPrin);
|
||||
|
||||
static nsresult
|
||||
ReportError(JSContext* cx, const nsAString& messageTag,
|
||||
nsIURI* aSource, nsIURI* aTarget);
|
||||
|
|
|
@ -8010,10 +8010,13 @@ if test "$MOZ_TREE_CAIRO"; then
|
|||
XLIB_SURFACE_FEATURE="#define CAIRO_HAS_XLIB_SURFACE 1"
|
||||
XLIB_XRENDER_SURFACE_FEATURE="#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1"
|
||||
PS_SURFACE_FEATURE="#define CAIRO_HAS_PS_SURFACE 1"
|
||||
fi
|
||||
if test "$_HAVE_FREETYPE2"; then
|
||||
FT_FONT_FEATURE="#define CAIRO_HAS_FT_FONT 1"
|
||||
MOZ_ENABLE_CAIRO_FT=1
|
||||
CAIRO_FT_CFLAGS="$FT2_CFLAGS"
|
||||
fi
|
||||
|
||||
case "$MOZ_WIDGET_TOOLKIT" in
|
||||
qt)
|
||||
QT_SURFACE_FEATURE="#define CAIRO_HAS_QT_SURFACE 1"
|
||||
|
|
|
@ -715,7 +715,7 @@ nsCopySupport::FireClipboardEvent(int32_t aType, int32_t aClipboardType, nsIPres
|
|||
// Now that we have copied, update the clipboard commands. This should have
|
||||
// the effect of updating the enabled state of the paste menu item.
|
||||
if (doDefault || count) {
|
||||
piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"));
|
||||
piWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"), nullptr, 0);
|
||||
}
|
||||
|
||||
return doDefault;
|
||||
|
|
|
@ -2749,13 +2749,17 @@ static void ExtractRectFromOffset(nsIFrame* aFrame,
|
|||
}
|
||||
|
||||
static nsTextFrame*
|
||||
GetTextFrameForContent(nsIContent* aContent)
|
||||
GetTextFrameForContent(nsIContent* aContent, bool aFlushLayout)
|
||||
{
|
||||
nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
|
||||
if (presShell) {
|
||||
presShell->FrameConstructor()->EnsureFrameForTextNode(
|
||||
static_cast<nsGenericDOMDataNode*>(aContent));
|
||||
aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
if (aFlushLayout) {
|
||||
aContent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
|
||||
}
|
||||
|
||||
nsIFrame* frame = aContent->GetPrimaryFrame();
|
||||
if (frame && frame->GetType() == nsGkAtoms::textFrame) {
|
||||
return static_cast<nsTextFrame*>(frame);
|
||||
|
@ -2766,9 +2770,10 @@ GetTextFrameForContent(nsIContent* aContent)
|
|||
|
||||
static nsresult GetPartialTextRect(nsLayoutUtils::RectCallback* aCallback,
|
||||
nsIContent* aContent, int32_t aStartOffset,
|
||||
int32_t aEndOffset, bool aClampToEdge)
|
||||
int32_t aEndOffset, bool aClampToEdge,
|
||||
bool aFlushLayout)
|
||||
{
|
||||
nsTextFrame* textFrame = GetTextFrameForContent(aContent);
|
||||
nsTextFrame* textFrame = GetTextFrameForContent(aContent, aFlushLayout);
|
||||
if (textFrame) {
|
||||
nsIFrame* relativeTo = nsLayoutUtils::GetContainingBlockForClientRect(textFrame);
|
||||
for (nsTextFrame* f = textFrame; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
|
||||
|
@ -2801,7 +2806,7 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
|
|||
nsRange* aRange,
|
||||
nsINode* aStartParent, int32_t aStartOffset,
|
||||
nsINode* aEndParent, int32_t aEndOffset,
|
||||
bool aClampToEdge)
|
||||
bool aClampToEdge, bool aFlushLayout)
|
||||
{
|
||||
// Hold strong pointers across the flush
|
||||
nsCOMPtr<nsINode> startContainer = aStartParent;
|
||||
|
@ -2812,11 +2817,12 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
|
|||
return;
|
||||
}
|
||||
|
||||
aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
|
||||
|
||||
// Recheck whether we're still in the document
|
||||
if (!aStartParent->IsInDoc()) {
|
||||
return;
|
||||
if (aFlushLayout) {
|
||||
aStartParent->OwnerDoc()->FlushPendingNotifications(Flush_Layout);
|
||||
// Recheck whether we're still in the document
|
||||
if (!aStartParent->IsInDoc()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RangeSubtreeIterator iter;
|
||||
|
@ -2828,7 +2834,7 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
|
|||
// the range is collapsed, only continue if the cursor is in a text node
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aStartParent);
|
||||
if (content && content->IsNodeOfType(nsINode::eTEXT)) {
|
||||
nsTextFrame* textFrame = GetTextFrameForContent(content);
|
||||
nsTextFrame* textFrame = GetTextFrameForContent(content, aFlushLayout);
|
||||
if (textFrame) {
|
||||
int32_t outOffset;
|
||||
nsIFrame* outFrame;
|
||||
|
@ -2858,10 +2864,12 @@ nsRange::CollectClientRects(nsLayoutUtils::RectCallback* aCollector,
|
|||
if (node == startContainer) {
|
||||
int32_t offset = startContainer == endContainer ?
|
||||
aEndOffset : content->GetText()->GetLength();
|
||||
GetPartialTextRect(aCollector, content, aStartOffset, offset, aClampToEdge);
|
||||
GetPartialTextRect(aCollector, content, aStartOffset, offset,
|
||||
aClampToEdge, aFlushLayout);
|
||||
continue;
|
||||
} else if (node == endContainer) {
|
||||
GetPartialTextRect(aCollector, content, 0, aEndOffset, aClampToEdge);
|
||||
GetPartialTextRect(aCollector, content, 0, aEndOffset,
|
||||
aClampToEdge, aFlushLayout);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -2883,7 +2891,7 @@ nsRange::GetBoundingClientRect(nsIDOMClientRect** aResult)
|
|||
}
|
||||
|
||||
already_AddRefed<DOMRect>
|
||||
nsRange::GetBoundingClientRect(bool aClampToEdge)
|
||||
nsRange::GetBoundingClientRect(bool aClampToEdge, bool aFlushLayout)
|
||||
{
|
||||
nsRefPtr<DOMRect> rect = new DOMRect(ToSupports(this));
|
||||
if (!mStartParent) {
|
||||
|
@ -2892,7 +2900,7 @@ nsRange::GetBoundingClientRect(bool aClampToEdge)
|
|||
|
||||
nsLayoutUtils::RectAccumulator accumulator;
|
||||
CollectClientRects(&accumulator, this, mStartParent, mStartOffset,
|
||||
mEndParent, mEndOffset, aClampToEdge);
|
||||
mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
|
||||
|
||||
nsRect r = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
|
||||
accumulator.mResultRect;
|
||||
|
@ -2908,7 +2916,7 @@ nsRange::GetClientRects(nsIDOMClientRectList** aResult)
|
|||
}
|
||||
|
||||
already_AddRefed<DOMRectList>
|
||||
nsRange::GetClientRects(bool aClampToEdge)
|
||||
nsRange::GetClientRects(bool aClampToEdge, bool aFlushLayout)
|
||||
{
|
||||
if (!mStartParent) {
|
||||
return nullptr;
|
||||
|
@ -2920,7 +2928,7 @@ nsRange::GetClientRects(bool aClampToEdge)
|
|||
nsLayoutUtils::RectListBuilder builder(rectList);
|
||||
|
||||
CollectClientRects(&builder, this, mStartParent, mStartOffset,
|
||||
mEndParent, mEndOffset, aClampToEdge);
|
||||
mEndParent, mEndOffset, aClampToEdge, aFlushLayout);
|
||||
return rectList.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -217,8 +217,10 @@ public:
|
|||
void SetStartAfter(nsINode& aNode, ErrorResult& aErr);
|
||||
void SetStartBefore(nsINode& aNode, ErrorResult& aErr);
|
||||
void SurroundContents(nsINode& aNode, ErrorResult& aErr);
|
||||
already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true);
|
||||
already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true);
|
||||
already_AddRefed<DOMRect> GetBoundingClientRect(bool aClampToEdge = true,
|
||||
bool aFlushLayout = true);
|
||||
already_AddRefed<DOMRectList> GetClientRects(bool aClampToEdge = true,
|
||||
bool aFlushLayout = true);
|
||||
|
||||
nsINode* GetParentObject() const { return mOwner; }
|
||||
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE MOZ_FINAL;
|
||||
|
@ -260,7 +262,7 @@ public:
|
|||
nsRange* aRange,
|
||||
nsINode* aStartParent, int32_t aStartOffset,
|
||||
nsINode* aEndParent, int32_t aEndOffset,
|
||||
bool aClampToEdge);
|
||||
bool aClampToEdge, bool aFlushLayout);
|
||||
|
||||
typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
|
||||
protected:
|
||||
|
|
|
@ -676,7 +676,9 @@ protected:
|
|||
*/
|
||||
virtual ~nsTextInputListener();
|
||||
|
||||
nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate);
|
||||
nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate,
|
||||
nsISelection* sel = nullptr,
|
||||
int16_t reason = 0);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -780,16 +782,18 @@ nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection*
|
|||
}
|
||||
}
|
||||
|
||||
UpdateTextInputCommands(NS_LITERAL_STRING("selectionchange"), aSel, aReason);
|
||||
|
||||
// if the collapsed state did not change, don't fire notifications
|
||||
if (collapsed == mSelectionWasCollapsed)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
mSelectionWasCollapsed = collapsed;
|
||||
|
||||
if (!weakFrame.IsAlive() || !nsContentUtils::IsFocusedContent(mFrame->GetContent()))
|
||||
return NS_OK;
|
||||
|
||||
return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
|
||||
return UpdateTextInputCommands(NS_LITERAL_STRING("select"), aSel, aReason);
|
||||
}
|
||||
|
||||
// END nsIDOMSelectionListener
|
||||
|
@ -930,7 +934,9 @@ nsTextInputListener::EditAction()
|
|||
|
||||
|
||||
nsresult
|
||||
nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
|
||||
nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate,
|
||||
nsISelection* sel,
|
||||
int16_t reason)
|
||||
{
|
||||
nsIContent* content = mFrame->GetContent();
|
||||
NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
|
||||
|
@ -941,7 +947,7 @@ nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
|
|||
nsPIDOMWindow *domWindow = doc->GetWindow();
|
||||
NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
|
||||
|
||||
return domWindow->UpdateCommands(commandsToUpdate);
|
||||
return domWindow->UpdateCommands(commandsToUpdate, sel, reason);
|
||||
}
|
||||
|
||||
// END nsTextInputListener
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "prlog.h"
|
||||
#include "prdtoa.h"
|
||||
#include "AudioStream.h"
|
||||
|
@ -16,6 +17,9 @@
|
|||
#include "mozilla/Telemetry.h"
|
||||
#include "soundtouch/SoundTouch.h"
|
||||
#include "Latency.h"
|
||||
#ifdef XP_MACOSX
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -246,10 +250,10 @@ AudioStream::AudioStream()
|
|||
, mLatencyRequest(HighLatency)
|
||||
, mReadPoint(0)
|
||||
, mDumpFile(nullptr)
|
||||
, mVolume(1.0)
|
||||
, mBytesPerFrame(0)
|
||||
, mState(INITIALIZED)
|
||||
, mNeedsStart(false)
|
||||
, mShouldDropFrames(false)
|
||||
{
|
||||
// keep a ref in case we shut down later than nsLayoutStatics
|
||||
mLatencyLog = AsyncLatencyLogger::Get(true);
|
||||
|
@ -498,8 +502,10 @@ AudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|||
params.channels = mOutChannels;
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(MOZ_B2G)
|
||||
mAudioChannel = aAudioChannel;
|
||||
params.stream_type = ConvertChannelToCubebType(aAudioChannel);
|
||||
#else
|
||||
mAudioChannel = dom::AudioChannel::Content;
|
||||
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
|
||||
#endif
|
||||
|
||||
|
@ -544,6 +550,62 @@ AudioStream::Init(int32_t aNumChannels, int32_t aRate,
|
|||
return rv;
|
||||
}
|
||||
|
||||
// On certain MacBookPro, the microphone is located near the left speaker.
|
||||
// We need to pan the sound output to the right speaker if we are using the mic
|
||||
// and the built-in speaker, or we will have terrible echo.
|
||||
void AudioStream::PanOutputIfNeeded(bool aMicrophoneActive)
|
||||
{
|
||||
#ifdef XP_MACOSX
|
||||
cubeb_device* device;
|
||||
int rv;
|
||||
char name[128];
|
||||
size_t length = sizeof(name);
|
||||
bool panCenter = false;
|
||||
|
||||
rv = sysctlbyname("hw.model", name, &length, NULL, 0);
|
||||
if (rv) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(name, "MacBookPro", 10)) {
|
||||
if (cubeb_stream_get_current_device(mCubebStream, &device) == CUBEB_OK) {
|
||||
// Check if we are currently outputing sound on external speakers.
|
||||
if (!strcmp(device->output_name, "ispk")) {
|
||||
// Pan everything to the right speaker.
|
||||
if (aMicrophoneActive) {
|
||||
LOG(("%p Panning audio output to the right.", this));
|
||||
if (cubeb_stream_set_panning(mCubebStream, 1.0) != CUBEB_OK) {
|
||||
NS_WARNING("Could not pan audio output to the right.");
|
||||
}
|
||||
} else {
|
||||
panCenter = true;
|
||||
}
|
||||
} else {
|
||||
panCenter = true;
|
||||
}
|
||||
if (panCenter) {
|
||||
LOG(("%p Panning audio output to the center.", this));
|
||||
if (cubeb_stream_set_panning(mCubebStream, 0.0) != CUBEB_OK) {
|
||||
NS_WARNING("Could not pan audio output to the center.");
|
||||
}
|
||||
// This a microphone that goes through the headphone plug, reset the
|
||||
// output to prevent echo building up.
|
||||
if (strcmp(device->input_name, "emic") == 0) {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
cubeb_stream_device_destroy(mCubebStream, device);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioStream::DeviceChangedCallback() {
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
PanOutputIfNeeded(mMicrophoneActive);
|
||||
mShouldDropFrames = true;
|
||||
}
|
||||
|
||||
// This code used to live inside AudioStream::Init(), but on Mac (others?)
|
||||
// it has been known to take 300-800 (or even 8500) ms to execute(!)
|
||||
nsresult
|
||||
|
@ -600,12 +662,17 @@ AudioStream::OpenCubeb(cubeb_stream_params &aParams,
|
|||
}
|
||||
}
|
||||
|
||||
cubeb_stream_register_device_changed_callback(mCubebStream,
|
||||
AudioStream::DeviceChangedCallback_s);
|
||||
|
||||
mState = INITIALIZED;
|
||||
|
||||
if (!mStartTime.IsNull()) {
|
||||
TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
|
||||
TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
|
||||
LOG(("AudioStream creation time %sfirst: %u ms", mIsFirst ? "" : "not ",
|
||||
(uint32_t) timeDelta.ToMilliseconds()));
|
||||
Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
|
||||
Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds());
|
||||
(uint32_t) timeDelta.ToMilliseconds()));
|
||||
Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
|
||||
Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -660,15 +727,20 @@ nsresult
|
|||
AudioStream::Write(const AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
|
||||
// See if we need to start() the stream, since we must do that from this thread
|
||||
CheckForStart();
|
||||
|
||||
if (mShouldDropFrames) {
|
||||
mBuffer.ContractTo(0);
|
||||
return NS_OK;
|
||||
}
|
||||
if (mState == ERRORED) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
NS_ASSERTION(mState == INITIALIZED || mState == STARTED || mState == RUNNING,
|
||||
"Stream write in unexpected state.");
|
||||
|
||||
// See if we need to start() the stream, since we must do that from this thread
|
||||
CheckForStart();
|
||||
|
||||
// Downmix to Stereo.
|
||||
if (mChannels > 2 && mChannels <= 8) {
|
||||
DownmixAudioToStereo(const_cast<AudioDataValue*> (aBuf), mChannels, aFrames);
|
||||
|
@ -751,9 +823,21 @@ AudioStream::Available()
|
|||
void
|
||||
AudioStream::SetVolume(double aVolume)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
|
||||
mVolume = aVolume;
|
||||
|
||||
if (cubeb_stream_set_volume(mCubebStream, aVolume * GetVolumeScale()) != CUBEB_OK) {
|
||||
NS_WARNING("Could not change volume on cubeb stream.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioStream::SetMicrophoneActive(bool aActive)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
|
||||
mMicrophoneActive = aActive;
|
||||
|
||||
PanOutputIfNeeded(mMicrophoneActive);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -794,11 +878,14 @@ AudioStream::StartUnlocked()
|
|||
mNeedsStart = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mState == INITIALIZED) {
|
||||
int r;
|
||||
{
|
||||
MonitorAutoUnlock mon(mMonitor);
|
||||
r = cubeb_stream_start(mCubebStream);
|
||||
|
||||
PanOutputIfNeeded(mMicrophoneActive);
|
||||
}
|
||||
mState = r == CUBEB_OK ? STARTED : ERRORED;
|
||||
LOG(("AudioStream: started %p, state %s", this, mState == STARTED ? "STARTED" : "ERRORED"));
|
||||
|
@ -1037,6 +1124,59 @@ AudioStream::GetTimeStretched(void* aBuffer, long aFrames, int64_t &aTimeMs)
|
|||
return processedFrames;
|
||||
}
|
||||
|
||||
void
|
||||
AudioStream::Reset()
|
||||
{
|
||||
mShouldDropFrames = true;
|
||||
mNeedsStart = true;
|
||||
|
||||
cubeb_stream_params params;
|
||||
params.rate = mInRate;
|
||||
params.channels = mOutChannels;
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(MOZ_B2G)
|
||||
params.stream_type = ConvertChannelToCubebType(mAudioChannel);
|
||||
#else
|
||||
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
|
||||
#endif
|
||||
|
||||
if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
} else {
|
||||
params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
}
|
||||
mBytesPerFrame = sizeof(AudioDataValue) * mOutChannels;
|
||||
|
||||
// Size mBuffer for one second of audio. This value is arbitrary, and was
|
||||
// selected based on the observed behaviour of the existing AudioStream
|
||||
// implementations.
|
||||
uint32_t bufferLimit = FramesToBytes(mInRate);
|
||||
NS_ABORT_IF_FALSE(bufferLimit % mBytesPerFrame == 0, "Must buffer complete frames");
|
||||
mBuffer.Reset();
|
||||
mBuffer.SetCapacity(bufferLimit);
|
||||
|
||||
|
||||
if (mLatencyRequest == LowLatency) {
|
||||
// Don't block this thread to initialize a cubeb stream.
|
||||
// When this is done, it will start callbacks from Cubeb. Those will
|
||||
// cause us to move from INITIALIZED to RUNNING. Until then, we
|
||||
// can't access any cubeb functions.
|
||||
// Use a RefPtr to avoid leaks if Dispatch fails
|
||||
RefPtr<AudioInitTask> init = new AudioInitTask(this, mLatencyRequest, params);
|
||||
init->Dispatch();
|
||||
return;
|
||||
}
|
||||
// High latency - open synchronously
|
||||
OpenCubeb(params, mLatencyRequest);
|
||||
|
||||
CheckForStart();
|
||||
}
|
||||
|
||||
long
|
||||
AudioStream::DataCallback(void* aBuffer, long aFrames)
|
||||
{
|
||||
|
@ -1049,6 +1189,8 @@ AudioStream::DataCallback(void* aBuffer, long aFrames)
|
|||
uint32_t servicedFrames = 0;
|
||||
int64_t insertTime;
|
||||
|
||||
mShouldDropFrames = false;
|
||||
|
||||
// NOTE: wasapi (others?) can call us back *after* stop()/Shutdown() (mState == SHUTDOWN)
|
||||
// Bug 996162
|
||||
|
||||
|
@ -1099,9 +1241,6 @@ AudioStream::DataCallback(void* aBuffer, long aFrames)
|
|||
} else {
|
||||
servicedFrames = GetTimeStretched(output, aFrames, insertTime);
|
||||
}
|
||||
float scaled_volume = float(GetVolumeScale() * mVolume);
|
||||
|
||||
ScaleAudioSamples(output, aFrames * mOutChannels, scaled_volume);
|
||||
|
||||
NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
|
||||
|
||||
|
|
|
@ -158,6 +158,14 @@ public:
|
|||
return amount;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mBuffer = nullptr;
|
||||
mCapacity = 0;
|
||||
mStart = 0;
|
||||
mCount = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoArrayPtr<uint8_t> mBuffer;
|
||||
uint32_t mCapacity;
|
||||
|
@ -169,8 +177,8 @@ class AudioInitTask;
|
|||
|
||||
// Access to a single instance of this class must be synchronized by
|
||||
// callers, or made from a single thread. One exception is that access to
|
||||
// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}
|
||||
// is thread-safe without external synchronization.
|
||||
// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels},
|
||||
// SetMicrophoneActive is thread-safe without external synchronization.
|
||||
class AudioStream MOZ_FINAL
|
||||
{
|
||||
virtual ~AudioStream();
|
||||
|
@ -212,6 +220,8 @@ public:
|
|||
// Closes the stream. All future use of the stream is an error.
|
||||
void Shutdown();
|
||||
|
||||
void Reset();
|
||||
|
||||
// Write audio data to the audio hardware. aBuf is an array of AudioDataValues
|
||||
// AudioDataValue of length aFrames*mChannels. If aFrames is larger
|
||||
// than the result of Available(), the write will block until sufficient
|
||||
|
@ -226,6 +236,11 @@ public:
|
|||
// 0 (meaning muted) to 1 (meaning full volume). Thread-safe.
|
||||
void SetVolume(double aVolume);
|
||||
|
||||
// Informs the AudioStream that a microphone is being used by someone in the
|
||||
// application.
|
||||
void SetMicrophoneActive(bool aActive);
|
||||
void PanOutputIfNeeded(bool aMicrophoneActive);
|
||||
|
||||
// Block until buffered audio data has been consumed.
|
||||
void Drain();
|
||||
|
||||
|
@ -304,8 +319,14 @@ private:
|
|||
static_cast<AudioStream*>(aThis)->StateCallback(aState);
|
||||
}
|
||||
|
||||
|
||||
static void DeviceChangedCallback_s(void * aThis) {
|
||||
static_cast<AudioStream*>(aThis)->DeviceChangedCallback();
|
||||
}
|
||||
|
||||
long DataCallback(void* aBuffer, long aFrames);
|
||||
void StateCallback(cubeb_state aState);
|
||||
void DeviceChangedCallback();
|
||||
|
||||
nsresult EnsureTimeStretcherInitializedUnlocked();
|
||||
|
||||
|
@ -331,6 +352,9 @@ private:
|
|||
int mOutRate;
|
||||
int mChannels;
|
||||
int mOutChannels;
|
||||
#if defined(__ANDROID__)
|
||||
dom::AudioChannel mAudioChannel;
|
||||
#endif
|
||||
// Number of frames written to the buffers.
|
||||
int64_t mWritten;
|
||||
AudioClock mAudioClock;
|
||||
|
@ -361,9 +385,6 @@ private:
|
|||
// frames.
|
||||
CircularByteBuffer mBuffer;
|
||||
|
||||
// Software volume level. Applied during the servicing of DataCallback().
|
||||
double mVolume;
|
||||
|
||||
// Owning reference to a cubeb_stream. cubeb_stream_destroy is called by
|
||||
// nsAutoRef's destructor.
|
||||
nsAutoRef<cubeb_stream> mCubebStream;
|
||||
|
@ -397,6 +418,12 @@ private:
|
|||
StreamState mState;
|
||||
bool mNeedsStart; // needed in case Start() is called before cubeb is open
|
||||
bool mIsFirst;
|
||||
// True if a microphone is active.
|
||||
bool mMicrophoneActive;
|
||||
// When we are in the process of changing the output device, and the callback
|
||||
// is not going to be called for a little while, simply drop incoming frames.
|
||||
// This is only on OSX for now, because other systems handle this gracefully.
|
||||
bool mShouldDropFrames;
|
||||
|
||||
// This mutex protects the static members below.
|
||||
static StaticMutex sMutex;
|
||||
|
|
|
@ -110,6 +110,7 @@ MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure that mFirstCycleBreaker and mMixer are updated when necessary.
|
||||
SetStreamOrderDirty();
|
||||
|
||||
mStreams.RemoveElement(aStream);
|
||||
|
@ -568,8 +569,18 @@ MediaStreamGraphImpl::UpdateStreamOrder()
|
|||
|
||||
if (!mMixer && shouldMix) {
|
||||
mMixer = new AudioMixer(AudioMixerCallback);
|
||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
||||
for (uint32_t i = 0; i < mStreams[i]->mAudioOutputStreams.Length(); ++i) {
|
||||
mStreams[i]->mAudioOutputStreams[i].mStream->SetMicrophoneActive(true);
|
||||
}
|
||||
}
|
||||
} else if (mMixer && !shouldMix) {
|
||||
mMixer = nullptr;
|
||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
||||
for (uint32_t i = 0; i < mStreams[i]->mAudioOutputStreams.Length(); ++i) {
|
||||
mStreams[i]->mAudioOutputStreams[i].mStream->SetMicrophoneActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The algorithm for finding cycles is based on Tim Leslie's iterative
|
||||
|
@ -692,17 +703,18 @@ MediaStreamGraphImpl::UpdateStreamOrder()
|
|||
MOZ_ASSERT(cycleStackMarker == ps->mCycleMarker);
|
||||
// If there are DelayNodes in this SCC, then they may break the cycles.
|
||||
bool haveDelayNode = false;
|
||||
auto next = static_cast<ProcessedMediaStream*>(sccStack.getFirst());
|
||||
auto next = sccStack.getFirst();
|
||||
// Streams in this SCC are identified by mCycleMarker <= cycleStackMarker.
|
||||
// (There may be other streams later in sccStack from other incompletely
|
||||
// searched SCCs, involving streams still on dfsStack.)
|
||||
//
|
||||
// DelayNodes in cycles must behave differently from those not in cycles,
|
||||
// so all DelayNodes in the SCC must be identified.
|
||||
while (next && next->mCycleMarker <= cycleStackMarker) {
|
||||
while (next && static_cast<ProcessedMediaStream*>(next)->
|
||||
mCycleMarker <= cycleStackMarker) {
|
||||
auto ns = next->AsAudioNodeStream();
|
||||
// Get next before perhaps removing from list below.
|
||||
next = static_cast<ProcessedMediaStream*>(next->getNext());
|
||||
next = next->getNext();
|
||||
if (ns && ns->Engine()->AsDelayNodeEngine()) {
|
||||
haveDelayNode = true;
|
||||
// DelayNodes break cycles by producing their output in a
|
||||
|
@ -716,8 +728,9 @@ MediaStreamGraphImpl::UpdateStreamOrder()
|
|||
}
|
||||
}
|
||||
auto after_scc = next;
|
||||
while ((next = static_cast<ProcessedMediaStream*>(sccStack.popFirst()))
|
||||
!= after_scc) {
|
||||
while ((next = sccStack.getFirst()) != after_scc) {
|
||||
next->remove();
|
||||
auto removed = static_cast<ProcessedMediaStream*>(next);
|
||||
if (haveDelayNode) {
|
||||
// Return streams to the DFS stack again (to order and detect cycles
|
||||
// without delayNodes). Any of these streams that are still inputs
|
||||
|
@ -726,14 +739,14 @@ MediaStreamGraphImpl::UpdateStreamOrder()
|
|||
// of these streams need input from streams on the visited stack, so
|
||||
// they can all be searched and ordered before the current stack head
|
||||
// is popped.
|
||||
next->mCycleMarker = NOT_VISITED;
|
||||
dfsStack.insertFront(next);
|
||||
removed->mCycleMarker = NOT_VISITED;
|
||||
dfsStack.insertFront(removed);
|
||||
} else {
|
||||
// Streams in cycles without any DelayNodes must be muted, and so do
|
||||
// not need input and can be ordered now. They must be ordered before
|
||||
// their consumers so that their muted output is available.
|
||||
next->mCycleMarker = IN_MUTED_CYCLE;
|
||||
mStreams[orderedStreamCount] = next;
|
||||
removed->mCycleMarker = IN_MUTED_CYCLE;
|
||||
mStreams[orderedStreamCount] = removed;
|
||||
++orderedStreamCount;
|
||||
}
|
||||
}
|
||||
|
@ -950,6 +963,9 @@ MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTim
|
|||
AudioStream::LowLatency);
|
||||
audioOutputStream->mTrackID = tracks->GetID();
|
||||
|
||||
// If there is a mixer, there is a micrphone active.
|
||||
audioOutputStream->mStream->SetMicrophoneActive(mMixer);
|
||||
|
||||
LogLatency(AsyncLatencyLogger::AudioStreamCreate,
|
||||
reinterpret_cast<uint64_t>(aStream),
|
||||
reinterpret_cast<int64_t>(audioOutputStream->mStream.get()));
|
||||
|
@ -2034,8 +2050,6 @@ MediaStream::RemoveAllListenersImpl()
|
|||
void
|
||||
MediaStream::DestroyImpl()
|
||||
{
|
||||
RemoveAllListenersImpl();
|
||||
|
||||
for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
|
||||
mConsumers[i]->Disconnect();
|
||||
}
|
||||
|
@ -2043,6 +2057,7 @@ MediaStream::DestroyImpl()
|
|||
mAudioOutputStreams[i].mStream->Shutdown();
|
||||
}
|
||||
mAudioOutputStreams.Clear();
|
||||
mGraph = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2056,8 +2071,10 @@ MediaStream::Destroy()
|
|||
Message(MediaStream* aStream) : ControlMessage(aStream) {}
|
||||
virtual void Run()
|
||||
{
|
||||
mStream->RemoveAllListenersImpl();
|
||||
auto graph = mStream->GraphImpl();
|
||||
mStream->DestroyImpl();
|
||||
mStream->GraphImpl()->RemoveStream(mStream);
|
||||
graph->RemoveStream(mStream);
|
||||
}
|
||||
virtual void RunDuringShutdown()
|
||||
{ Run(); }
|
||||
|
@ -2338,10 +2355,9 @@ MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, Media
|
|||
void
|
||||
SourceMediaStream::DestroyImpl()
|
||||
{
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mDestroyed = true;
|
||||
}
|
||||
// Hold mMutex while mGraph is reset so that other threads holding mMutex
|
||||
// can null-check know that the graph will not destroyed.
|
||||
MutexAutoLock lock(mMutex);
|
||||
MediaStream::DestroyImpl();
|
||||
}
|
||||
|
||||
|
@ -2350,7 +2366,7 @@ SourceMediaStream::SetPullEnabled(bool aEnabled)
|
|||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mPullEnabled = aEnabled;
|
||||
if (mPullEnabled && !mDestroyed) {
|
||||
if (mPullEnabled && GraphImpl()) {
|
||||
GraphImpl()->EnsureNextIteration();
|
||||
}
|
||||
}
|
||||
|
@ -2370,8 +2386,8 @@ SourceMediaStream::AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
|
|||
data->mCommands = TRACK_CREATE;
|
||||
data->mData = aSegment;
|
||||
data->mHaveEnough = false;
|
||||
if (!mDestroyed) {
|
||||
GraphImpl()->EnsureNextIteration();
|
||||
if (auto graph = GraphImpl()) {
|
||||
graph->EnsureNextIteration();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2413,7 +2429,8 @@ SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegme
|
|||
MutexAutoLock lock(mMutex);
|
||||
// ::EndAllTrackAndFinished() can end these before the sources notice
|
||||
bool appended = false;
|
||||
if (!mFinished) {
|
||||
auto graph = GraphImpl();
|
||||
if (!mFinished && graph) {
|
||||
TrackData *track = FindDataForTrack(aID);
|
||||
if (track) {
|
||||
// Data goes into mData, and on the next iteration of the MSG moves
|
||||
|
@ -2432,13 +2449,11 @@ SourceMediaStream::AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegme
|
|||
NotifyDirectConsumers(track, aRawSegment ? aRawSegment : aSegment);
|
||||
track->mData->AppendFrom(aSegment); // note: aSegment is now dead
|
||||
appended = true;
|
||||
graph->EnsureNextIteration();
|
||||
} else {
|
||||
aSegment->Clear();
|
||||
}
|
||||
}
|
||||
if (!mDestroyed) {
|
||||
GraphImpl()->EnsureNextIteration();
|
||||
}
|
||||
return appended;
|
||||
}
|
||||
|
||||
|
@ -2535,8 +2550,8 @@ SourceMediaStream::EndTrack(TrackID aID)
|
|||
track->mCommands |= TRACK_END;
|
||||
}
|
||||
}
|
||||
if (!mDestroyed) {
|
||||
GraphImpl()->EnsureNextIteration();
|
||||
if (auto graph = GraphImpl()) {
|
||||
graph->EnsureNextIteration();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2546,8 +2561,8 @@ SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
|
|||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(aKnownTime >= mUpdateKnownTracksTime);
|
||||
mUpdateKnownTracksTime = aKnownTime;
|
||||
if (!mDestroyed) {
|
||||
GraphImpl()->EnsureNextIteration();
|
||||
if (auto graph = GraphImpl()) {
|
||||
graph->EnsureNextIteration();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2556,8 +2571,8 @@ SourceMediaStream::FinishWithLockHeld()
|
|||
{
|
||||
mMutex.AssertCurrentThreadOwns();
|
||||
mUpdateFinished = true;
|
||||
if (!mDestroyed) {
|
||||
GraphImpl()->EnsureNextIteration();
|
||||
if (auto graph = GraphImpl()) {
|
||||
graph->EnsureNextIteration();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2660,6 +2675,7 @@ MediaInputPort::Destroy()
|
|||
{
|
||||
mPort->Disconnect();
|
||||
--mPort->GraphImpl()->mPortCount;
|
||||
mPort->SetGraphImpl(nullptr);
|
||||
NS_RELEASE(mPort);
|
||||
}
|
||||
virtual void RunDuringShutdown()
|
||||
|
@ -2686,7 +2702,7 @@ MediaInputPort::Graph()
|
|||
void
|
||||
MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
|
||||
{
|
||||
MOZ_ASSERT(!mGraph, "Should only be called once");
|
||||
MOZ_ASSERT(!mGraph || !aGraph, "Should only be set once");
|
||||
mGraph = aGraph;
|
||||
}
|
||||
|
||||
|
@ -2759,7 +2775,10 @@ ProcessedMediaStream::DestroyImpl()
|
|||
mInputs[i]->Disconnect();
|
||||
}
|
||||
MediaStream::DestroyImpl();
|
||||
GraphImpl()->SetStreamOrderDirty();
|
||||
// The stream order is only important if there are connections, in which
|
||||
// case MediaInputPort::Disconnect() called SetStreamOrderDirty().
|
||||
// MediaStreamGraphImpl::RemoveStream() will also call
|
||||
// SetStreamOrderDirty(), for other reasons.
|
||||
}
|
||||
|
||||
MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime, TrackRate aSampleRate)
|
||||
|
|
|
@ -677,7 +677,7 @@ protected:
|
|||
bool mMainThreadFinished;
|
||||
bool mMainThreadDestroyed;
|
||||
|
||||
// Our media stream graph
|
||||
// Our media stream graph. null if destroyed on the graph thread.
|
||||
MediaStreamGraphImpl* mGraph;
|
||||
|
||||
dom::AudioChannel mAudioChannelType;
|
||||
|
@ -697,7 +697,7 @@ public:
|
|||
mMutex("mozilla::media::SourceMediaStream"),
|
||||
mUpdateKnownTracksTime(0),
|
||||
mPullEnabled(false),
|
||||
mUpdateFinished(false), mDestroyed(false)
|
||||
mUpdateFinished(false)
|
||||
{}
|
||||
|
||||
virtual SourceMediaStream* AsSourceStream() { return this; }
|
||||
|
@ -727,8 +727,6 @@ public:
|
|||
void AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
|
||||
MediaSegment* aSegment);
|
||||
|
||||
struct TrackData;
|
||||
void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
|
||||
/**
|
||||
* Append media data to a track. Ownership of aSegment remains with the caller,
|
||||
* but aSegment is emptied.
|
||||
|
@ -795,10 +793,13 @@ public:
|
|||
*/
|
||||
TrackTicks GetBufferedTicks(TrackID aID);
|
||||
|
||||
void RegisterForAudioMixing();
|
||||
|
||||
// XXX need a Reset API
|
||||
|
||||
friend class MediaStreamGraphImpl;
|
||||
|
||||
protected:
|
||||
struct ThreadAndRunnable {
|
||||
void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable)
|
||||
{
|
||||
|
@ -840,10 +841,10 @@ public:
|
|||
bool mHaveEnough;
|
||||
};
|
||||
|
||||
void RegisterForAudioMixing();
|
||||
bool NeedsMixing();
|
||||
|
||||
protected:
|
||||
void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
|
||||
|
||||
TrackData* FindDataForTrack(TrackID aID)
|
||||
{
|
||||
for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
|
||||
|
@ -875,7 +876,6 @@ protected:
|
|||
nsTArray<nsRefPtr<MediaStreamDirectListener> > mDirectListeners;
|
||||
bool mPullEnabled;
|
||||
bool mUpdateFinished;
|
||||
bool mDestroyed;
|
||||
bool mNeedsMixing;
|
||||
};
|
||||
|
||||
|
|
|
@ -97,6 +97,10 @@ FFmpegDataDecoder<LIBAV_VER>::Init()
|
|||
}
|
||||
mCodecContext->extradata = mExtraData.begin();
|
||||
|
||||
if (codec->capabilities & CODEC_CAP_DR1) {
|
||||
mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
|
||||
}
|
||||
|
||||
if (avcodec_open2(mCodecContext, codec, nullptr) < 0) {
|
||||
NS_WARNING("Couldn't initialise ffmpeg decoder");
|
||||
return NS_ERROR_FAILURE;
|
||||
|
|
|
@ -78,20 +78,45 @@ FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
|
|||
|
||||
// If we've decoded a frame then we need to output it
|
||||
if (decoded) {
|
||||
nsAutoPtr<VideoData> data;
|
||||
|
||||
VideoInfo info;
|
||||
info.mDisplay = nsIntSize(mCodecContext->width, mCodecContext->height);
|
||||
info.mStereoMode = StereoMode::MONO;
|
||||
info.mHasVideo = true;
|
||||
|
||||
data = VideoData::CreateFromImage(
|
||||
info, mImageContainer, aSample->byte_offset, mFrame->pkt_pts,
|
||||
aSample->duration, static_cast<Image*>(mFrame->opaque),
|
||||
aSample->is_sync_point, -1,
|
||||
gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
|
||||
VideoData::YCbCrBuffer b;
|
||||
b.mPlanes[0].mData = mFrame->data[0];
|
||||
b.mPlanes[0].mStride = mFrame->linesize[0];
|
||||
b.mPlanes[0].mHeight = mFrame->height;
|
||||
b.mPlanes[0].mWidth = mFrame->width;
|
||||
b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
|
||||
|
||||
mCallback->Output(data.forget());
|
||||
b.mPlanes[1].mData = mFrame->data[1];
|
||||
b.mPlanes[1].mStride = mFrame->linesize[1];
|
||||
b.mPlanes[1].mHeight = (mFrame->height + 1) >> 1;
|
||||
b.mPlanes[1].mWidth = (mFrame->width + 1) >> 1;
|
||||
b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
|
||||
|
||||
b.mPlanes[2].mData = mFrame->data[2];
|
||||
b.mPlanes[2].mStride = mFrame->linesize[2];
|
||||
b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
|
||||
b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
|
||||
b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
|
||||
|
||||
VideoData *v = VideoData::Create(info,
|
||||
mImageContainer,
|
||||
aSample->byte_offset,
|
||||
mFrame->pkt_pts,
|
||||
aSample->duration,
|
||||
b,
|
||||
aSample->is_sync_point,
|
||||
-1,
|
||||
gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
|
||||
if (!v) {
|
||||
NS_WARNING("image allocation error.");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
mCallback->Output(v);
|
||||
}
|
||||
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
|
@ -99,27 +124,6 @@ FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
PlanarYCbCrDataFromAVFrame(mozilla::layers::PlanarYCbCrData& aData,
|
||||
AVFrame* aFrame)
|
||||
{
|
||||
aData.mPicX = aData.mPicY = 0;
|
||||
aData.mPicSize = mozilla::gfx::IntSize(aFrame->width, aFrame->height);
|
||||
aData.mStereoMode = StereoMode::MONO;
|
||||
|
||||
aData.mYChannel = aFrame->data[0];
|
||||
aData.mYStride = aFrame->linesize[0];
|
||||
aData.mYSize = aData.mPicSize;
|
||||
aData.mYSkip = 0;
|
||||
|
||||
aData.mCbChannel = aFrame->data[1];
|
||||
aData.mCrChannel = aFrame->data[2];
|
||||
aData.mCbCrStride = aFrame->linesize[1];
|
||||
aData.mCbSkip = aData.mCrSkip = 0;
|
||||
aData.mCbCrSize =
|
||||
mozilla::gfx::IntSize((aFrame->width + 1) / 2, (aFrame->height + 1) / 2);
|
||||
}
|
||||
|
||||
/* static */ int
|
||||
FFmpegH264Decoder<LIBAV_VER>::AllocateBufferCb(AVCodecContext* aCodecContext,
|
||||
AVFrame* aFrame)
|
||||
|
@ -159,16 +163,17 @@ int
|
|||
FFmpegH264Decoder<LIBAV_VER>::AllocateYUV420PVideoBuffer(
|
||||
AVCodecContext* aCodecContext, AVFrame* aFrame)
|
||||
{
|
||||
// Older versions of ffmpeg require that edges be allocated* around* the
|
||||
// actual image.
|
||||
int edgeWidth = avcodec_get_edge_width();
|
||||
bool needAlign = aCodecContext->codec->capabilities & CODEC_CAP_DR1;
|
||||
int edgeWidth = needAlign ? avcodec_get_edge_width() : 0;
|
||||
int decodeWidth = aCodecContext->width + edgeWidth * 2;
|
||||
int decodeHeight = aCodecContext->height + edgeWidth * 2;
|
||||
|
||||
// Align width and height to possibly speed up decode.
|
||||
int stride_align[AV_NUM_DATA_POINTERS];
|
||||
avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight,
|
||||
stride_align);
|
||||
if (needAlign) {
|
||||
// Align width and height to account for CODEC_FLAG_EMU_EDGE.
|
||||
int stride_align[AV_NUM_DATA_POINTERS];
|
||||
avcodec_align_dimensions2(aCodecContext, &decodeWidth, &decodeHeight,
|
||||
stride_align);
|
||||
}
|
||||
|
||||
// Get strides for each plane.
|
||||
av_image_fill_linesizes(aFrame->linesize, aCodecContext->pix_fmt,
|
||||
|
@ -215,10 +220,6 @@ FFmpegH264Decoder<LIBAV_VER>::AllocateYUV420PVideoBuffer(
|
|||
aFrame->width = aCodecContext->width;
|
||||
aFrame->height = aCodecContext->height;
|
||||
|
||||
mozilla::layers::PlanarYCbCrData data;
|
||||
PlanarYCbCrDataFromAVFrame(data, aFrame);
|
||||
ycbcr->SetDataNoCopy(data);
|
||||
|
||||
aFrame->opaque = static_cast<void*>(image.forget().take());
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -231,6 +231,12 @@ private:
|
|||
return mDecoders[mActiveVideoDecoder]->GetReader();
|
||||
}
|
||||
|
||||
void SetMediaSourceDuration(double aDuration) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ErrorResult dummy;
|
||||
mMediaSource->SetDuration(aDuration, dummy);
|
||||
}
|
||||
|
||||
nsTArray<nsRefPtr<SubBufferDecoder>> mPendingDecoders;
|
||||
nsTArray<nsRefPtr<SubBufferDecoder>> mDecoders;
|
||||
|
||||
|
@ -585,8 +591,9 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
|||
if (maxDuration != -1) {
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mDecoder->SetMediaDuration(maxDuration);
|
||||
ErrorResult dummy;
|
||||
mMediaSource->SetDuration(maxDuration, dummy);
|
||||
nsRefPtr<nsIRunnable> task (
|
||||
NS_NewRunnableMethodWithArg<double>(this, &MediaSourceReader::SetMediaSourceDuration, maxDuration));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
*aInfo = mInfo;
|
||||
|
|
|
@ -9,6 +9,7 @@ DIRS += [
|
|||
'gmp',
|
||||
'mediasource',
|
||||
'ogg',
|
||||
'systemservices',
|
||||
'webaudio',
|
||||
'webvtt'
|
||||
]
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "OSXRunLoopSingleton.h"
|
||||
#include <mozilla/StaticMutex.h>
|
||||
#include <mozilla/NullPtr.h>
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <CoreAudio/AudioHardware.h>
|
||||
#include <CoreAudio/HostTime.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
static bool gRunLoopSet = false;
|
||||
static mozilla::StaticMutex gMutex;
|
||||
|
||||
void mozilla_set_coreaudio_notification_runloop_if_needed()
|
||||
{
|
||||
mozilla::StaticMutexAutoLock lock(gMutex);
|
||||
if (gRunLoopSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is needed so that AudioUnit listeners get called on this thread, and
|
||||
* not the main thread. If we don't do that, they are not called, or a crash
|
||||
* occur, depending on the OSX version. */
|
||||
AudioObjectPropertyAddress runloop_address = {
|
||||
kAudioHardwarePropertyRunLoop,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
CFRunLoopRef run_loop = nullptr;
|
||||
|
||||
OSStatus r;
|
||||
r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
|
||||
&runloop_address,
|
||||
0, NULL, sizeof(CFRunLoopRef), &run_loop);
|
||||
if (r != noErr) {
|
||||
NS_WARNING("Could not make global CoreAudio notifications use their own thread.");
|
||||
}
|
||||
|
||||
gRunLoopSet = true;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef OSXRUNLOOPSINGLETON_H_
|
||||
#define OSXRUNLOOPSINGLETON_H_
|
||||
|
||||
#include <mozilla/Types.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This function tells CoreAudio to use its own thread for device change
|
||||
* notifications, and can be called from any thread without external
|
||||
* synchronization. */
|
||||
void MOZ_EXPORT
|
||||
mozilla_set_coreaudio_notification_runloop_if_needed();
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OSXRUNLOOPSINGLETON_H_
|
|
@ -0,0 +1,199 @@
|
|||
/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "OpenSLESProvider.h"
|
||||
#include "prlog.h"
|
||||
#include "nsDebug.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <SLES/OpenSLES_AndroidConfiguration.h>
|
||||
|
||||
// NSPR_LOG_MODULES=OpenSLESProvider:5
|
||||
#undef LOG
|
||||
#undef LOG_ENABLED
|
||||
#if defined(PR_LOGGING)
|
||||
PRLogModuleInfo *gOpenSLESProviderLog;
|
||||
#define LOG(args) PR_LOG(gOpenSLESProviderLog, PR_LOG_DEBUG, args)
|
||||
#define LOG_ENABLED() PR_LOG_TEST(gLoadManagerLog, 5)
|
||||
#else
|
||||
#define LOG(args)
|
||||
#define LOG_ENABLED() (false)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
OpenSLESProvider::OpenSLESProvider()
|
||||
: mLock("OpenSLESProvider.mLock"),
|
||||
mSLEngine(nullptr),
|
||||
mSLEngineUsers(0),
|
||||
mIsRealized(false),
|
||||
mOpenSLESLib(nullptr)
|
||||
{
|
||||
#if defined(PR_LOGGING)
|
||||
if (!gOpenSLESProviderLog)
|
||||
gOpenSLESProviderLog = PR_NewLogModule("OpenSLESProvider");
|
||||
LOG(("OpenSLESProvider being initialized"));
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenSLESProvider::~OpenSLESProvider()
|
||||
{
|
||||
if (mOpenSLESLib) {
|
||||
LOG(("OpenSLES Engine was not properly Destroyed"));
|
||||
(void)dlclose(mOpenSLESLib);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
OpenSLESProvider& OpenSLESProvider::getInstance()
|
||||
{
|
||||
// This doesn't need a Mutex in C++11 or GCC 4.3+, see N2660 and
|
||||
// https://gcc.gnu.org/projects/cxx0x.html
|
||||
static OpenSLESProvider instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/* static */
|
||||
SLresult OpenSLESProvider::Get(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions)
|
||||
{
|
||||
OpenSLESProvider& provider = OpenSLESProvider::getInstance();
|
||||
return provider.GetEngine(aObjectm, aOptionCount, aOptions);
|
||||
}
|
||||
|
||||
SLresult OpenSLESProvider::GetEngine(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
LOG(("Getting OpenSLES engine"));
|
||||
// Bug 1042051: Validate options are the same
|
||||
if (mSLEngine != nullptr) {
|
||||
*aObjectm = mSLEngine;
|
||||
mSLEngineUsers++;
|
||||
LOG(("Returning existing engine, %d users", mSLEngineUsers));
|
||||
return SL_RESULT_SUCCESS;
|
||||
} else {
|
||||
int res = ConstructEngine(aObjectm, aOptionCount, aOptions);
|
||||
if (res == SL_RESULT_SUCCESS) {
|
||||
// Bug 1042051: Store engine options
|
||||
mSLEngine = *aObjectm;
|
||||
mSLEngineUsers++;
|
||||
LOG(("Returning new engine"));
|
||||
} else {
|
||||
LOG(("Error getting engine: %d", res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
SLresult OpenSLESProvider::ConstructEngine(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions)
|
||||
{
|
||||
mLock.AssertCurrentThreadOwns();
|
||||
|
||||
if (!mOpenSLESLib) {
|
||||
mOpenSLESLib = dlopen("libOpenSLES.so", RTLD_LAZY);
|
||||
if (!mOpenSLESLib) {
|
||||
LOG(("Failed to dlopen OpenSLES library"));
|
||||
return SL_RESULT_MEMORY_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
|
||||
SLuint32,
|
||||
const SLEngineOption *,
|
||||
SLuint32,
|
||||
const SLInterfaceID *,
|
||||
const SLboolean *);
|
||||
|
||||
slCreateEngine_t f_slCreateEngine =
|
||||
(slCreateEngine_t)dlsym(mOpenSLESLib, "slCreateEngine");
|
||||
int result = f_slCreateEngine(aObjectm, aOptionCount, aOptions, 0, NULL, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void OpenSLESProvider::Destroy(SLObjectItf * aObjectm)
|
||||
{
|
||||
OpenSLESProvider& provider = OpenSLESProvider::getInstance();
|
||||
provider.DestroyEngine(aObjectm);
|
||||
}
|
||||
|
||||
void OpenSLESProvider::DestroyEngine(SLObjectItf * aObjectm)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
NS_ASSERTION(mOpenSLESLib, "OpenSLES destroy called but library is not open");
|
||||
|
||||
mSLEngineUsers--;
|
||||
LOG(("Freeing engine, %d users left", mSLEngineUsers));
|
||||
if (mSLEngineUsers) {
|
||||
return;
|
||||
}
|
||||
|
||||
(*(*aObjectm))->Destroy(*aObjectm);
|
||||
// This assumes SLObjectItf is a pointer, but given the previous line,
|
||||
// that's a given.
|
||||
*aObjectm = nullptr;
|
||||
|
||||
(void)dlclose(mOpenSLESLib);
|
||||
mOpenSLESLib = nullptr;
|
||||
mIsRealized = false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
SLresult OpenSLESProvider::Realize(SLObjectItf aObjectm)
|
||||
{
|
||||
OpenSLESProvider& provider = OpenSLESProvider::getInstance();
|
||||
return provider.RealizeEngine(aObjectm);
|
||||
}
|
||||
|
||||
SLresult OpenSLESProvider::RealizeEngine(SLObjectItf aObjectm)
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
NS_ASSERTION(mOpenSLESLib, "OpenSLES realize called but library is not open");
|
||||
NS_ASSERTION(aObjectm != nullptr, "OpenSLES realize engine with empty ObjectItf");
|
||||
|
||||
if (mIsRealized) {
|
||||
LOG(("Not realizing already realized engine"));
|
||||
return SL_RESULT_SUCCESS;
|
||||
} else {
|
||||
SLresult res = (*aObjectm)->Realize(aObjectm, SL_BOOLEAN_FALSE);
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
LOG(("Error realizing OpenSLES engine: %d", res));
|
||||
} else {
|
||||
LOG(("Realized OpenSLES engine"));
|
||||
mIsRealized = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
extern "C" {
|
||||
SLresult mozilla_get_sles_engine(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions)
|
||||
{
|
||||
return mozilla::OpenSLESProvider::Get(aObjectm, aOptionCount, aOptions);
|
||||
}
|
||||
|
||||
void mozilla_destroy_sles_engine(SLObjectItf * aObjectm)
|
||||
{
|
||||
mozilla::OpenSLESProvider::Destroy(aObjectm);
|
||||
}
|
||||
|
||||
SLresult mozilla_realize_sles_engine(SLObjectItf aObjectm)
|
||||
{
|
||||
return mozilla::OpenSLESProvider::Realize(aObjectm);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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 _OPENSLESPROVIDER_H_
|
||||
#define _OPENSLESPROVIDER_H_
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <mozilla/Types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern MOZ_EXPORT
|
||||
SLresult mozilla_get_sles_engine(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions);
|
||||
extern MOZ_EXPORT
|
||||
void mozilla_destroy_sles_engine(SLObjectItf * aObjectm);
|
||||
/* Realize is always in synchronous mode. */
|
||||
extern MOZ_EXPORT
|
||||
SLresult mozilla_realize_sles_engine(SLObjectItf aObjectm);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
extern PRLogModuleInfo *gOpenSLESProviderLog;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class OpenSLESProvider {
|
||||
public:
|
||||
static SLresult Get(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions);
|
||||
static void Destroy(SLObjectItf * aObjectm);
|
||||
static SLresult Realize(SLObjectItf aObjectm);
|
||||
private:
|
||||
OpenSLESProvider();
|
||||
~OpenSLESProvider();
|
||||
OpenSLESProvider(OpenSLESProvider const&); // NO IMPLEMENTATION
|
||||
void operator=(OpenSLESProvider const&); // NO IMPLEMENTATION
|
||||
static OpenSLESProvider& getInstance();
|
||||
SLresult GetEngine(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions);
|
||||
SLresult ConstructEngine(SLObjectItf * aObjectm,
|
||||
SLuint32 aOptionCount,
|
||||
const SLEngineOption *aOptions);
|
||||
SLresult RealizeEngine(SLObjectItf aObjectm);
|
||||
void DestroyEngine(SLObjectItf * aObjectm);
|
||||
|
||||
// Protect all our internal variables
|
||||
mozilla::Mutex mLock;
|
||||
SLObjectItf mSLEngine;
|
||||
int mSLEngineUsers;
|
||||
bool mIsRealized;
|
||||
void *mOpenSLESLib;
|
||||
};
|
||||
|
||||
} //namespace
|
||||
#endif // cplusplus
|
||||
|
||||
#endif /* _OPENSLESPROVIDER_H_ */
|
|
@ -0,0 +1,42 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
if CONFIG['MOZ_WEBRTC']:
|
||||
EXPORTS += ['LoadManager.h',
|
||||
'LoadManagerFactory.h',
|
||||
'LoadMonitor.h',
|
||||
]
|
||||
UNIFIED_SOURCES += ['LoadManager.cpp',
|
||||
'LoadManagerFactory.cpp',
|
||||
'LoadMonitor.cpp',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'/media/webrtc/trunk',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'):
|
||||
EXPORTS += [
|
||||
'OpenSLESProvider.h'
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'OpenSLESProvider.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
UNIFIED_SOURCES += ['OSXRunLoopSingleton.cpp']
|
||||
EXPORTS += ['OSXRunLoopSingleton.h']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
CXXFLAGS += [
|
||||
'-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/wilhelm/include',
|
||||
'system/media/wilhelm/include',
|
||||
]
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
|
@ -8,7 +8,7 @@ var gSmallTests = [
|
|||
{ name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
|
||||
{ name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
|
||||
{ name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 },
|
||||
{ name:"small-shot-mp3.mp4", type:"audio/mp4; codecs=mp3", duration:0.34 },
|
||||
// { name:"small-shot-mp3.mp4", type:"audio/mp4; codecs=mp3", duration:0.34 },
|
||||
{ name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
|
||||
{ name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266 },
|
||||
{ name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 },
|
||||
|
|
|
@ -96,9 +96,10 @@ AudioContext::AudioContext(nsPIDOMWindow* aWindow,
|
|||
// bound to the window.
|
||||
mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
|
||||
aNumberOfChannels, aLength, aSampleRate);
|
||||
// We skip calling SetIsOnlyNodeForContext during mDestination's constructor,
|
||||
// because we can only call SetIsOnlyNodeForContext after mDestination has
|
||||
// been set up.
|
||||
// We skip calling SetIsOnlyNodeForContext and the creation of the
|
||||
// audioChannelAgent during mDestination's constructor, because we can only
|
||||
// call them after mDestination has been set up.
|
||||
mDestination->CreateAudioChannelAgent();
|
||||
mDestination->SetIsOnlyNodeForContext(true);
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче