mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
1e5237c994
@ -299,10 +299,10 @@ pref("ui.dragThresholdY", 25);
|
||||
// they're not maintained anywhere else.
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
pref("dom.ipc.tabs.disabled", true);
|
||||
pref("layers.async-pan-zoom.enabled", false);
|
||||
#else
|
||||
pref("dom.ipc.tabs.disabled", false);
|
||||
pref("layers.acceleration.disabled", false);
|
||||
pref("layers.async-pan-zoom.enabled", true);
|
||||
pref("gfx.content.azure.backends", "cairo");
|
||||
#endif
|
||||
|
||||
|
@ -26,9 +26,6 @@
|
||||
#ifdef XP_WIN
|
||||
// we want a wmain entry point
|
||||
#include "nsWindowsWMain.cpp"
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#define strcasecmp _stricmp
|
||||
#endif
|
||||
|
||||
|
@ -36,9 +36,6 @@
|
||||
#define XRE_DONT_SUPPORT_XPSP2
|
||||
#endif
|
||||
#define XRE_WANT_ENVIRON
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#define strcasecmp _stricmp
|
||||
#ifdef MOZ_SANDBOX
|
||||
#include "mozilla/sandboxing/SandboxInitialization.h"
|
||||
|
@ -973,6 +973,26 @@ pref("security.sandbox.windows.log.stackTraceDepth", 0);
|
||||
pref("security.sandbox.content.level", 1);
|
||||
#endif
|
||||
|
||||
#if defined(XP_LINUX) && defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
// This pref is introduced as part of bug 742434, the naming is inspired from
|
||||
// its Windows/Mac counterpart, but on Linux it's an integer which means:
|
||||
// 0 -> "no sandbox"
|
||||
// 1 -> "content sandbox using seccomp-bpf when available"
|
||||
// 2 -> "seccomp-bpf + file broker"
|
||||
// Content sandboxing on Linux is currently in the stage of
|
||||
// 'just getting it enabled', which includes a very permissive whitelist. We
|
||||
// enable seccomp-bpf on nightly to see if everything is running, or if we need
|
||||
// to whitelist more system calls.
|
||||
//
|
||||
// So the purpose of this setting is to allow nightly users to disable the
|
||||
// sandbox while we fix their problems. This way, they won't have to wait for
|
||||
// another nightly release which disables seccomp-bpf again.
|
||||
//
|
||||
// This setting may not be required anymore once we decide to permanently
|
||||
// enable the content sandbox.
|
||||
pref("security.sandbox.content.level", 1);
|
||||
#endif
|
||||
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||
#if defined(MOZ_SANDBOX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
// ID (a UUID when set by gecko) that is used to form the name of a
|
||||
@ -1366,10 +1386,6 @@ pref("browser.tabs.crashReporting.includeURL", false);
|
||||
pref("browser.tabs.crashReporting.emailMe", false);
|
||||
pref("browser.tabs.crashReporting.email", "");
|
||||
|
||||
#ifndef MOZ_MULET
|
||||
pref("layers.async-pan-zoom.enabled", true);
|
||||
#endif
|
||||
|
||||
// Enable e10s add-on interposition by default.
|
||||
pref("extensions.interposition.enabled", true);
|
||||
pref("extensions.interposition.prefetching", true);
|
||||
|
@ -369,7 +369,9 @@ var CookieStore = {
|
||||
}
|
||||
|
||||
for (let pathToNamesMap of this._hosts.get(host).values()) {
|
||||
cookies.push(...pathToNamesMap.values());
|
||||
for (let nameToCookiesMap of pathToNamesMap.values()) {
|
||||
cookies.push(...nameToCookiesMap.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +457,14 @@ var CookieStore = {
|
||||
this._hosts.set(cookie.host, new Map());
|
||||
}
|
||||
|
||||
let pathToNamesMap = this._hosts.get(cookie.host);
|
||||
let originAttributesMap = this._hosts.get(cookie.host);
|
||||
// If cookie.originAttributes is null, originAttributes will be an empty string.
|
||||
let originAttributes = ChromeUtils.originAttributesToSuffix(cookie.originAttributes);
|
||||
if (!originAttributesMap.has(originAttributes)) {
|
||||
originAttributesMap.set(originAttributes, new Map());
|
||||
}
|
||||
|
||||
let pathToNamesMap = originAttributesMap.get(originAttributes);
|
||||
|
||||
if (!pathToNamesMap.has(cookie.path)) {
|
||||
pathToNamesMap.set(cookie.path, new Map());
|
||||
|
@ -69,3 +69,73 @@ add_task(function* () {
|
||||
yield promiseRemoveTab(tab2);
|
||||
});
|
||||
|
||||
// Opens "uri" in a new tab with the provided userContextId and focuses it.
|
||||
// Returns the newly opened tab.
|
||||
function* openTabInUserContext(userContextId) {
|
||||
// Open the tab in the correct userContextId.
|
||||
let tab = gBrowser.addTab("http://example.com", { userContextId });
|
||||
|
||||
// Select tab and make sure its browser is focused.
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.ownerDocument.defaultView.focus();
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
return { tab, browser };
|
||||
}
|
||||
|
||||
function waitForNewCookie() {
|
||||
return new Promise(resolve => {
|
||||
Services.obs.addObserver(function observer(subj, topic, data) {
|
||||
let cookie = subj.QueryInterface(Ci.nsICookie2);
|
||||
if (data == "added") {
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
resolve();
|
||||
}
|
||||
}, "cookie-changed", false);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test() {
|
||||
const USER_CONTEXTS = [
|
||||
"default",
|
||||
"personal",
|
||||
"work",
|
||||
];
|
||||
|
||||
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
const { TabStateFlusher } = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
|
||||
|
||||
// Make sure userContext is enabled.
|
||||
yield SpecialPowers.pushPrefEnv({
|
||||
"set": [ [ "privacy.userContext.enabled", true ] ]
|
||||
});
|
||||
|
||||
let lastSessionRestore;
|
||||
for (let userContextId of Object.keys(USER_CONTEXTS)) {
|
||||
// Load the page in 3 different contexts and set a cookie
|
||||
// which should only be visible in that context.
|
||||
let cookie = USER_CONTEXTS[userContextId];
|
||||
|
||||
// Open our tab in the given user context.
|
||||
let { tab, browser } = yield* openTabInUserContext(userContextId);
|
||||
|
||||
yield Promise.all([
|
||||
waitForNewCookie(),
|
||||
ContentTask.spawn(browser, cookie, cookie => content.document.cookie = cookie)
|
||||
]);
|
||||
|
||||
// Ensure the tab's session history is up-to-date.
|
||||
yield TabStateFlusher.flush(browser);
|
||||
|
||||
lastSessionRestore = ss.getWindowState(window);
|
||||
|
||||
// Remove the tab.
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
let state = JSON.parse(lastSessionRestore);
|
||||
is(state.windows[0].cookies.length, USER_CONTEXTS.length,
|
||||
"session restore should have each container's cookie");
|
||||
});
|
||||
|
||||
|
@ -2430,12 +2430,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.tab-throbber[progress] {
|
||||
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
@media (-moz-mac-yosemite-theme) {
|
||||
/* image preloading hack from shared/tabs.inc.css */
|
||||
#tabbrowser-tabs::before {
|
||||
|
@ -557,4 +557,8 @@
|
||||
.tab-icon-image {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
|
||||
}
|
||||
|
||||
.tab-throbber[progress] {
|
||||
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
|
||||
}
|
||||
}
|
||||
|
@ -1917,12 +1917,6 @@ html|span.ac-emphasize-text-url {
|
||||
|
||||
%include ../shared/tabs.inc.css
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
.tab-throbber[progress] {
|
||||
list-style-image: url("chrome://global/skin/icons/loading@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove border between tab strip and navigation toolbar on Windows 10+ */
|
||||
@media not all and (-moz-os-version: windows-xp) {
|
||||
@media not all and (-moz-os-version: windows-vista) {
|
||||
|
@ -640,4 +640,48 @@ def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
|
||||
set_config('MOZ_DEBUG_FLAGS', debug_flags)
|
||||
add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
|
||||
|
||||
# Some standard library headers (notably bionic on Android) declare standard
|
||||
# functions (e.g. getchar()) and also #define macros for those standard
|
||||
# functions. libc++ deals with this by doing something like the following
|
||||
# (explanatory comments added):
|
||||
#
|
||||
# #ifdef FUNC
|
||||
# // Capture the definition of FUNC.
|
||||
# inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
|
||||
# #undef FUNC
|
||||
# // Use a real inline definition.
|
||||
# inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
|
||||
# #endif
|
||||
#
|
||||
# _LIBCPP_INLINE_VISIBILITY is typically defined as:
|
||||
#
|
||||
# __attribute__((__visibility__("hidden"), __always_inline__))
|
||||
#
|
||||
# Unfortunately, this interacts badly with our system header wrappers, as the:
|
||||
#
|
||||
# #pragma GCC visibility push(default)
|
||||
#
|
||||
# that they do prior to including the actual system header is treated by the
|
||||
# compiler as an explicit declaration of visibility on every function declared
|
||||
# in the header. Therefore, when the libc++ code above is encountered, it is
|
||||
# as though the compiler has effectively seen:
|
||||
#
|
||||
# int FUNC(...) __attribute__((__visibility__("default")));
|
||||
# int FUNC(...) __attribute__((__visibility__("hidden")));
|
||||
#
|
||||
# and the compiler complains about the mismatched visibility declarations.
|
||||
#
|
||||
# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
|
||||
# existing definition. We can therefore define it to the empty string (since
|
||||
# we are properly managing visibility ourselves) and avoid this whole mess.
|
||||
# Note that we don't need to do this with gcc, as libc++ detects gcc and
|
||||
# effectively does the same thing we are doing here.
|
||||
@depends(c_compiler, target)
|
||||
def libcxx_inline_visibility(c_compiler, target):
|
||||
if c_compiler.type == 'clang' and target.os == 'Android':
|
||||
return ''
|
||||
|
||||
set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_inline_visibility)
|
||||
set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49', libcxx_inline_visibility)
|
||||
|
||||
include('rust.configure')
|
||||
|
Binary file not shown.
@ -7,59 +7,74 @@
|
||||
|
||||
const TEST_URI = URL_ROOT + "browser_toolbox_options_disable_js.html";
|
||||
|
||||
var doc;
|
||||
var toolbox;
|
||||
|
||||
function test() {
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
doc = content.document;
|
||||
gDevTools.showToolbox(target).then(testSelectTool);
|
||||
}, true);
|
||||
|
||||
content.location = TEST_URI;
|
||||
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
|
||||
}
|
||||
|
||||
function testSelectTool(aToolbox) {
|
||||
toolbox = aToolbox;
|
||||
toolbox.once("options-selected", testJSEnabled);
|
||||
function testSelectTool(toolbox) {
|
||||
toolbox.once("options-selected", () => testToggleJS(toolbox));
|
||||
toolbox.selectTool("options");
|
||||
}
|
||||
|
||||
function testJSEnabled(event, tool, secondPass) {
|
||||
let testToggleJS = Task.async(function* (toolbox) {
|
||||
ok(true, "Toolbox selected via selectTool method");
|
||||
|
||||
yield testJSEnabled();
|
||||
yield testJSEnabledIframe();
|
||||
|
||||
// Disable JS.
|
||||
yield toggleJS(toolbox);
|
||||
|
||||
yield testJSDisabled();
|
||||
yield testJSDisabledIframe();
|
||||
|
||||
// Re-enable JS.
|
||||
yield toggleJS(toolbox);
|
||||
|
||||
yield testJSEnabled();
|
||||
yield testJSEnabledIframe();
|
||||
|
||||
finishUp(toolbox);
|
||||
});
|
||||
|
||||
function* testJSEnabled() {
|
||||
info("Testing that JS is enabled");
|
||||
|
||||
// We use executeSoon here because switching docSehll.allowJavascript to true
|
||||
// We use waitForTick here because switching docShell.allowJavascript to true
|
||||
// takes a while to become live.
|
||||
executeSoon(function () {
|
||||
yield waitForTick();
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
let doc = content.document;
|
||||
let output = doc.getElementById("output");
|
||||
doc.querySelector("#logJSEnabled").click();
|
||||
is(output.textContent, "JavaScript Enabled", 'Output is "JavaScript Enabled"');
|
||||
testJSEnabledIframe(secondPass);
|
||||
});
|
||||
}
|
||||
|
||||
function testJSEnabledIframe(secondPass) {
|
||||
function* testJSEnabledIframe() {
|
||||
info("Testing that JS is enabled in the iframe");
|
||||
|
||||
let iframe = doc.querySelector("iframe");
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let output = iframeDoc.getElementById("output");
|
||||
iframeDoc.querySelector("#logJSEnabled").click();
|
||||
is(output.textContent, "JavaScript Enabled",
|
||||
'Output is "JavaScript Enabled" in iframe');
|
||||
if (secondPass) {
|
||||
finishUp();
|
||||
} else {
|
||||
toggleJS().then(testJSDisabled);
|
||||
}
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
let doc = content.document;
|
||||
let iframe = doc.querySelector("iframe");
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let output = iframeDoc.getElementById("output");
|
||||
iframeDoc.querySelector("#logJSEnabled").click();
|
||||
is(output.textContent, "JavaScript Enabled",
|
||||
'Output is "JavaScript Enabled" in iframe');
|
||||
});
|
||||
}
|
||||
|
||||
let toggleJS = Task.async(function* () {
|
||||
function* toggleJS(toolbox) {
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
let cbx = panel.panelDoc.getElementById("devtools-disable-javascript");
|
||||
|
||||
@ -72,38 +87,38 @@ let toggleJS = Task.async(function* () {
|
||||
let browserLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
cbx.click();
|
||||
yield browserLoaded;
|
||||
doc = content.document;
|
||||
});
|
||||
|
||||
function testJSDisabled() {
|
||||
info("Testing that JS is disabled");
|
||||
|
||||
let output = doc.getElementById("output");
|
||||
doc.querySelector("#logJSDisabled").click();
|
||||
|
||||
ok(output.textContent !== "JavaScript Disabled",
|
||||
'output is not "JavaScript Disabled"');
|
||||
testJSDisabledIframe();
|
||||
}
|
||||
|
||||
function testJSDisabledIframe() {
|
||||
info("Testing that JS is disabled in the iframe");
|
||||
function* testJSDisabled() {
|
||||
info("Testing that JS is disabled");
|
||||
|
||||
let iframe = doc.querySelector("iframe");
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let output = iframeDoc.getElementById("output");
|
||||
iframeDoc.querySelector("#logJSDisabled").click();
|
||||
ok(output.textContent !== "JavaScript Disabled",
|
||||
'output is not "JavaScript Disabled" in iframe');
|
||||
toggleJS().then(function () {
|
||||
testJSEnabled(null, null, true);
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
let doc = content.document;
|
||||
let output = doc.getElementById("output");
|
||||
doc.querySelector("#logJSDisabled").click();
|
||||
|
||||
ok(output.textContent !== "JavaScript Disabled",
|
||||
'output is not "JavaScript Disabled"');
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
function* testJSDisabledIframe() {
|
||||
info("Testing that JS is disabled in the iframe");
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
|
||||
let doc = content.document;
|
||||
let iframe = doc.querySelector("iframe");
|
||||
let iframeDoc = iframe.contentDocument;
|
||||
let output = iframeDoc.getElementById("output");
|
||||
iframeDoc.querySelector("#logJSDisabled").click();
|
||||
ok(output.textContent !== "JavaScript Disabled",
|
||||
'output is not "JavaScript Disabled" in iframe');
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp(toolbox) {
|
||||
toolbox.destroy().then(function () {
|
||||
gBrowser.removeCurrentTab();
|
||||
toolbox = doc = null;
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ const nodeConstants = require("devtools/shared/dom-node-constants");
|
||||
const MAX_STRING_GRIP_LENGTH = 36;
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
|
||||
const validProtocols = /^(http|https|ftp|data|javascript|resource|chrome):/i;
|
||||
|
||||
// Constants for compatibility with the Web Console output implementation before
|
||||
// bug 778766.
|
||||
// TODO: remove these once bug 778766 is fixed.
|
||||
@ -2223,9 +2225,11 @@ Widgets.URLString.prototype = extend(Widgets.BaseWidget.prototype, {
|
||||
*/
|
||||
_isURL: function (token) {
|
||||
try {
|
||||
let url = new URL(token);
|
||||
return ["http:", "https:", "ftp:", "data:", "javascript:",
|
||||
"resource:", "chrome:"].includes(url.protocol);
|
||||
if (!validProtocols.test(token)) {
|
||||
return false;
|
||||
}
|
||||
new URL(token);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
@ -63,9 +63,9 @@ GetCurrentThreadDebuggerMallocSizeOf()
|
||||
{
|
||||
auto ccrt = CycleCollectedJSRuntime::Get();
|
||||
MOZ_ASSERT(ccrt);
|
||||
auto rt = ccrt->Runtime();
|
||||
MOZ_ASSERT(rt);
|
||||
auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
|
||||
auto cx = ccrt->Context();
|
||||
MOZ_ASSERT(cx);
|
||||
auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(cx);
|
||||
MOZ_ASSERT(mallocSizeOf);
|
||||
return mallocSizeOf;
|
||||
}
|
||||
@ -1246,8 +1246,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
mozilla::MallocSizeOf mallocSizeOf = dbg::GetDebuggerMallocSizeOf(rt);
|
||||
mozilla::MallocSizeOf mallocSizeOf = dbg::GetDebuggerMallocSizeOf(cx);
|
||||
MOZ_ASSERT(mallocSizeOf);
|
||||
protobufNode.set_size(ubiNode.size(mallocSizeOf));
|
||||
|
||||
|
@ -60,11 +60,19 @@ GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly,
|
||||
mTarget,
|
||||
mAnimation,
|
||||
mTiming)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(KeyframeEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly)
|
||||
if (tmp->mTiming) {
|
||||
tmp->mTiming->Unlink();
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTarget, mAnimation, mTiming)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget, mAnimation, mTiming)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly)
|
||||
@ -517,6 +525,29 @@ KeyframeEffectReadOnly::HasAnimationOfProperties(
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
SpecifiedKeyframeArraysAreEqual(const nsTArray<Keyframe>& aA,
|
||||
const nsTArray<Keyframe>& aB)
|
||||
{
|
||||
if (aA.Length() != aB.Length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aA.Length(); i++) {
|
||||
const Keyframe& a = aA[i];
|
||||
const Keyframe& b = aB[i];
|
||||
if (a.mOffset != b.mOffset ||
|
||||
a.mTimingFunction != b.mTimingFunction ||
|
||||
a.mPropertyValues != b.mPropertyValues) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
|
||||
{
|
||||
@ -524,20 +555,37 @@ KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
|
||||
|
||||
nsTArray<AnimationProperty> properties;
|
||||
if (mTarget) {
|
||||
// When GetComputedKeyframeValues or GetAnimationPropertiesFromKeyframes
|
||||
// calculate computed values from |mKeyframes|, they could possibly
|
||||
// trigger a subsequent restyle in which we rebuild animations. If that
|
||||
// happens we could find that |mKeyframes| is overwritten while it is
|
||||
// being iterated over. Normally that shouldn't happen but just in case we
|
||||
// make a copy of |mKeyframes| first and iterate over that instead.
|
||||
auto keyframesCopy(mKeyframes);
|
||||
|
||||
nsTArray<ComputedKeyframeValues> computedValues =
|
||||
KeyframeUtils::GetComputedKeyframeValues(mKeyframes, mTarget->mElement,
|
||||
KeyframeUtils::GetComputedKeyframeValues(keyframesCopy,
|
||||
mTarget->mElement,
|
||||
aStyleContext);
|
||||
|
||||
if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
|
||||
KeyframeUtils::ApplySpacing(mKeyframes, SpacingMode::paced,
|
||||
KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
|
||||
mEffectOptions.mPacedProperty,
|
||||
computedValues);
|
||||
}
|
||||
|
||||
properties =
|
||||
KeyframeUtils::GetAnimationPropertiesFromKeyframes(mKeyframes,
|
||||
KeyframeUtils::GetAnimationPropertiesFromKeyframes(keyframesCopy,
|
||||
computedValues,
|
||||
aStyleContext);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
|
||||
"Apart from the computed offset members, the keyframes array"
|
||||
" should not be modified");
|
||||
#endif
|
||||
|
||||
mKeyframes.SwapElements(keyframesCopy);
|
||||
}
|
||||
|
||||
if (mProperties == properties) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/Attr.h"
|
||||
#include "mozilla/dom/Grid.h"
|
||||
#include "nsDOMAttributeMap.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIContentInlines.h"
|
||||
@ -953,7 +954,6 @@ Element::GetClientRects()
|
||||
return rectList.forget();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
@ -3289,6 +3289,21 @@ Element::MozRequestPointerLock()
|
||||
OwnerDoc()->RequestPointerLock(this);
|
||||
}
|
||||
|
||||
void
|
||||
Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult)
|
||||
{
|
||||
nsIFrame* frame = GetPrimaryFrame();
|
||||
if (frame && (frame->GetType() == nsGkAtoms::gridContainerFrame)) {
|
||||
// If primary frame is a nsGridContainerFrame, all the next frames
|
||||
// in flow will also be nsGridContainerFrame.
|
||||
for (; frame != nullptr; frame = frame->GetNextInFlow()) {
|
||||
aResult.AppendElement(
|
||||
new Grid(this, static_cast<nsGridContainerFrame*>(frame))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Animation>
|
||||
Element::Animate(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
|
@ -140,6 +140,7 @@ class UndoManager;
|
||||
class DOMRect;
|
||||
class DOMRectList;
|
||||
class DestinationInsertionPointList;
|
||||
class Grid;
|
||||
|
||||
// IID for the dom::Element interface
|
||||
#define NS_ELEMENT_IID \
|
||||
@ -830,6 +831,8 @@ public:
|
||||
0;
|
||||
}
|
||||
|
||||
void GetGridFragments(nsTArray<RefPtr<Grid>>& aResult);
|
||||
|
||||
virtual already_AddRefed<UndoManager> GetUndoManager()
|
||||
{
|
||||
return nullptr;
|
||||
|
@ -11152,15 +11152,14 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
||||
buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
|
||||
|
||||
// Null out the operation callback while we're re-entering JS here.
|
||||
JSRuntime* rt = JS_GetRuntime(cx);
|
||||
JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr);
|
||||
JSInterruptCallback old = JS_SetInterruptCallback(cx, nullptr);
|
||||
|
||||
// Open the dialog.
|
||||
rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
|
||||
debugButton, neverShowDlg, &neverShowDlgChk,
|
||||
&buttonPressed);
|
||||
|
||||
JS_SetInterruptCallback(rt, old);
|
||||
JS_SetInterruptCallback(cx, old);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
|
||||
return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback,
|
||||
nsIGlobalObject* aIncumbentGlobal)
|
||||
{
|
||||
if (aCx && JS::RuntimeOptionsRef(aCx).asyncStack()) {
|
||||
if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
|
||||
JS::RootedObject stack(aCx);
|
||||
if (!JS::CaptureCurrentStack(aCx, &stack)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
@ -230,7 +230,7 @@ protected:
|
||||
nsIGlobalObject* aIncumbentGlobal,
|
||||
const FastCallbackConstructor&)
|
||||
{
|
||||
if (aCx && JS::RuntimeOptionsRef(aCx).asyncStack()) {
|
||||
if (aCx && JS::ContextOptionsRef(aCx).asyncStack()) {
|
||||
JS::RootedObject stack(aCx);
|
||||
if (!JS::CaptureCurrentStack(aCx, &stack)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
|
@ -74,6 +74,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/xul',
|
||||
'/js/xpconnect/src',
|
||||
'/js/xpconnect/wrappers',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
'/layout/xul/tree',
|
||||
'/media/mtransport',
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "CameraPreviewMediaStream.h"
|
||||
#include "CameraCommon.h"
|
||||
#include "MediaStreamListener.h"
|
||||
|
||||
/**
|
||||
* Maximum number of outstanding invalidates before we start to drop frames;
|
||||
@ -89,7 +90,7 @@ CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
|
||||
|
||||
RefPtr<MediaStreamListener> listener(aListener);
|
||||
mListeners.RemoveElement(aListener);
|
||||
listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED);
|
||||
listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamGraphEvent::EVENT_REMOVED);
|
||||
}
|
||||
|
||||
void
|
||||
@ -103,7 +104,7 @@ CameraPreviewMediaStream::OnPreviewStateChange(bool aActive)
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0,
|
||||
MediaStreamListener::TRACK_EVENT_CREATED,
|
||||
TrackEventCommand::TRACK_EVENT_CREATED,
|
||||
tmpSegment);
|
||||
l->NotifyFinishedTrackCreation(mFakeMediaStreamGraph);
|
||||
}
|
||||
|
@ -67,12 +67,12 @@ public:
|
||||
}
|
||||
|
||||
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset, uint32_t aTrackEvents,
|
||||
StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream,
|
||||
TrackID aInputTrackID) override
|
||||
{
|
||||
if (aTrackEvents & TRACK_EVENT_CREATED) {
|
||||
if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod<TrackID>(
|
||||
this, &TrackCreatedListener::DoNotifyTrackCreated, aID));
|
||||
}
|
||||
|
@ -1437,9 +1437,9 @@ protected:
|
||||
CheckedUint32 GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint8_t bytesPerPixel);
|
||||
|
||||
CheckedUint32 GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
|
||||
CheckedUint32* const out_startOffset,
|
||||
CheckedUint32* const out_rowStride);
|
||||
bool ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
|
||||
uint8_t bytesPerPixel, uint32_t* const out_rowStride,
|
||||
uint32_t* const out_endOffset);
|
||||
|
||||
GLenum mPixelStore_ColorspaceConversion;
|
||||
bool mPixelStore_FlipY;
|
||||
|
@ -1224,30 +1224,33 @@ WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei he
|
||||
|
||||
if (gl->WorkAroundDriverBugs() &&
|
||||
gl->IsANGLE() &&
|
||||
gl->Version() < 300 && // ANGLE ES2 doesn't support HALF_FLOAT reads properly.
|
||||
readType == LOCAL_GL_FLOAT &&
|
||||
auxReadFormat == destFormat &&
|
||||
auxReadType == LOCAL_GL_HALF_FLOAT)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!IsWebGL2()); // No SKIP_PIXELS, etc.
|
||||
|
||||
readType = auxReadType;
|
||||
|
||||
const char funcName[] = "readPixels";
|
||||
const auto readBytesPerPixel = webgl::BytesPerPixel({readFormat, readType});
|
||||
const auto destBytesPerPixel = webgl::BytesPerPixel({destFormat, destType});
|
||||
|
||||
CheckedUint32 readOffset;
|
||||
CheckedUint32 readStride;
|
||||
const CheckedUint32 readSize = GetPackSize(width, height, readBytesPerPixel,
|
||||
&readOffset, &readStride);
|
||||
|
||||
CheckedUint32 destOffset;
|
||||
CheckedUint32 destStride;
|
||||
const CheckedUint32 destSize = GetPackSize(width, height, destBytesPerPixel,
|
||||
&destOffset, &destStride);
|
||||
if (!readSize.isValid() || !destSize.isValid()) {
|
||||
uint32_t readStride;
|
||||
uint32_t readByteCount;
|
||||
uint32_t destStride;
|
||||
uint32_t destByteCount;
|
||||
if (!ValidatePackSize(funcName, width, height, readBytesPerPixel, &readStride,
|
||||
&readByteCount) ||
|
||||
!ValidatePackSize(funcName, width, height, destBytesPerPixel, &destStride,
|
||||
&destByteCount))
|
||||
{
|
||||
ErrorOutOfMemory("readPixels: Overflow calculating sizes for conversion.");
|
||||
return false;
|
||||
}
|
||||
|
||||
UniqueBuffer readBuffer = malloc(readSize.value());
|
||||
UniqueBuffer readBuffer = malloc(readByteCount);
|
||||
if (!readBuffer) {
|
||||
ErrorOutOfMemory("readPixels: Failed to alloc temp buffer for conversion.");
|
||||
return false;
|
||||
@ -1268,11 +1271,11 @@ WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei he
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t channelsPerRow = std::min(readStride.value() / sizeof(uint16_t),
|
||||
destStride.value() / sizeof(float));
|
||||
size_t channelsPerRow = std::min(readStride / sizeof(uint16_t),
|
||||
destStride / sizeof(float));
|
||||
|
||||
const uint8_t* srcRow = (uint8_t*)(readBuffer.get()) + readOffset.value();
|
||||
uint8_t* dstRow = (uint8_t*)(destBytes) + destOffset.value();
|
||||
const uint8_t* srcRow = (uint8_t*)readBuffer.get();
|
||||
uint8_t* dstRow = (uint8_t*)destBytes;
|
||||
|
||||
for (size_t j = 0; j < (size_t)height; j++) {
|
||||
auto src = (const uint16_t*)srcRow;
|
||||
@ -1285,8 +1288,8 @@ WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei he
|
||||
++dst;
|
||||
}
|
||||
|
||||
srcRow += readStride.value();
|
||||
dstRow += destStride.value();
|
||||
srcRow += readStride;
|
||||
dstRow += destStride;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1391,35 +1394,47 @@ IsIntegerFormatAndTypeUnpackable(GLenum format, GLenum type)
|
||||
}
|
||||
|
||||
|
||||
CheckedUint32
|
||||
WebGLContext::GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
|
||||
CheckedUint32* const out_startOffset,
|
||||
CheckedUint32* const out_rowStride)
|
||||
bool
|
||||
WebGLContext::ValidatePackSize(const char* funcName, uint32_t width, uint32_t height,
|
||||
uint8_t bytesPerPixel, uint32_t* const out_rowStride,
|
||||
uint32_t* const out_endOffset)
|
||||
{
|
||||
if (!width || !height) {
|
||||
*out_startOffset = 0;
|
||||
*out_rowStride = 0;
|
||||
return 0;
|
||||
*out_endOffset = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const CheckedUint32 pixelsPerRow = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
|
||||
: width);
|
||||
const CheckedUint32 skipPixels = mPixelStore_PackSkipPixels;
|
||||
const CheckedUint32 skipRows = mPixelStore_PackSkipRows;
|
||||
const CheckedUint32 alignment = mPixelStore_PackAlignment;
|
||||
|
||||
// GLES 3.0.4, p116 (PACK_ functions like UNPACK_)
|
||||
const auto totalBytesPerRow = bytesPerPixel * pixelsPerRow;
|
||||
const auto rowStride = RoundUpToMultipleOf(totalBytesPerRow, alignment);
|
||||
|
||||
const auto startOffset = rowStride * skipRows + bytesPerPixel * skipPixels;
|
||||
const auto usedBytesPerRow = bytesPerPixel * width;
|
||||
const auto rowLength = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength
|
||||
: width);
|
||||
const auto skipPixels = mPixelStore_PackSkipPixels;
|
||||
const auto skipRows = mPixelStore_PackSkipRows;
|
||||
const auto alignment = mPixelStore_PackAlignment;
|
||||
|
||||
const auto bytesNeeded = startOffset + rowStride * (height - 1) + usedBytesPerRow;
|
||||
const auto usedPixelsPerRow = CheckedUint32(skipPixels) + width;
|
||||
const auto usedRowsPerImage = CheckedUint32(skipRows) + height;
|
||||
|
||||
*out_startOffset = startOffset;
|
||||
*out_rowStride = rowStride;
|
||||
return bytesNeeded;
|
||||
if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > rowLength) {
|
||||
ErrorInvalidOperation("%s: SKIP_PIXELS + width > ROW_LENGTH.", funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto rowLengthBytes = CheckedUint32(rowLength) * bytesPerPixel;
|
||||
const auto rowStride = RoundUpToMultipleOf(rowLengthBytes, alignment);
|
||||
|
||||
const auto usedBytesPerRow = usedPixelsPerRow * bytesPerPixel;
|
||||
const auto usedBytesPerImage = (usedRowsPerImage - 1) * rowStride + usedBytesPerRow;
|
||||
|
||||
if (!rowStride.isValid() || !usedBytesPerImage.isValid()) {
|
||||
ErrorInvalidOperation("%s: Invalid UNPACK_ params.", funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_rowStride = rowStride.value();
|
||||
*out_endOffset = usedBytesPerImage.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1428,6 +1443,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
const dom::Nullable<dom::ArrayBufferView>& pixels,
|
||||
ErrorResult& out_error)
|
||||
{
|
||||
const char funcName[] = "readPixels";
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
@ -1539,6 +1555,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
MOZ_CRASH("GFX: bad `type`");
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
const auto& view = pixels.Value();
|
||||
|
||||
// Compute length and data. Don't reenter after this point, lest the
|
||||
@ -1552,27 +1570,29 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
if (dataType != requiredDataType)
|
||||
return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
|
||||
|
||||
CheckedUint32 startOffset;
|
||||
CheckedUint32 rowStride;
|
||||
const auto bytesNeeded = GetPackSize(width, height, bytesPerPixel, &startOffset,
|
||||
&rowStride);
|
||||
if (!bytesNeeded.isValid()) {
|
||||
ErrorInvalidOperation("readPixels: Integer overflow computing the needed buffer"
|
||||
" size.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesNeeded.value() > bytesAvailable) {
|
||||
ErrorInvalidOperation("readPixels: buffer too small");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?");
|
||||
out_error.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
uint32_t rowStride;
|
||||
uint32_t bytesNeeded;
|
||||
if (!ValidatePackSize(funcName, width, height, bytesPerPixel, &rowStride,
|
||||
&bytesNeeded))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesNeeded > bytesAvailable) {
|
||||
ErrorInvalidOperation("readPixels: buffer too small");
|
||||
return;
|
||||
}
|
||||
|
||||
//////
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
const webgl::FormatUsageInfo* srcFormat;
|
||||
@ -1643,7 +1663,6 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
|
||||
|
||||
if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
|
||||
// Warning: Possibly shared memory. See bug 1225033.
|
||||
DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat,
|
||||
auxReadType);
|
||||
return;
|
||||
@ -1659,34 +1678,6 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and"
|
||||
" may be slow.");
|
||||
|
||||
// Currently, the spec dictates that we need to zero the out-of-bounds pixels.
|
||||
|
||||
// Ideally we could just ReadPixels into the buffer, then zero the undefined parts.
|
||||
// However, we can't do this for *shared* ArrayBuffers, as they can have racey
|
||||
// accesses from Workers.
|
||||
|
||||
// We can use a couple tricks to do this faster, but we shouldn't encourage this
|
||||
// anyway. Why not just do it the really safe, dead-simple way, even if it is
|
||||
// hilariously slow?
|
||||
|
||||
////////////////////////////////////
|
||||
// Clear the targetted pixels to zero.
|
||||
|
||||
if (mPixelStore_PackRowLength ||
|
||||
mPixelStore_PackSkipPixels ||
|
||||
mPixelStore_PackSkipRows)
|
||||
{
|
||||
// Targetted bytes might not be contiguous, so do it row-by-row.
|
||||
uint8_t* row = (uint8_t*)data + startOffset.value();
|
||||
const auto bytesPerRow = bytesPerPixel * width;
|
||||
for (uint32_t j = 0; j < uint32_t(height); j++) {
|
||||
std::memset(row, 0, bytesPerRow);
|
||||
row += rowStride.value();
|
||||
}
|
||||
} else {
|
||||
std::memset(data, 0, bytesNeeded.value());
|
||||
}
|
||||
|
||||
////////////////////////////////////
|
||||
// Read only the in-bounds pixels.
|
||||
|
||||
@ -1698,7 +1689,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
|
||||
if (IsWebGL2()) {
|
||||
if (!mPixelStore_PackRowLength) {
|
||||
gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, width);
|
||||
gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackSkipPixels + width);
|
||||
}
|
||||
gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX);
|
||||
gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY);
|
||||
@ -1712,12 +1703,12 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
|
||||
} else {
|
||||
// I *did* say "hilariously slow".
|
||||
|
||||
uint8_t* row = (uint8_t*)data + startOffset.value() + writeX * bytesPerPixel;
|
||||
row += writeY * rowStride.value();
|
||||
uint8_t* row = (uint8_t*)data + writeX * bytesPerPixel;
|
||||
row += writeY * rowStride;
|
||||
for (uint32_t j = 0; j < rwHeight; j++) {
|
||||
DoReadPixelsAndConvert(readX, readY+j, rwWidth, 1, format, type, row,
|
||||
auxReadFormat, auxReadType);
|
||||
row += rowStride.value();
|
||||
row += rowStride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,11 +43,7 @@ WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat()
|
||||
bool
|
||||
WebGLExtensionColorBufferHalfFloat::IsSupported(const WebGLContext* webgl)
|
||||
{
|
||||
gl::GLContext* gl = webgl->GL();
|
||||
|
||||
// ANGLE doesn't support ReadPixels from a RGBA16F with RGBA/FLOAT.
|
||||
return gl->IsSupported(gl::GLFeature::renderbuffer_color_half_float) ||
|
||||
gl->IsANGLE();
|
||||
return webgl->GL()->IsSupported(gl::GLFeature::renderbuffer_color_half_float);
|
||||
}
|
||||
|
||||
IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionColorBufferHalfFloat, EXT_color_buffer_half_float)
|
||||
|
@ -28,7 +28,6 @@ support-files =
|
||||
support-files = pointerevent_pointercancel_touch-manual.html
|
||||
[test_pointerevent_pointerdown-manual.html]
|
||||
support-files = pointerevent_pointerdown-manual.html
|
||||
disabled = should be investigated
|
||||
[test_pointerevent_pointerenter_does_not_bubble-manual.html]
|
||||
support-files = pointerevent_pointerenter_does_not_bubble-manual.html
|
||||
[test_pointerevent_pointerenter_nohover-manual.html]
|
||||
@ -41,7 +40,6 @@ support-files =
|
||||
support-files = pointerevent_pointerleave_after_pointerup_nohover-manual.html
|
||||
[test_pointerevent_pointerleave_descendant_over-manual.html]
|
||||
support-files = pointerevent_pointerleave_descendant_over-manual.html
|
||||
skip-if = (os == 'linux') # Bug 1180188 - Issue on Linux
|
||||
[test_pointerevent_pointerleave_descendants-manual.html]
|
||||
support-files = pointerevent_pointerleave_descendants-manual.html
|
||||
[test_pointerevent_pointerleave_does_not_bubble-manual.html]
|
||||
@ -53,7 +51,6 @@ support-files =
|
||||
disabled = should be investigated
|
||||
[test_pointerevent_pointerleave_touch-manual.html]
|
||||
support-files = pointerevent_pointerleave_touch-manual.html
|
||||
skip-if = (os == 'linux') # Bug 1180188 - Issue on Linux
|
||||
[test_pointerevent_pointermove-manual.html]
|
||||
support-files = pointerevent_pointermove-manual.html
|
||||
[test_pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html]
|
||||
@ -97,7 +94,6 @@ support-files =
|
||||
support-files = pointerevent_setpointercapture_disconnected-manual.html
|
||||
[test_pointerevent_setpointercapture_inactive_button_mouse-manual.html]
|
||||
support-files = pointerevent_setpointercapture_inactive_button_mouse-manual.html
|
||||
skip-if = (os == 'linux') && e10s # Bug 1180188 - Issue on Linux
|
||||
[test_pointerevent_setpointercapture_invalid_pointerid-manual.html]
|
||||
support-files = pointerevent_setpointercapture_invalid_pointerid-manual.html
|
||||
[test_pointerevent_setpointercapture_relatedtarget-manual.html]
|
||||
@ -110,7 +106,6 @@ support-files =
|
||||
disabled = disabled
|
||||
[test_pointerevent_touch-action-illegal.html]
|
||||
support-files = pointerevent_touch-action-illegal.html
|
||||
disabled = disabled
|
||||
[test_pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html]
|
||||
support-files = pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
|
||||
disabled = disabled
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsISettingsService.h"
|
||||
|
||||
#include "nsGeolocation.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
@ -32,7 +31,6 @@
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
||||
#include "mozilla/dom/WakeLock.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
@ -62,9 +60,6 @@ class nsIPrincipal;
|
||||
// that a window can make.
|
||||
#define MAX_GEO_REQUESTS_PER_WINDOW 1500
|
||||
|
||||
// The settings key.
|
||||
#define GEO_SETTINGS_ENABLED "geolocation.enabled"
|
||||
|
||||
using mozilla::Unused; // <snicker>
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
@ -150,53 +145,6 @@ CreatePositionOptionsCopy(const PositionOptions& aOptions)
|
||||
return geoOptions.forget();
|
||||
}
|
||||
|
||||
class GeolocationSettingsCallback : public nsISettingsServiceCallback
|
||||
{
|
||||
virtual ~GeolocationSettingsCallback() {
|
||||
MOZ_COUNT_DTOR(GeolocationSettingsCallback);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
GeolocationSettingsCallback() {
|
||||
MOZ_COUNT_CTOR(GeolocationSettingsCallback);
|
||||
}
|
||||
|
||||
NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// The geolocation is enabled by default:
|
||||
bool value = true;
|
||||
if (aResult.isBoolean()) {
|
||||
value = aResult.toBoolean();
|
||||
}
|
||||
|
||||
MozSettingValue(value);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleError(const nsAString& aName) override
|
||||
{
|
||||
NS_WARNING("Unable to get value for '" GEO_SETTINGS_ENABLED "'");
|
||||
|
||||
// Default it's enabled:
|
||||
MozSettingValue(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void MozSettingValue(const bool aValue)
|
||||
{
|
||||
RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
|
||||
if (gs) {
|
||||
gs->HandleMozsettingValue(aValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback)
|
||||
|
||||
class RequestPromptEvent : public Runnable
|
||||
{
|
||||
public:
|
||||
@ -382,12 +330,14 @@ NS_INTERFACE_MAP_END
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
|
||||
NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
|
||||
|
||||
void
|
||||
nsGeolocationRequest::Notify()
|
||||
{
|
||||
SetTimeoutTimer();
|
||||
NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
|
||||
}
|
||||
|
||||
void
|
||||
nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
|
||||
{
|
||||
@ -559,6 +509,7 @@ nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
|
||||
|
||||
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
|
||||
requester.forget(aRequester);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -646,6 +597,7 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
|
||||
MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
|
||||
"non-shutdown getCurrentPosition request after callback!");
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
nsGeolocationRequest::GetPrincipal()
|
||||
{
|
||||
@ -662,6 +614,7 @@ nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
|
||||
NS_DispatchToMainThread(ev);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
|
||||
{
|
||||
@ -703,6 +656,7 @@ nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
|
||||
RefPtr<nsGeolocationRequest> request(mRequest);
|
||||
request->Notify();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -721,7 +675,6 @@ NS_IMPL_RELEASE(nsGeolocationService)
|
||||
|
||||
|
||||
static bool sGeoEnabled = true;
|
||||
static bool sGeoInitPending = true;
|
||||
static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
|
||||
|
||||
nsresult nsGeolocationService::Init()
|
||||
@ -734,28 +687,9 @@ nsresult nsGeolocationService::Init()
|
||||
}
|
||||
|
||||
if (XRE_IsContentProcess()) {
|
||||
sGeoInitPending = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// check if the geolocation service is enable from settings
|
||||
nsCOMPtr<nsISettingsService> settings =
|
||||
do_GetService("@mozilla.org/settingsService;1");
|
||||
|
||||
if (settings) {
|
||||
nsCOMPtr<nsISettingsServiceLock> settingsLock;
|
||||
nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<GeolocationSettingsCallback> callback = new GeolocationSettingsCallback();
|
||||
rv = settingsLock->Get(GEO_SETTINGS_ENABLED, callback);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// If we cannot obtain the settings service, we continue
|
||||
// assuming that the geolocation is enabled:
|
||||
sGeoInitPending = false;
|
||||
}
|
||||
|
||||
// geolocation service can be enabled -> now register observer
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!obs) {
|
||||
@ -763,7 +697,6 @@ nsresult nsGeolocationService::Init()
|
||||
}
|
||||
|
||||
obs->AddObserver(this, "xpcom-shutdown", false);
|
||||
obs->AddObserver(this, "mozsettings-changed", false);
|
||||
|
||||
#ifdef MOZ_ENABLE_QT5GEOPOSITION
|
||||
mProvider = new QTMLocationProvider();
|
||||
@ -820,47 +753,6 @@ nsGeolocationService::~nsGeolocationService()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
nsGeolocationService::HandleMozsettingChanged(nsISupports* aSubject)
|
||||
{
|
||||
// The string that we're interested in will be a JSON string that looks like:
|
||||
// {"key":"gelocation.enabled","value":true}
|
||||
|
||||
RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
|
||||
if (!WrappedJSToDictionary(aSubject, setting)) {
|
||||
return;
|
||||
}
|
||||
if (!setting.mKey.EqualsASCII(GEO_SETTINGS_ENABLED)) {
|
||||
return;
|
||||
}
|
||||
if (!setting.mValue.isBoolean()) {
|
||||
return;
|
||||
}
|
||||
|
||||
HandleMozsettingValue(setting.mValue.toBoolean());
|
||||
}
|
||||
|
||||
void
|
||||
nsGeolocationService::HandleMozsettingValue(const bool aValue)
|
||||
{
|
||||
if (!aValue) {
|
||||
// turn things off
|
||||
StopDevice();
|
||||
Update(nullptr);
|
||||
mLastPosition.position = nullptr;
|
||||
sGeoEnabled = false;
|
||||
} else {
|
||||
sGeoEnabled = true;
|
||||
}
|
||||
|
||||
if (sGeoInitPending) {
|
||||
sGeoInitPending = false;
|
||||
for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) {
|
||||
mGeolocators[i]->ServiceReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
@ -870,7 +762,6 @@ nsGeolocationService::Observe(nsISupports* aSubject,
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, "xpcom-shutdown");
|
||||
obs->RemoveObserver(this, "mozsettings-changed");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
|
||||
@ -881,11 +772,6 @@ nsGeolocationService::Observe(nsISupports* aSubject,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp("mozsettings-changed", aTopic)) {
|
||||
HandleMozsettingChanged(aSubject);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp("timer-callback", aTopic)) {
|
||||
// decide if we can close down the service.
|
||||
for (uint32_t i = 0; i< mGeolocators.Length(); i++)
|
||||
@ -909,17 +795,21 @@ nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
|
||||
if (aSomewhere) {
|
||||
SetCachedPosition(aSomewhere);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
|
||||
mGeolocators[i]->Update(aSomewhere);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGeolocationService::NotifyError(uint16_t aErrorCode)
|
||||
{
|
||||
for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
|
||||
mGeolocators[i]->NotifyError(aErrorCode);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -939,7 +829,7 @@ nsGeolocationService::GetCachedPosition()
|
||||
nsresult
|
||||
nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
|
||||
{
|
||||
if (!sGeoEnabled || sGeoInitPending) {
|
||||
if (!sGeoEnabled) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
@ -990,7 +880,6 @@ nsGeolocationService::StopDisconnectTimer()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsGeolocationService::SetDisconnectTimer()
|
||||
{
|
||||
@ -1013,6 +902,7 @@ nsGeolocationService::HighAccuracyRequested()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1026,17 +916,11 @@ nsGeolocationService::UpdateAccuracy(bool aForceHigh)
|
||||
if (cpc->IsAlive()) {
|
||||
cpc->SendSetGeolocationHigherAccuracy(highRequired);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mHigherAccuracy && highRequired) {
|
||||
mProvider->SetHighAccuracy(true);
|
||||
}
|
||||
|
||||
if (mHigherAccuracy && !highRequired) {
|
||||
mProvider->SetHighAccuracy(false);
|
||||
}
|
||||
|
||||
mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
|
||||
mHigherAccuracy = highRequired;
|
||||
}
|
||||
|
||||
@ -1048,6 +932,7 @@ nsGeolocationService::StopDevice()
|
||||
if (XRE_IsContentProcess()) {
|
||||
ContentChild* cpc = ContentChild::GetSingleton();
|
||||
cpc->SendRemoveGeolocationListener();
|
||||
|
||||
return; // bail early
|
||||
}
|
||||
|
||||
@ -1083,8 +968,10 @@ nsGeolocationService::GetGeolocationService()
|
||||
if (NS_FAILED(result->Init())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClearOnShutdown(&nsGeolocationService::sService);
|
||||
nsGeolocationService::sService = result;
|
||||
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
@ -1187,21 +1074,21 @@ Geolocation::Init(nsPIDOMWindowInner* aContentDom)
|
||||
if (mService) {
|
||||
mService->AddLocator(this);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Geolocation::ContainsRequest(nsGeolocationRequest* aRequest)
|
||||
{
|
||||
if (aRequest->IsWatch()) {
|
||||
if (mWatchingCallbacks.Contains(aRequest)) {
|
||||
if (aRequest->IsWatch() && mWatchingCallbacks.Contains(aRequest)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (mPendingCallbacks.Contains(aRequest)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mPendingCallbacks.Contains(aRequest)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1225,23 +1112,27 @@ Geolocation::HandleEvent(nsIDOMEvent* aEvent)
|
||||
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
ContentChild* cpc = ContentChild::GetSingleton();
|
||||
|
||||
if (!info.lockingProcesses().Contains(cpc->GetID())) {
|
||||
cpc->SendRemoveGeolocationListener();
|
||||
mService->StopDisconnectTimer();
|
||||
}
|
||||
} else {
|
||||
mService->SetDisconnectTimer();
|
||||
|
||||
// We will unconditionally allow all the requests in the callbacks
|
||||
// because if a request is put into either of these two callbacks,
|
||||
// it means that it has been allowed before.
|
||||
// That's why when we resume them, we unconditionally allow them again.
|
||||
for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
|
||||
mWatchingCallbacks[i]->Allow(JS::UndefinedHandleValue);
|
||||
}
|
||||
for (uint32_t i = 0, length = mPendingCallbacks.Length(); i < length; ++i) {
|
||||
mPendingCallbacks[i]->Allow(JS::UndefinedHandleValue);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mService->SetDisconnectTimer();
|
||||
|
||||
// We will unconditionally allow all the requests in the callbacks
|
||||
// because if a request is put into either of these two callbacks,
|
||||
// it means that it has been allowed before.
|
||||
// That's why when we resume them, we unconditionally allow them again.
|
||||
for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
|
||||
mWatchingCallbacks[i]->Allow(JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0, length = mPendingCallbacks.Length(); i < length; ++i) {
|
||||
mPendingCallbacks[i]->Allow(JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1342,8 +1233,10 @@ Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
|
||||
for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
|
||||
mWatchingCallbacks[i]->Update(aSomewhere);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Geolocation::NotifyError(uint16_t aErrorCode)
|
||||
{
|
||||
@ -1351,6 +1244,7 @@ Geolocation::NotifyError(uint16_t aErrorCode)
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
|
||||
|
||||
for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
|
||||
@ -1374,6 +1268,7 @@ Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1385,6 +1280,7 @@ Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
|
||||
this->ClearWatch(aRequest->WatchId());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1447,11 +1343,6 @@ Geolocation::GetCurrentPosition(GeoPositionCallback& callback,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (sGeoInitPending) {
|
||||
mPendingRequests.AppendElement(request);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return GetCurrentPositionReady(request);
|
||||
}
|
||||
|
||||
@ -1541,11 +1432,6 @@ Geolocation::WatchPosition(GeoPositionCallback& aCallback,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (sGeoInitPending) {
|
||||
mPendingRequests.AppendElement(request);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return WatchPositionReady(request);
|
||||
}
|
||||
|
||||
|
@ -72,9 +72,6 @@ public:
|
||||
|
||||
nsresult Init();
|
||||
|
||||
void HandleMozsettingChanged(nsISupports* aSubject);
|
||||
void HandleMozsettingValue(const bool aValue);
|
||||
|
||||
// Management of the Geolocation objects
|
||||
void AddLocator(mozilla::dom::Geolocation* locator);
|
||||
void RemoveLocator(mozilla::dom::Geolocation* locator);
|
||||
|
65
dom/grid/Grid.cpp
Normal file
65
dom/grid/Grid.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Grid.h"
|
||||
|
||||
#include "GridDimension.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Grid, mParent, mRows, mCols)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Grid)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Grid)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Grid)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
Grid::Grid(nsISupports* aParent,
|
||||
nsGridContainerFrame* aFrame)
|
||||
: mParent(do_QueryInterface(aParent))
|
||||
, mRows(new GridDimension(this))
|
||||
, mCols(new GridDimension(this))
|
||||
{
|
||||
MOZ_ASSERT(aFrame,
|
||||
"Should never be instantiated with a null nsGridContainerFrame");
|
||||
|
||||
const ComputedGridTrackInfo* rowTrackInfo = aFrame->GetComputedTemplateRows();
|
||||
mRows->SetTrackInfo(rowTrackInfo);
|
||||
mRows->SetLineInfo(rowTrackInfo);
|
||||
|
||||
const ComputedGridTrackInfo* colTrackInfo = aFrame->GetComputedTemplateColumns();
|
||||
mCols->SetTrackInfo(colTrackInfo);
|
||||
mCols->SetLineInfo(colTrackInfo);
|
||||
}
|
||||
|
||||
Grid::~Grid()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Grid::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
GridDimension*
|
||||
Grid::Rows() const
|
||||
{
|
||||
return mRows;
|
||||
}
|
||||
|
||||
GridDimension*
|
||||
Grid::Cols() const
|
||||
{
|
||||
return mCols;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
51
dom/grid/Grid.h
Normal file
51
dom/grid/Grid.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_Grid_h
|
||||
#define mozilla_dom_Grid_h
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class GridDimension;
|
||||
|
||||
class Grid : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit Grid(nsISupports* aParent, nsGridContainerFrame* aFrame);
|
||||
|
||||
protected:
|
||||
virtual ~Grid();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Grid)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
Element* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
GridDimension* Rows() const;
|
||||
GridDimension* Cols() const;
|
||||
|
||||
protected:
|
||||
nsCOMPtr<Element> mParent;
|
||||
RefPtr<GridDimension> mRows;
|
||||
RefPtr<GridDimension> mCols;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_Grid_h */
|
69
dom/grid/GridDimension.cpp
Normal file
69
dom/grid/GridDimension.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GridDimension.h"
|
||||
|
||||
#include "Grid.h"
|
||||
#include "GridLines.h"
|
||||
#include "GridTracks.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridDimension, mParent, mLines, mTracks)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GridDimension)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GridDimension)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridDimension)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
GridDimension::GridDimension(Grid* aParent)
|
||||
: mParent(aParent)
|
||||
, mLines(new GridLines(this))
|
||||
, mTracks(new GridTracks(this))
|
||||
{
|
||||
MOZ_ASSERT(aParent, "Should never be instantiated with a null Grid");
|
||||
}
|
||||
|
||||
GridDimension::~GridDimension()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GridDimension::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridDimensionBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
GridLines*
|
||||
GridDimension::Lines() const
|
||||
{
|
||||
return mLines;
|
||||
}
|
||||
|
||||
GridTracks*
|
||||
GridDimension::Tracks() const
|
||||
{
|
||||
return mTracks;
|
||||
}
|
||||
|
||||
void
|
||||
GridDimension::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
|
||||
{
|
||||
mTracks->SetTrackInfo(aTrackInfo);
|
||||
}
|
||||
|
||||
void
|
||||
GridDimension::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo)
|
||||
{
|
||||
mLines->SetLineInfo(aTrackInfo);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
56
dom/grid/GridDimension.h
Normal file
56
dom/grid/GridDimension.h
Normal file
@ -0,0 +1,56 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_GridDimension_h
|
||||
#define mozilla_dom_GridDimension_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ComputedGridTrackInfo;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Grid;
|
||||
class GridLines;
|
||||
class GridTracks;
|
||||
|
||||
class GridDimension : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit GridDimension(Grid* aParent);
|
||||
|
||||
protected:
|
||||
virtual ~GridDimension();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridDimension)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
Grid* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
GridLines* Lines() const;
|
||||
GridTracks* Tracks() const;
|
||||
|
||||
void SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo);
|
||||
void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo);
|
||||
|
||||
protected:
|
||||
RefPtr<Grid> mParent;
|
||||
RefPtr<GridLines> mLines;
|
||||
RefPtr<GridTracks> mTracks;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_GridDimension_h */
|
79
dom/grid/GridLine.cpp
Normal file
79
dom/grid/GridLine.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GridLine.h"
|
||||
|
||||
#include "GridLines.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLine, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLine)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLine)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLine)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
GridLine::GridLine(GridLines *aParent)
|
||||
: mParent(aParent)
|
||||
, mStart(0.0)
|
||||
, mBreadth(0.0)
|
||||
, mNumber(0)
|
||||
{
|
||||
MOZ_ASSERT(aParent, "Should never be instantiated with a null GridLines");
|
||||
}
|
||||
|
||||
GridLine::~GridLine()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
GridLine::GetNames(nsTArray<nsString>& aNames) const
|
||||
{
|
||||
aNames = mNames;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GridLine::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridLineBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
double
|
||||
GridLine::Start() const
|
||||
{
|
||||
return mStart;
|
||||
}
|
||||
|
||||
double
|
||||
GridLine::Breadth() const
|
||||
{
|
||||
return mBreadth;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridLine::Number() const
|
||||
{
|
||||
return mNumber;
|
||||
}
|
||||
|
||||
void
|
||||
GridLine::SetLineValues(double aStart,
|
||||
double aBreadth,
|
||||
uint32_t aNumber,
|
||||
const nsTArray<nsString>& aNames)
|
||||
{
|
||||
mStart = aStart;
|
||||
mBreadth = aBreadth;
|
||||
mNumber = aNumber;
|
||||
mNames = aNames;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
60
dom/grid/GridLine.h
Normal file
60
dom/grid/GridLine.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_GridLine_h
|
||||
#define mozilla_dom_GridLine_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class GridLines;
|
||||
|
||||
class GridLine : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit GridLine(GridLines* aParent);
|
||||
|
||||
protected:
|
||||
virtual ~GridLine();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridLine)
|
||||
|
||||
void GetNames(nsTArray<nsString>& aNames) const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
GridLines* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
double Start() const;
|
||||
double Breadth() const;
|
||||
uint32_t Number() const;
|
||||
|
||||
void SetLineValues(double aStart,
|
||||
double aBreadth,
|
||||
uint32_t aNumber,
|
||||
const nsTArray<nsString>& aNames);
|
||||
|
||||
protected:
|
||||
RefPtr<GridLines> mParent;
|
||||
double mStart;
|
||||
double mBreadth;
|
||||
uint32_t mNumber;
|
||||
nsTArray<nsString> mNames;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_GridLine_h */
|
108
dom/grid/GridLines.cpp
Normal file
108
dom/grid/GridLines.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GridLines.h"
|
||||
|
||||
#include "GridDimension.h"
|
||||
#include "GridLine.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridLines, mParent, mLines)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GridLines)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GridLines)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridLines)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
GridLines::GridLines(GridDimension* aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(aParent,
|
||||
"Should never be instantiated with a null GridDimension");
|
||||
}
|
||||
|
||||
GridLines::~GridLines()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GridLines::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridLinesBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridLines::Length() const
|
||||
{
|
||||
return mLines.Length();
|
||||
}
|
||||
|
||||
GridLine*
|
||||
GridLines::Item(uint32_t aIndex)
|
||||
{
|
||||
return mLines.SafeElementAt(aIndex);
|
||||
}
|
||||
|
||||
GridLine*
|
||||
GridLines::IndexedGetter(uint32_t aIndex,
|
||||
bool& aFound)
|
||||
{
|
||||
aFound = aIndex < mLines.Length();
|
||||
if (!aFound) {
|
||||
return nullptr;
|
||||
}
|
||||
return mLines[aIndex];
|
||||
}
|
||||
|
||||
void
|
||||
GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo)
|
||||
{
|
||||
mLines.Clear();
|
||||
|
||||
if (!aTrackInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t trackCount = aTrackInfo->mEndFragmentTrack -
|
||||
aTrackInfo->mStartFragmentTrack;
|
||||
|
||||
// If there is at least one track, line count is one more
|
||||
// than the number of tracks.
|
||||
if (trackCount > 0) {
|
||||
double endOfLastTrack = 0.0;
|
||||
double startOfNextTrack;
|
||||
|
||||
for (uint32_t i = aTrackInfo->mStartFragmentTrack;
|
||||
i < aTrackInfo->mEndFragmentTrack + 1;
|
||||
i++) {
|
||||
startOfNextTrack = (i < aTrackInfo->mEndFragmentTrack) ?
|
||||
aTrackInfo->mPositions[i] :
|
||||
endOfLastTrack;
|
||||
|
||||
GridLine* line = new GridLine(this);
|
||||
mLines.AppendElement(line);
|
||||
line->SetLineValues(
|
||||
nsPresContext::AppUnitsToDoubleCSSPixels(endOfLastTrack),
|
||||
nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
|
||||
endOfLastTrack),
|
||||
i + 1,
|
||||
nsTArray<nsString>()
|
||||
);
|
||||
|
||||
if (i < aTrackInfo->mEndFragmentTrack) {
|
||||
endOfLastTrack = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
52
dom/grid/GridLines.h
Normal file
52
dom/grid/GridLines.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_GridLines_h
|
||||
#define mozilla_dom_GridLines_h
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class GridDimension;
|
||||
class GridLine;
|
||||
|
||||
class GridLines : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit GridLines(GridDimension* aParent);
|
||||
|
||||
protected:
|
||||
virtual ~GridLines();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridLines)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
GridDimension* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
uint32_t Length() const;
|
||||
GridLine* Item(uint32_t aIndex);
|
||||
GridLine* IndexedGetter(uint32_t aIndex, bool& aFound);
|
||||
|
||||
void SetLineInfo(const ComputedGridTrackInfo* aTrackInfo);
|
||||
|
||||
protected:
|
||||
RefPtr<GridDimension> mParent;
|
||||
nsTArray<RefPtr<GridLine>> mLines;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_GridLines_h */
|
80
dom/grid/GridTrack.cpp
Normal file
80
dom/grid/GridTrack.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GridTrack.h"
|
||||
|
||||
#include "GridTracks.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridTrack, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GridTrack)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GridTrack)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridTrack)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
GridTrack::GridTrack(GridTracks* aParent)
|
||||
: mParent(aParent)
|
||||
, mStart(0.0)
|
||||
, mBreadth(0.0)
|
||||
, mType(GridTrackType::Implicit)
|
||||
, mState(GridTrackState::Static)
|
||||
{
|
||||
MOZ_ASSERT(aParent, "Should never be instantiated with a null GridTracks");
|
||||
}
|
||||
|
||||
GridTrack::~GridTrack()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GridTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridTrackBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
double
|
||||
GridTrack::Start() const
|
||||
{
|
||||
return mStart;
|
||||
}
|
||||
|
||||
double
|
||||
GridTrack::Breadth() const
|
||||
{
|
||||
return mBreadth;
|
||||
}
|
||||
|
||||
GridTrackType
|
||||
GridTrack::Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
GridTrackState
|
||||
GridTrack::State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void
|
||||
GridTrack::SetTrackValues(double aStart,
|
||||
double aBreadth,
|
||||
GridTrackType aType,
|
||||
GridTrackState aState)
|
||||
{
|
||||
mStart = aStart;
|
||||
mBreadth = aBreadth;
|
||||
mType = aType;
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
55
dom/grid/GridTrack.h
Normal file
55
dom/grid/GridTrack.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_GridTrack_h
|
||||
#define mozilla_dom_GridTrack_h
|
||||
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class GridTracks;
|
||||
|
||||
class GridTrack : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit GridTrack(GridTracks *parent);
|
||||
|
||||
protected:
|
||||
virtual ~GridTrack();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridTrack)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
GridTracks* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
double Start() const;
|
||||
double Breadth() const;
|
||||
GridTrackType Type() const;
|
||||
GridTrackState State() const;
|
||||
|
||||
void SetTrackValues(double aStart, double aBreadth, GridTrackType aType, GridTrackState aState);
|
||||
|
||||
protected:
|
||||
RefPtr<GridTracks> mParent;
|
||||
double mStart;
|
||||
double mBreadth;
|
||||
GridTrackType mType;
|
||||
GridTrackState mState;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_GridTrack_h */
|
98
dom/grid/GridTracks.cpp
Normal file
98
dom/grid/GridTracks.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GridTracks.h"
|
||||
|
||||
#include "GridDimension.h"
|
||||
#include "GridTrack.h"
|
||||
#include "mozilla/dom/GridBinding.h"
|
||||
#include "nsGridContainerFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridTracks, mParent, mTracks)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(GridTracks)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(GridTracks)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridTracks)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
GridTracks::GridTracks(GridDimension *aParent)
|
||||
: mParent(aParent)
|
||||
{
|
||||
MOZ_ASSERT(aParent,
|
||||
"Should never be instantiated with a null GridDimension");
|
||||
}
|
||||
|
||||
GridTracks::~GridTracks()
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
GridTracks::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return GridTracksBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GridTracks::Length() const
|
||||
{
|
||||
return mTracks.Length();
|
||||
}
|
||||
|
||||
GridTrack*
|
||||
GridTracks::Item(uint32_t aIndex)
|
||||
{
|
||||
return mTracks.SafeElementAt(aIndex);
|
||||
}
|
||||
|
||||
GridTrack*
|
||||
GridTracks::IndexedGetter(uint32_t aIndex,
|
||||
bool& aFound)
|
||||
{
|
||||
aFound = aIndex < mTracks.Length();
|
||||
if (!aFound) {
|
||||
return nullptr;
|
||||
}
|
||||
return mTracks[aIndex];
|
||||
}
|
||||
|
||||
void
|
||||
GridTracks::SetTrackInfo(const ComputedGridTrackInfo* aTrackInfo)
|
||||
{
|
||||
// rebuild the tracks based on aTrackInfo
|
||||
mTracks.Clear();
|
||||
|
||||
if (!aTrackInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = aTrackInfo->mStartFragmentTrack;
|
||||
i < aTrackInfo->mEndFragmentTrack;
|
||||
i++) {
|
||||
GridTrack* track = new GridTrack(this);
|
||||
mTracks.AppendElement(track);
|
||||
track->SetTrackValues(
|
||||
nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mPositions[i]),
|
||||
nsPresContext::AppUnitsToDoubleCSSPixels(aTrackInfo->mSizes[i]),
|
||||
(
|
||||
// Implicit if index is before the first explicit track, or after
|
||||
// the last explicit track.
|
||||
(i < aTrackInfo->mNumLeadingImplicitTracks) ||
|
||||
(i >= aTrackInfo->mNumLeadingImplicitTracks +
|
||||
aTrackInfo->mNumExplicitTracks) ?
|
||||
GridTrackType::Implicit :
|
||||
GridTrackType::Explicit
|
||||
),
|
||||
GridTrackState(aTrackInfo->mStates[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
55
dom/grid/GridTracks.h
Normal file
55
dom/grid/GridTracks.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_GridTracks_h
|
||||
#define mozilla_dom_GridTracks_h
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ComputedGridTrackInfo;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class GridDimension;
|
||||
class GridTrack;
|
||||
|
||||
class GridTracks : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit GridTracks(GridDimension* aParent);
|
||||
|
||||
protected:
|
||||
virtual ~GridTracks();
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridTracks)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
GridDimension* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
uint32_t Length() const;
|
||||
GridTrack* Item(uint32_t aIndex);
|
||||
GridTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
|
||||
|
||||
void SetTrackInfo(const mozilla::ComputedGridTrackInfo* aTrackInfo);
|
||||
|
||||
protected:
|
||||
RefPtr<GridDimension> mParent;
|
||||
nsTArray<RefPtr<GridTrack>> mTracks;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_GridTracks_h */
|
31
dom/grid/moz.build
Normal file
31
dom/grid/moz.build
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- 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 += ['test/chrome.ini']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Grid.h',
|
||||
'GridDimension.h',
|
||||
'GridLine.h',
|
||||
'GridLines.h',
|
||||
'GridTrack.h',
|
||||
'GridTracks.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Grid.cpp',
|
||||
'GridDimension.cpp',
|
||||
'GridLine.cpp',
|
||||
'GridLines.cpp',
|
||||
'GridTrack.cpp',
|
||||
'GridTracks.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/layout/generic',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
3
dom/grid/test/chrome.ini
Normal file
3
dom/grid/test/chrome.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[chrome/test_grid_object.html]
|
||||
[chrome/test_grid_state.html]
|
||||
[chrome/test_grid_repeats.html]
|
92
dom/grid/test/chrome/test_grid_object.html
Normal file
92
dom/grid/test/chrome/test_grid_object.html
Normal file
@ -0,0 +1,92 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 40px;
|
||||
}
|
||||
.wrapper {
|
||||
display: grid;
|
||||
width: 400px;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: 100px 1fr 1fr 100px;
|
||||
background-color: #f00;
|
||||
}
|
||||
.box {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
var wrapper = document.getElementById("wrapper");
|
||||
var boxA = document.getElementById("boxA");
|
||||
var boxB = document.getElementById("boxB");
|
||||
var boxC = document.getElementById("boxC");
|
||||
|
||||
// test function existence
|
||||
is(typeof(wrapper.getGridFragments), "function",
|
||||
"getGridFragments function exists."
|
||||
);
|
||||
|
||||
// test that display:grid elements have grids and display:block elements don't
|
||||
is(boxA.getGridFragments().length, 0, "No grid on display:block styled objects.");
|
||||
is(wrapper.getGridFragments().length, 1,
|
||||
"One grid on an un-fragmented display:grid styled object."
|
||||
);
|
||||
|
||||
var grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test column and row track counts
|
||||
is(grid.cols.tracks.length, 4,
|
||||
"Grid.cols.tracks property has length that matches grid-template-columns."
|
||||
);
|
||||
is(grid.rows.tracks.length, 2,
|
||||
"Grid.rows.tracks property has length that matches content."
|
||||
);
|
||||
|
||||
// test column track position
|
||||
is(grid.cols.tracks[1].start, 110, "Grid column track 1 position is as expected.");
|
||||
|
||||
// test column track width
|
||||
is(grid.cols.tracks[0].breadth, boxA.offsetWidth,
|
||||
"Grid column track width (fixed size) matches item width."
|
||||
);
|
||||
is(grid.cols.tracks[1].breadth, boxB.offsetWidth,
|
||||
"Grid column track width (flexible size) matches item width."
|
||||
);
|
||||
is(grid.cols.tracks[1].breadth, grid.cols.tracks[2].breadth,
|
||||
"Grid column track widths with equal proportion flexible size actually are same size."
|
||||
);
|
||||
|
||||
// test explicit / implicit tracks
|
||||
is(grid.cols.tracks[0].type, "explicit", "Grid column track 0 is explicit.");
|
||||
is(grid.rows.tracks[0].type, "implicit", "Grid row track 0 is implicit.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="runTests();">
|
||||
|
||||
<div id="wrapper" class="wrapper">
|
||||
<div id="boxA" class="box a">A</div>
|
||||
<div id="boxB" class="box b">B</div>
|
||||
<div id="boxC" class="box c">C</div>
|
||||
<div class="box d">D</div>
|
||||
<div class="box e">E</div>
|
||||
<div class="box f">F</div>
|
||||
<div class="box g">G</div>
|
||||
<div class="box h">H</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
53
dom/grid/test/chrome/test_grid_repeats.html
Normal file
53
dom/grid/test/chrome/test_grid_repeats.html
Normal file
@ -0,0 +1,53 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 40px;
|
||||
}
|
||||
.wrapper {
|
||||
display: grid;
|
||||
width: 600px;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: repeat(2, 100px) repeat(auto-fill, 50px);
|
||||
background-color: #f00;
|
||||
}
|
||||
.box {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
var wrapper = document.getElementById("wrapper");
|
||||
var grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test state of tracks
|
||||
is(grid.cols.tracks[1].state, "static", "Grid column track 1 is marked as static.");
|
||||
is(grid.cols.tracks[2].state, "repeat", "Grid column track 2 is marked as repeat.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="runTests();">
|
||||
|
||||
<div id="wrapper" class="wrapper">
|
||||
<div id="boxA" class="box a">A</div>
|
||||
<div class="box b">B</div>
|
||||
<div class="box c">C</div>
|
||||
<div class="box d">D</div>
|
||||
<div class="box e">E</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
54
dom/grid/test/chrome/test_grid_state.html
Normal file
54
dom/grid/test/chrome/test_grid_state.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 40px;
|
||||
}
|
||||
.wrapper {
|
||||
display: grid;
|
||||
width: 600px;
|
||||
grid-gap: 0px;
|
||||
grid-template-columns: 50px repeat(auto-fit, 100px);
|
||||
background-color: #f00;
|
||||
}
|
||||
.box {
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
.a {
|
||||
grid-column: 3;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTests() {
|
||||
var wrapper = document.getElementById("wrapper");
|
||||
var grid = wrapper.getGridFragments()[0];
|
||||
|
||||
// test count after removal
|
||||
is(grid.cols.tracks.length, 2, "Grid column track array compensates for removed auto-fit columns.");
|
||||
|
||||
// test state of tracks
|
||||
is(grid.cols.tracks[0].state, "static", "Grid column track 0 is marked as static.");
|
||||
is(grid.cols.tracks[1].state, "repeat", "Grid column track 1 is marked as repeat.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onLoad="runTests();">
|
||||
|
||||
<div id="wrapper" class="wrapper">
|
||||
<div id="boxA" class="box a">A</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -2253,7 +2253,7 @@ HTMLInputElement::ConvertStringToNumber(nsAString& aValue,
|
||||
case NS_FORM_INPUT_DATE:
|
||||
{
|
||||
uint32_t year, month, day;
|
||||
if (!GetValueAsDate(aValue, &year, &month, &day)) {
|
||||
if (!ParseDate(aValue, &year, &month, &day)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2510,7 +2510,7 @@ HTMLInputElement::GetValueAsDate(ErrorResult& aRv)
|
||||
uint32_t year, month, day;
|
||||
nsAutoString value;
|
||||
GetValueInternal(value);
|
||||
if (!GetValueAsDate(value, &year, &month, &day)) {
|
||||
if (!ParseDate(value, &year, &month, &day)) {
|
||||
return Nullable<Date>();
|
||||
}
|
||||
|
||||
@ -5341,6 +5341,13 @@ HTMLInputElement::SanitizeValue(nsAString& aValue)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NS_FORM_INPUT_MONTH:
|
||||
{
|
||||
if (!aValue.IsEmpty() && !IsValidMonth(aValue)) {
|
||||
aValue.Truncate();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NS_FORM_INPUT_COLOR:
|
||||
{
|
||||
if (IsValidSimpleColor(aValue)) {
|
||||
@ -5371,19 +5378,57 @@ bool HTMLInputElement::IsValidSimpleColor(const nsAString& aValue) const
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLInputElement::IsValidDate(const nsAString& aValue) const
|
||||
HTMLInputElement::IsValidMonth(const nsAString& aValue) const
|
||||
{
|
||||
uint32_t year, month, day;
|
||||
return GetValueAsDate(aValue, &year, &month, &day);
|
||||
uint32_t year, month;
|
||||
return ParseMonth(aValue, &year, &month);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLInputElement::GetValueAsDate(const nsAString& aValue,
|
||||
HTMLInputElement::IsValidDate(const nsAString& aValue) const
|
||||
{
|
||||
uint32_t year, month, day;
|
||||
return ParseDate(aValue, &year, &month, &day);
|
||||
}
|
||||
|
||||
bool HTMLInputElement::ParseYear(const nsAString& aValue, uint32_t* aYear) const
|
||||
{
|
||||
if (aValue.Length() < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DigitSubStringToNumber(aValue, 0, aValue.Length(), aYear) &&
|
||||
*aYear > 0;
|
||||
}
|
||||
|
||||
bool HTMLInputElement::ParseMonth(const nsAString& aValue,
|
||||
uint32_t* aYear,
|
||||
uint32_t* aMonth) const
|
||||
{
|
||||
// Parse the year, month values out a string formatted as 'yyyy-mm'.
|
||||
if (aValue.Length() < 7) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t endOfYearOffset = aValue.Length() - 3;
|
||||
if (aValue[endOfYearOffset] != '-') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nsAString& yearStr = Substring(aValue, 0, endOfYearOffset);
|
||||
if (!ParseYear(yearStr, aYear)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) &&
|
||||
*aMonth > 0 && *aMonth <= 12;
|
||||
}
|
||||
|
||||
bool HTMLInputElement::ParseDate(const nsAString& aValue,
|
||||
uint32_t* aYear,
|
||||
uint32_t* aMonth,
|
||||
uint32_t* aDay) const
|
||||
{
|
||||
|
||||
/*
|
||||
* Parse the year, month, day values out a date string formatted as 'yyyy-mm-dd'.
|
||||
* -The year must be 4 or more digits long, and year > 0
|
||||
@ -5391,29 +5436,21 @@ HTMLInputElement::GetValueAsDate(const nsAString& aValue,
|
||||
* -The day must be exactly 2 digit long, and 01 <= day <= maxday
|
||||
* Where maxday is the number of days in the month 'month' and year 'year'
|
||||
*/
|
||||
|
||||
if (aValue.Length() < 10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t endOfYearOffset = aValue.Length() - 6;
|
||||
|
||||
if (aValue[endOfYearOffset] != '-' ||
|
||||
aValue[endOfYearOffset + 3] != '-') {
|
||||
uint32_t endOfMonthOffset = aValue.Length() - 3;
|
||||
if (aValue[endOfMonthOffset] != '-') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DigitSubStringToNumber(aValue, 0, endOfYearOffset, aYear) ||
|
||||
*aYear < 1) {
|
||||
const nsAString& yearMonthStr = Substring(aValue, 0, endOfMonthOffset);
|
||||
if (!ParseMonth(yearMonthStr, aYear, aMonth)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DigitSubStringToNumber(aValue, endOfYearOffset + 1, 2, aMonth) ||
|
||||
*aMonth < 1 || *aMonth > 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DigitSubStringToNumber(aValue, endOfYearOffset + 4, 2, aDay) &&
|
||||
return DigitSubStringToNumber(aValue, endOfMonthOffset + 1, 2, aDay) &&
|
||||
*aDay > 0 && *aDay <= NumberOfDaysInMonth(*aMonth, *aYear);
|
||||
}
|
||||
|
||||
|
@ -1151,6 +1151,14 @@ protected:
|
||||
*/
|
||||
bool IsValidSimpleColor(const nsAString& aValue) const;
|
||||
|
||||
/**
|
||||
* Parse a date string of the form yyyy-mm
|
||||
* @param the string to be parsed.
|
||||
* @return whether the string is a valid month.
|
||||
* Note : this function does not consider the empty string as valid.
|
||||
*/
|
||||
bool IsValidMonth(const nsAString& aValue) const;
|
||||
|
||||
/**
|
||||
* Parse a date string of the form yyyy-mm-dd
|
||||
* @param the string to be parsed.
|
||||
@ -1159,16 +1167,38 @@ protected:
|
||||
*/
|
||||
bool IsValidDate(const nsAString& aValue) const;
|
||||
|
||||
/**
|
||||
* Parse a year string of the form yyyy
|
||||
*
|
||||
* @param the string to be parsed.
|
||||
*
|
||||
* @return the year in aYear.
|
||||
* @return whether the parsing was successful.
|
||||
*/
|
||||
bool ParseYear(const nsAString& aValue, uint32_t* aYear) const;
|
||||
|
||||
/**
|
||||
* Parse a month string of the form yyyy-mm
|
||||
*
|
||||
* @param the string to be parsed.
|
||||
* @return the year and month in aYear and aMonth.
|
||||
* @return whether the parsing was successful.
|
||||
*/
|
||||
bool ParseMonth(const nsAString& aValue,
|
||||
uint32_t* aYear,
|
||||
uint32_t* aMonth) const;
|
||||
|
||||
/**
|
||||
* Parse a date string of the form yyyy-mm-dd
|
||||
*
|
||||
* @param the string to be parsed.
|
||||
* @return the date in aYear, aMonth, aDay.
|
||||
* @return whether the parsing was successful.
|
||||
*/
|
||||
bool GetValueAsDate(const nsAString& aValue,
|
||||
uint32_t* aYear,
|
||||
uint32_t* aMonth,
|
||||
uint32_t* aDay) const;
|
||||
bool ParseDate(const nsAString& aValue,
|
||||
uint32_t* aYear,
|
||||
uint32_t* aMonth,
|
||||
uint32_t* aDay) const;
|
||||
|
||||
/**
|
||||
* This methods returns the number of days in a given month, for a given year.
|
||||
|
@ -69,6 +69,7 @@
|
||||
#include "mozilla/dom/MediaSource.h"
|
||||
#include "MediaMetadataManager.h"
|
||||
#include "MediaSourceDecoder.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "DOMMediaStream.h"
|
||||
#include "AudioStreamTrack.h"
|
||||
#include "VideoStreamTrack.h"
|
||||
@ -273,6 +274,56 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This listener observes the first video frame to arrive with a non-empty size,
|
||||
* and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size.
|
||||
*/
|
||||
class HTMLMediaElement::StreamSizeListener : public DirectMediaStreamTrackListener {
|
||||
public:
|
||||
explicit StreamSizeListener(HTMLMediaElement* aElement) :
|
||||
mElement(aElement),
|
||||
mInitialSizeFound(false)
|
||||
{}
|
||||
void Forget() { mElement = nullptr; }
|
||||
|
||||
void ReceivedSize(gfx::IntSize aSize)
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
RefPtr<HTMLMediaElement> deathGrip = mElement;
|
||||
mElement->UpdateInitialMediaSize(aSize);
|
||||
}
|
||||
|
||||
void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
const MediaSegment& aMedia) override
|
||||
{
|
||||
if (mInitialSizeFound || aMedia.GetType() != MediaSegment::VIDEO) {
|
||||
return;
|
||||
}
|
||||
const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
|
||||
for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
|
||||
if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
|
||||
mInitialSizeFound = true;
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NewRunnableMethod<gfx::IntSize>(
|
||||
this, &StreamSizeListener::ReceivedSize,
|
||||
c->mFrame.GetIntrinsicSize());
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// These fields may only be accessed on the main thread
|
||||
HTMLMediaElement* mElement;
|
||||
|
||||
// These fields may only be accessed on the MSG thread
|
||||
bool mInitialSizeFound;
|
||||
};
|
||||
|
||||
/**
|
||||
* There is a reference cycle involving this class: MediaLoadListener
|
||||
* holds a reference to the HTMLMediaElement, which holds a reference
|
||||
@ -656,6 +707,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
|
||||
#ifdef MOZ_EME
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
|
||||
@ -682,6 +734,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
|
||||
#ifdef MOZ_EME
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
|
||||
@ -880,6 +933,14 @@ void HTMLMediaElement::AbortExistingLoads()
|
||||
|
||||
bool fireTimeUpdate = false;
|
||||
|
||||
// We need to remove StreamSizeListener before VideoTracks get emptied.
|
||||
if (mMediaStreamSizeListener) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
|
||||
// When aborting the existing loads, empty the objects in audio track list and
|
||||
// video track list, no events (in particular, no removetrack events) are
|
||||
// fired as part of this. Ending MediaStream sends track ended notifications,
|
||||
@ -3336,9 +3397,9 @@ public:
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
|
||||
}
|
||||
virtual void NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event) override
|
||||
MediaStreamGraphEvent event) override
|
||||
{
|
||||
if (event == EVENT_FINISHED) {
|
||||
if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
|
||||
@ -3375,59 +3436,6 @@ private:
|
||||
bool mPendingNotifyOutput;
|
||||
};
|
||||
|
||||
/**
|
||||
* This listener observes the first video frame to arrive with a non-empty size,
|
||||
* and calls HTMLMediaElement::ReceivedMediaStreamInitialSize() with that size.
|
||||
*/
|
||||
class HTMLMediaElement::StreamSizeListener : public MediaStreamListener {
|
||||
public:
|
||||
explicit StreamSizeListener(HTMLMediaElement* aElement) :
|
||||
mElement(aElement),
|
||||
mInitialSizeFound(false)
|
||||
{}
|
||||
void Forget() { mElement = nullptr; }
|
||||
|
||||
void ReceivedSize(gfx::IntSize aSize)
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
RefPtr<HTMLMediaElement> deathGrip = mElement;
|
||||
mElement->UpdateInitialMediaSize(aSize);
|
||||
}
|
||||
|
||||
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream,
|
||||
TrackID aInputTrackID) override
|
||||
{
|
||||
if (mInitialSizeFound || aQueuedMedia.GetType() != MediaSegment::VIDEO) {
|
||||
return;
|
||||
}
|
||||
const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
|
||||
for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
|
||||
if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0,0)) {
|
||||
mInitialSizeFound = true;
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NewRunnableMethod<gfx::IntSize>(
|
||||
this, &StreamSizeListener::ReceivedSize,
|
||||
c->mFrame.GetIntrinsicSize());
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// These fields may only be accessed on the main thread
|
||||
HTMLMediaElement* mElement;
|
||||
|
||||
// These fields may only be accessed on the MSG thread
|
||||
bool mInitialSizeFound;
|
||||
};
|
||||
|
||||
class HTMLMediaElement::MediaStreamTracksAvailableCallback:
|
||||
public OnTracksAvailableCallback
|
||||
{
|
||||
@ -3546,9 +3554,6 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
||||
RefPtr<MediaStream> stream = GetSrcMediaStream();
|
||||
if (stream) {
|
||||
stream->SetAudioChannelType(mAudioChannel);
|
||||
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
stream->AddListener(mMediaStreamSizeListener);
|
||||
}
|
||||
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
@ -3579,10 +3584,8 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
||||
UpdateSrcMediaStreamPlaying(REMOVING_SRC_STREAM);
|
||||
|
||||
if (mMediaStreamSizeListener) {
|
||||
RefPtr<MediaStream> stream = GetSrcMediaStream();
|
||||
if (stream) {
|
||||
stream->RemoveListener(mMediaStreamSizeListener);
|
||||
}
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
@ -3618,7 +3621,8 @@ CreateVideoTrack(VideoStreamTrack* aStreamTrack)
|
||||
aStreamTrack->GetLabel(label);
|
||||
|
||||
return MediaTrackList::CreateVideoTrack(id, NS_LITERAL_STRING("main"),
|
||||
label, EmptyString());
|
||||
label, EmptyString(),
|
||||
aStreamTrack);
|
||||
}
|
||||
|
||||
void HTMLMediaElement::ConstructMediaTracks()
|
||||
@ -3650,6 +3654,11 @@ void HTMLMediaElement::ConstructMediaTracks()
|
||||
// must be selected.
|
||||
int index = firstEnabledVideo >= 0 ? firstEnabledVideo : 0;
|
||||
(*VideoTracks())[index]->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
|
||||
VideoTrack* track = (*VideoTracks())[index];
|
||||
VideoStreamTrack* streamTrack = track->GetVideoStreamTrack();
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
streamTrack->AddDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = streamTrack;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3670,8 +3679,20 @@ HTMLMediaElement::NotifyMediaStreamTrackAdded(const RefPtr<MediaStreamTrack>& aT
|
||||
RefPtr<AudioTrack> audioTrack = CreateAudioTrack(t);
|
||||
AudioTracks()->AddTrack(audioTrack);
|
||||
} else if (VideoStreamTrack* t = aTrack->AsVideoStreamTrack()) {
|
||||
// TODO: Fix this per the spec on bug 1273443.
|
||||
int32_t selectedIndex = VideoTracks()->SelectedIndex();
|
||||
RefPtr<VideoTrack> videoTrack = CreateVideoTrack(t);
|
||||
VideoTracks()->AddTrack(videoTrack);
|
||||
// New MediaStreamTrack added, set the new added video track as selected
|
||||
// video track when there is no selected track.
|
||||
if (selectedIndex == -1) {
|
||||
MOZ_ASSERT(!mSelectedVideoStreamTrack);
|
||||
videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
t->AddDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = t;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -3690,6 +3711,48 @@ HTMLMediaElement::NotifyMediaStreamTrackRemoved(const RefPtr<MediaStreamTrack>&
|
||||
AudioTracks()->RemoveTrack(t);
|
||||
} else if (MediaTrack* t = VideoTracks()->GetTrackById(id)) {
|
||||
VideoTracks()->RemoveTrack(t);
|
||||
// TODO: Fix this per the spec on bug 1273443.
|
||||
// If the removed media stream track is selected video track and there are
|
||||
// still video tracks, change the selected video track to the first
|
||||
// remaining track.
|
||||
if (aTrack == mSelectedVideoStreamTrack) {
|
||||
// The mMediaStreamSizeListener might already reset to nullptr.
|
||||
if (mMediaStreamSizeListener) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
}
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
MOZ_ASSERT(mSrcStream);
|
||||
nsTArray<RefPtr<VideoStreamTrack>> tracks;
|
||||
mSrcStream->GetVideoTracks(tracks);
|
||||
|
||||
for (const RefPtr<VideoStreamTrack>& track : tracks) {
|
||||
if (track->Ended()) {
|
||||
continue;
|
||||
}
|
||||
if (!track->Enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString trackId;
|
||||
track->GetId(trackId);
|
||||
MediaTrack* videoTrack = VideoTracks()->GetTrackById(trackId);
|
||||
MOZ_ASSERT(videoTrack);
|
||||
|
||||
videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
|
||||
if (mMediaStreamSizeListener) {
|
||||
track->AddDirectListener(mMediaStreamSizeListener);
|
||||
}
|
||||
mSelectedVideoStreamTrack = track;
|
||||
return;
|
||||
}
|
||||
|
||||
// There is no enabled video track existing, clean the
|
||||
// mMediaStreamSizeListener.
|
||||
if (mMediaStreamSizeListener) {
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// XXX (bug 1208328) Uncomment this when DOMMediaStream doesn't call
|
||||
// NotifyTrackRemoved multiple times for the same track, i.e., when it
|
||||
@ -4608,10 +4671,7 @@ void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)
|
||||
if (!mMediaStreamSizeListener) {
|
||||
return;
|
||||
}
|
||||
RefPtr<MediaStream> stream = GetSrcMediaStream();
|
||||
if (stream) {
|
||||
stream->RemoveListener(mMediaStreamSizeListener);
|
||||
}
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
|
@ -1247,6 +1247,8 @@ protected:
|
||||
// Holds a reference to the size-getting MediaStreamListener attached to
|
||||
// mSrcStream.
|
||||
RefPtr<StreamSizeListener> mMediaStreamSizeListener;
|
||||
// The selected video stream track which contained mMediaStreamSizeListener.
|
||||
RefPtr<VideoStreamTrack> mSelectedVideoStreamTrack;
|
||||
|
||||
const RefPtr<ShutdownObserver> mShutdownObserver;
|
||||
|
||||
|
@ -74,7 +74,7 @@ var inputTypes =
|
||||
[
|
||||
"text", "password", "search", "tel", "hidden", "checkbox", "radio",
|
||||
"submit", "image", "reset", "button", "email", "url", "number", "date",
|
||||
"time", "range", "color"
|
||||
"time", "range", "color", "month"
|
||||
];
|
||||
|
||||
var todoTypes =
|
||||
@ -176,6 +176,20 @@ function sanitizeValue(aType, aValue)
|
||||
}
|
||||
return aValue;
|
||||
case "month":
|
||||
// https://html.spec.whatwg.org/multipage/infrastructure.html#valid-month-string
|
||||
var match = /^([0-9]{4,})-([0-9]{2})$/.exec(aValue);
|
||||
if (!match) {
|
||||
return "";
|
||||
}
|
||||
var year = Number(match[1]);
|
||||
if (year === 0) {
|
||||
return "";
|
||||
}
|
||||
var month = Number(match[2]);
|
||||
if (month > 12 || month < 1) {
|
||||
return "";
|
||||
}
|
||||
return aValue;
|
||||
case "week":
|
||||
case "datetime":
|
||||
case "datetime-local":
|
||||
@ -330,6 +344,27 @@ function checkSanitizing(element, inputTypeDescription)
|
||||
"FFAAZZ",
|
||||
"ABCDEF",
|
||||
"#7654321",
|
||||
// For month
|
||||
"1970-01",
|
||||
"1234-12",
|
||||
"123456789-01",
|
||||
"2013-13",
|
||||
"0000-00",
|
||||
"2015-00",
|
||||
"0001-01",
|
||||
"1-1",
|
||||
"888-05",
|
||||
"2013-3",
|
||||
"2013-may",
|
||||
"2000-1a",
|
||||
"2013-03-13",
|
||||
"december",
|
||||
"abcdef",
|
||||
"12",
|
||||
" 2013-03",
|
||||
"2013 - 03",
|
||||
"2013 03",
|
||||
"2013/03",
|
||||
];
|
||||
|
||||
for (value of testData) {
|
||||
|
@ -181,6 +181,23 @@ function runTest()
|
||||
'23:08:09.1012',
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'month',
|
||||
validData: [
|
||||
'0001-01',
|
||||
'2012-12',
|
||||
'100000-01',
|
||||
],
|
||||
invalidData: [
|
||||
'1-01',
|
||||
'-',
|
||||
'december',
|
||||
'2012-dec',
|
||||
'2012/12',
|
||||
'2012-99',
|
||||
'2012-1',
|
||||
]
|
||||
},
|
||||
{ type: 'week', todo: true },
|
||||
{ type: 'datetime', todo: true },
|
||||
{ type: 'datetime-local', todo: true },
|
||||
|
@ -47,7 +47,7 @@ var todoTypes = [
|
||||
"datetime", "week", "datetime-local"
|
||||
];
|
||||
|
||||
var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color' ];
|
||||
var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color', 'month' ];
|
||||
|
||||
var length = testData.length;
|
||||
for (var i=0; i<length; ++i) {
|
||||
@ -60,7 +60,8 @@ for (var i=0; i<length; ++i) {
|
||||
// range will sanitize its value to 50 (the default) if it isn't a valid
|
||||
// number. We need to handle that specially.
|
||||
if (testData[j][0] == 'range' || testData[i][0] == 'range') {
|
||||
if (testData[j][0] == 'date' || testData[j][0] == 'time') {
|
||||
if (testData[j][0] == 'date' || testData[j][0] == 'time' ||
|
||||
testData[j][0] == 'month') {
|
||||
expectedValue = '';
|
||||
} else if (testData[j][0] == 'color') {
|
||||
expectedValue = '#000000';
|
||||
@ -69,7 +70,7 @@ for (var i=0; i<length; ++i) {
|
||||
}
|
||||
} else if (testData[i][0] == 'color' || testData[j][0] == 'color') {
|
||||
if (testData[j][0] == 'number' || testData[j][0] == 'date' ||
|
||||
testData[j][0] == 'time') {
|
||||
testData[j][0] == 'time' || testData[j][0] == 'month') {
|
||||
expectedValue = ''
|
||||
} else {
|
||||
expectedValue = '#000000';
|
||||
|
@ -28,8 +28,6 @@ ContentBridgeChild::ContentBridgeChild(Transport* aTransport)
|
||||
|
||||
ContentBridgeChild::~ContentBridgeChild()
|
||||
{
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -27,8 +27,6 @@ ContentBridgeParent::ContentBridgeParent(Transport* aTransport)
|
||||
|
||||
ContentBridgeParent::~ContentBridgeParent()
|
||||
{
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2271,8 +2271,6 @@ ContentParent::ContentParent(mozIApplication* aApp,
|
||||
? base::PRIVILEGES_INHERIT
|
||||
: base::PRIVILEGES_DEFAULT;
|
||||
mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
|
||||
|
||||
IToplevelProtocol::SetTransport(mSubprocess->GetChannel());
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
@ -2477,6 +2475,14 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
|
||||
#endif
|
||||
MaybeFileDesc brokerFd = void_t();
|
||||
#ifdef XP_LINUX
|
||||
// XXX: Checking the pref here makes it possible to enable/disable sandboxing
|
||||
// during an active session. Currently the pref is only used for testing
|
||||
// purpose. If the decision is made to permanently rely on the pref, this
|
||||
// should be changed so that it is required to restart firefox for the change
|
||||
// of value to take effect.
|
||||
shouldSandbox = (Preferences::GetInt("security.sandbox.content.level") > 0) &&
|
||||
!PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX");
|
||||
|
||||
if (shouldSandbox) {
|
||||
MOZ_ASSERT(!mSandboxBroker);
|
||||
UniquePtr<SandboxBroker::Policy> policy =
|
||||
|
@ -162,20 +162,17 @@ public:
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
/** Sets the information associated with this hang: this includes the ID of
|
||||
* the plugin which caused the hang as well as the content PID.
|
||||
/**
|
||||
* Sets the information associated with this hang: this includes the ID of
|
||||
* the plugin which caused the hang as well as the content PID. The ID of
|
||||
* a minidump taken during the hang can also be provided.
|
||||
*
|
||||
* @param aHangData The hang information
|
||||
* @param aDumpId The ID of a minidump taken when the hang occurred
|
||||
*/
|
||||
void SetHangData(const HangData& aHangData) { mHangData = aHangData; }
|
||||
|
||||
/** Sets the ID of the crash dump associated with this hang. When the ID has
|
||||
* been set then the corresponding crash dump will be used for reporting
|
||||
* instead of generating a new one.
|
||||
*
|
||||
* @param aId The ID of the crash dump taken when the hang was detected. */
|
||||
void SetDumpId(nsString& aId) {
|
||||
mDumpId = aId;
|
||||
void SetHangData(const HangData& aHangData, const nsAString& aDumpId) {
|
||||
mHangData = aHangData;
|
||||
mDumpId = aDumpId;
|
||||
}
|
||||
|
||||
void ClearHang() {
|
||||
@ -216,9 +213,17 @@ public:
|
||||
void EndStartingDebugger();
|
||||
void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
|
||||
|
||||
/**
|
||||
* Update the dump for the specified plugin. This method is thread-safe and
|
||||
* is used to replace a browser minidump with a full minidump. If aDumpId is
|
||||
* empty this is a no-op.
|
||||
*/
|
||||
void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
|
||||
|
||||
MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
|
||||
void ShutdownOnThread();
|
||||
|
||||
const RefPtr<ProcessHangMonitor> mHangMonitor;
|
||||
@ -258,11 +263,6 @@ HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
|
||||
|
||||
HangMonitorChild::~HangMonitorChild()
|
||||
{
|
||||
// For some reason IPDL doesn't automatically delete the channel for a
|
||||
// bridged protocol (bug 1090570). So we have to do it ourselves.
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(sInstance == this);
|
||||
sInstance = nullptr;
|
||||
@ -475,11 +475,6 @@ HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
|
||||
|
||||
HangMonitorParent::~HangMonitorParent()
|
||||
{
|
||||
// For some reason IPDL doesn't automatically delete the channel for a
|
||||
// bridged protocol (bug 1090570). So we have to do it ourselves.
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
MutexAutoLock lock(mBrowserCrashDumpHashLock);
|
||||
|
||||
@ -550,11 +545,15 @@ class HangObserverNotifier final : public Runnable
|
||||
{
|
||||
public:
|
||||
HangObserverNotifier(HangMonitoredProcess* aProcess,
|
||||
HangMonitorParent *aParent,
|
||||
const HangData& aHangData,
|
||||
const nsString& aBrowserDumpId)
|
||||
const nsString& aBrowserDumpId,
|
||||
bool aTakeMinidump)
|
||||
: mProcess(aProcess),
|
||||
mParent(aParent),
|
||||
mHangData(aHangData),
|
||||
mBrowserDumpId(aBrowserDumpId)
|
||||
mBrowserDumpId(aBrowserDumpId),
|
||||
mTakeMinidump(aTakeMinidump)
|
||||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -564,14 +563,19 @@ public:
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString dumpId;
|
||||
if (mHangData.type() == HangData::TPluginHangData) {
|
||||
if ((mHangData.type() == HangData::TPluginHangData) && mTakeMinidump) {
|
||||
// We've been handed a partial minidump; complete it with plugin and
|
||||
// content process dumps.
|
||||
const PluginHangData& phd = mHangData.get_PluginHangData();
|
||||
plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
|
||||
mBrowserDumpId, dumpId);
|
||||
mParent->UpdateMinidump(phd.pluginId(), dumpId);
|
||||
} else {
|
||||
// We already have a full minidump; go ahead and use it.
|
||||
dumpId = mBrowserDumpId;
|
||||
}
|
||||
|
||||
mProcess->SetHangData(mHangData);
|
||||
mProcess->SetDumpId(dumpId);
|
||||
mProcess->SetHangData(mHangData, dumpId);
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
@ -581,10 +585,40 @@ public:
|
||||
|
||||
private:
|
||||
RefPtr<HangMonitoredProcess> mProcess;
|
||||
HangMonitorParent* mParent;
|
||||
HangData mHangData;
|
||||
nsAutoString mBrowserDumpId;
|
||||
bool mTakeMinidump;
|
||||
};
|
||||
|
||||
// Take a minidump of the browser process if one wasn't already taken for the
|
||||
// plugin that caused the hang. Return false if a dump was already available or
|
||||
// true if new one has been taken.
|
||||
bool
|
||||
HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd,
|
||||
nsString& aCrashId)
|
||||
{
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
MutexAutoLock lock(mBrowserCrashDumpHashLock);
|
||||
if (!mBrowserCrashDumpIds.Get(aPhd.pluginId(), &aCrashId)) {
|
||||
nsCOMPtr<nsIFile> browserDump;
|
||||
if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) {
|
||||
if (!CrashReporter::GetIDFromMinidump(browserDump, aCrashId)
|
||||
|| aCrashId.IsEmpty()) {
|
||||
browserDump->Remove(false);
|
||||
NS_WARNING("Failed to generate timely browser stack, "
|
||||
"this is bad for plugin hang analysis!");
|
||||
} else {
|
||||
mBrowserCrashDumpIds.Put(aPhd.pluginId(), aCrashId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // MOZ_CRASHREPORTER
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
|
||||
{
|
||||
@ -606,30 +640,17 @@ HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
|
||||
// Before we wake up the browser main thread we want to take a
|
||||
// browser minidump.
|
||||
nsAutoString crashId;
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
bool takeMinidump = false;
|
||||
if (aHangData.type() == HangData::TPluginHangData) {
|
||||
MutexAutoLock lock(mBrowserCrashDumpHashLock);
|
||||
const PluginHangData& phd = aHangData.get_PluginHangData();
|
||||
if (!mBrowserCrashDumpIds.Get(phd.pluginId(), &crashId)) {
|
||||
nsCOMPtr<nsIFile> browserDump;
|
||||
if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) {
|
||||
if (!CrashReporter::GetIDFromMinidump(browserDump, crashId) || crashId.IsEmpty()) {
|
||||
browserDump->Remove(false);
|
||||
NS_WARNING("Failed to generate timely browser stack, this is bad for plugin hang analysis!");
|
||||
} else {
|
||||
mBrowserCrashDumpIds.Put(phd.pluginId(), crashId);
|
||||
}
|
||||
}
|
||||
}
|
||||
takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId);
|
||||
}
|
||||
#endif
|
||||
|
||||
mHangMonitor->InitiateCPOWTimeout();
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
nsCOMPtr<nsIRunnable> notifier =
|
||||
new HangObserverNotifier(mProcess, aHangData, crashId);
|
||||
new HangObserverNotifier(mProcess, this, aHangData, crashId, takeMinidump);
|
||||
NS_DispatchToMainThread(notifier);
|
||||
|
||||
return true;
|
||||
@ -726,6 +747,17 @@ HangMonitorParent::CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
HangMonitorParent::UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId)
|
||||
{
|
||||
if (aDumpId.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mBrowserCrashDumpHashLock);
|
||||
mBrowserCrashDumpIds.Put(aPluginId, aDumpId);
|
||||
}
|
||||
|
||||
/* HangMonitoredProcess implementation */
|
||||
|
||||
NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
|
||||
|
@ -4,6 +4,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MediaStreamGraphImpl.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
@ -78,7 +79,7 @@ AudioCaptureStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
|
||||
MediaStreamListener* l = mListeners[i];
|
||||
AudioSegment tmp;
|
||||
l->NotifyQueuedTrackChanges(
|
||||
Graph(), mTrackId, 0, MediaStreamListener::TRACK_EVENT_CREATED, tmp);
|
||||
Graph(), mTrackId, 0, TrackEventCommand::TRACK_EVENT_CREATED, tmp);
|
||||
l->NotifyFinishedTrackCreation(Graph());
|
||||
}
|
||||
mTrackCreated = true;
|
||||
|
@ -186,18 +186,18 @@ public:
|
||||
}
|
||||
|
||||
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset, uint32_t aTrackEvents,
|
||||
StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream,
|
||||
TrackID aInputTrackID) override
|
||||
{
|
||||
if (aTrackEvents & TRACK_EVENT_CREATED) {
|
||||
if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<TrackID, MediaSegment::Type, MediaStream*, TrackID>(
|
||||
this, &OwnedStreamListener::DoNotifyTrackCreated,
|
||||
aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID);
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
|
||||
} else if (aTrackEvents & TRACK_EVENT_ENDED) {
|
||||
} else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<MediaStream*, TrackID, TrackID>(
|
||||
this, &OwnedStreamListener::DoNotifyTrackEnded,
|
||||
@ -276,12 +276,12 @@ public:
|
||||
// The methods below are called on the MediaStreamGraph thread.
|
||||
|
||||
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset, uint32_t aTrackEvents,
|
||||
StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream,
|
||||
TrackID aInputTrackID) override
|
||||
{
|
||||
if (aTrackEvents & TRACK_EVENT_ENDED) {
|
||||
if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod<StorensRefPtrPassByPtr<MediaStream>, TrackID>(
|
||||
this, &PlaybackStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID);
|
||||
@ -757,7 +757,7 @@ DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const
|
||||
}
|
||||
|
||||
bool
|
||||
DOMMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
|
||||
DOMMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
|
||||
{
|
||||
if (GetInputStream() && GetInputStream()->AsSourceStream()) {
|
||||
GetInputStream()->AsSourceStream()->AddDirectListener(aListener);
|
||||
@ -767,7 +767,7 @@ DOMMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
|
||||
}
|
||||
|
||||
void
|
||||
DOMMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
|
||||
DOMMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
|
||||
{
|
||||
if (GetInputStream() && GetInputStream()->AsSourceStream()) {
|
||||
GetInputStream()->AsSourceStream()->RemoveDirectListener(aListener);
|
||||
|
@ -30,7 +30,7 @@ class DOMLocalMediaStream;
|
||||
class DOMMediaStream;
|
||||
class MediaStream;
|
||||
class MediaInputPort;
|
||||
class MediaStreamDirectListener;
|
||||
class DirectMediaStreamListener;
|
||||
class MediaStreamGraph;
|
||||
class ProcessedMediaStream;
|
||||
|
||||
@ -439,8 +439,8 @@ public:
|
||||
* Allows users to get access to media data without going through graph
|
||||
* queuing. Returns a bool to let us know if direct data will be delivered.
|
||||
*/
|
||||
bool AddDirectListener(MediaStreamDirectListener *aListener);
|
||||
void RemoveDirectListener(MediaStreamDirectListener *aListener);
|
||||
bool AddDirectListener(DirectMediaStreamListener *aListener);
|
||||
void RemoveDirectListener(DirectMediaStreamListener *aListener);
|
||||
|
||||
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
|
||||
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "mozilla/dom/MediaStreamTrack.h"
|
||||
#include "GetUserMediaRequest.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsHashPropertyBag.h"
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
@ -243,6 +244,253 @@ HostHasPermission(nsIURI &docURI)
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is an implementation of MediaStreamListener. This is used
|
||||
* to Start() and Stop() the underlying MediaEngineSource when MediaStreams
|
||||
* are assigned and deassigned in content.
|
||||
*/
|
||||
class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
|
||||
{
|
||||
public:
|
||||
// Create in an inactive state
|
||||
GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
|
||||
uint64_t aWindowID,
|
||||
const PrincipalHandle& aPrincipalHandle)
|
||||
: mMediaThread(aThread)
|
||||
, mMainThreadCheck(nullptr)
|
||||
, mWindowID(aWindowID)
|
||||
, mPrincipalHandle(aPrincipalHandle)
|
||||
, mStopped(false)
|
||||
, mFinished(false)
|
||||
, mRemoved(false)
|
||||
, mAudioStopped(false)
|
||||
, mVideoStopped(false) {}
|
||||
|
||||
~GetUserMediaCallbackMediaStreamListener()
|
||||
{
|
||||
Unused << mMediaThread;
|
||||
// It's OK to release mStream on any thread; they have thread-safe
|
||||
// refcounts.
|
||||
}
|
||||
|
||||
void Activate(already_AddRefed<SourceMediaStream> aStream,
|
||||
AudioDevice* aAudioDevice,
|
||||
VideoDevice* aVideoDevice)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mMainThreadCheck = PR_GetCurrentThread();
|
||||
mStream = aStream;
|
||||
mAudioDevice = aAudioDevice;
|
||||
mVideoDevice = aVideoDevice;
|
||||
|
||||
mStream->AddListener(this);
|
||||
}
|
||||
|
||||
MediaStream *Stream() // Can be used to test if Activate was called
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
SourceMediaStream *GetSourceStream()
|
||||
{
|
||||
NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
|
||||
if (!mStream) {
|
||||
return nullptr;
|
||||
}
|
||||
return mStream->AsSourceStream();
|
||||
}
|
||||
|
||||
void StopSharing();
|
||||
|
||||
void StopTrack(TrackID aID);
|
||||
|
||||
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
|
||||
|
||||
already_AddRefed<PledgeVoid>
|
||||
ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
|
||||
TrackID aID,
|
||||
const dom::MediaTrackConstraints& aConstraints);
|
||||
|
||||
// mVideo/AudioDevice are set by Activate(), so we assume they're capturing
|
||||
// if set and represent a real capture device.
|
||||
bool CapturingVideo()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
|
||||
(!mVideoDevice->GetSource()->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingAudio()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mAudioDevice && !mStopped &&
|
||||
!mAudioDevice->GetSource()->IsAvailable() &&
|
||||
(!mAudioDevice->GetSource()->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingScreen()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
|
||||
}
|
||||
bool CapturingWindow()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
|
||||
}
|
||||
bool CapturingApplication()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
|
||||
}
|
||||
bool CapturingBrowser()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
|
||||
}
|
||||
|
||||
// implement in .cpp to avoid circular dependency with MediaOperationTask
|
||||
// Can be invoked from EITHER MainThread or MSG thread
|
||||
void Stop();
|
||||
|
||||
void
|
||||
AudioConfig(bool aEchoOn, uint32_t aEcho,
|
||||
bool aAgcOn, uint32_t aAGC,
|
||||
bool aNoiseOn, uint32_t aNoise,
|
||||
int32_t aPlayoutDelay);
|
||||
|
||||
void
|
||||
Remove()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// allow calling even if inactive (!mStream) for easier cleanup
|
||||
// Caller holds strong reference to us, so no death grip required
|
||||
if (mStream && !mRemoved) {
|
||||
MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
|
||||
mRemoved = true; // RemoveListener is async, avoid races
|
||||
// If it's destroyed, don't call - listener will be removed and we'll be notified!
|
||||
if (!mStream->IsDestroyed()) {
|
||||
mStream->RemoveListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Proxy NotifyPull() to sources
|
||||
void
|
||||
NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
|
||||
{
|
||||
// Currently audio sources ignore NotifyPull, but they could
|
||||
// watch it especially for fake audio.
|
||||
if (mAudioDevice) {
|
||||
mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
|
||||
aDesiredTime, mPrincipalHandle);
|
||||
}
|
||||
if (mVideoDevice) {
|
||||
mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
|
||||
aDesiredTime, mPrincipalHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamGraphEvent aEvent) override
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
|
||||
switch (aEvent) {
|
||||
case MediaStreamGraphEvent::EVENT_FINISHED:
|
||||
rv = NS_GetMainThread(getter_AddRefs(thread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_ASSERTION(false, "Mainthread not available; running on current thread");
|
||||
// Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
|
||||
MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
|
||||
NotifyFinished();
|
||||
return;
|
||||
}
|
||||
thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
|
||||
NS_DISPATCH_NORMAL);
|
||||
break;
|
||||
case MediaStreamGraphEvent::EVENT_REMOVED:
|
||||
rv = NS_GetMainThread(getter_AddRefs(thread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_ASSERTION(false, "Mainthread not available; running on current thread");
|
||||
// Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
|
||||
MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
|
||||
NotifyRemoved();
|
||||
return;
|
||||
}
|
||||
thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
|
||||
NS_DISPATCH_NORMAL);
|
||||
break;
|
||||
case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS:
|
||||
NotifyDirectListeners(aGraph, true);
|
||||
break;
|
||||
case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS:
|
||||
NotifyDirectListeners(aGraph, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotifyFinished();
|
||||
|
||||
void
|
||||
NotifyRemoved();
|
||||
|
||||
void
|
||||
NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
|
||||
|
||||
PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
|
||||
|
||||
private:
|
||||
// Set at construction
|
||||
base::Thread* mMediaThread;
|
||||
// never ever indirect off this; just for assertions
|
||||
PRThread* mMainThreadCheck;
|
||||
|
||||
uint64_t mWindowID;
|
||||
const PrincipalHandle mPrincipalHandle;
|
||||
|
||||
// true after this listener has sent MEDIA_STOP. MainThread only.
|
||||
bool mStopped;
|
||||
|
||||
// true after the stream this listener is listening to has finished in the
|
||||
// MediaStreamGraph. MainThread only.
|
||||
bool mFinished;
|
||||
|
||||
// true after this listener has been removed from its MediaStream.
|
||||
// MainThread only.
|
||||
bool mRemoved;
|
||||
|
||||
// true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
|
||||
// MainThread only.
|
||||
bool mAudioStopped;
|
||||
|
||||
// true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
|
||||
// MainThread only.
|
||||
bool mVideoStopped;
|
||||
|
||||
// Set at Activate on MainThread
|
||||
|
||||
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
|
||||
// No locking needed as they're only addrefed except on the MediaManager thread
|
||||
RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
|
||||
RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
|
||||
RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
|
||||
};
|
||||
|
||||
// Generic class for running long media operations like Start off the main
|
||||
// thread, and then (because nsDOMMediaStreams aren't threadsafe),
|
||||
// ProxyReleases mStream since it's cycle collected.
|
||||
@ -3144,14 +3392,14 @@ GetUserMediaCallbackMediaStreamListener::StopSharing()
|
||||
|
||||
// ApplyConstraints for track
|
||||
|
||||
already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
|
||||
already_AddRefed<media::Pledge<bool, dom::MediaStreamError*>>
|
||||
GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
||||
nsPIDOMWindowInner* aWindow,
|
||||
TrackID aTrackID,
|
||||
const MediaTrackConstraints& aConstraints)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RefPtr<PledgeVoid> p = new PledgeVoid();
|
||||
RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = new media::Pledge<bool, dom::MediaStreamError*>();
|
||||
|
||||
// XXX to support multiple tracks of a type in a stream, this should key off
|
||||
// the TrackID and not just the type
|
||||
@ -3204,7 +3452,7 @@ GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
|
||||
if (!mgr) {
|
||||
return NS_OK;
|
||||
}
|
||||
RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
|
||||
RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = mgr->mOutstandingVoidPledges.Remove(id);
|
||||
if (p) {
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
p->Resolve(false);
|
||||
@ -3320,6 +3568,26 @@ GetUserMediaCallbackMediaStreamListener::NotifyRemoved()
|
||||
}
|
||||
}
|
||||
|
||||
GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
GetUserMediaStatus aStatus,
|
||||
bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
|
||||
: mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
|
||||
, mIsVideo(aIsVideo), mWindowID(aWindowID) {}
|
||||
|
||||
GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
|
||||
GetUserMediaStatus aStatus,
|
||||
already_AddRefed<DOMMediaStream> aStream,
|
||||
OnTracksAvailableCallback* aOnTracksAvailableCallback,
|
||||
bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
|
||||
: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
|
||||
mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
|
||||
mOnFailure(aError) {}
|
||||
GetUserMediaNotificationEvent::~GetUserMediaNotificationEvent()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GetUserMediaNotificationEvent::Run()
|
||||
{
|
||||
|
@ -53,6 +53,10 @@ struct MediaTrackConstraints;
|
||||
struct MediaTrackConstraintSet;
|
||||
} // namespace dom
|
||||
|
||||
class MediaManager;
|
||||
class GetUserMediaCallbackMediaStreamListener;
|
||||
class GetUserMediaTask;
|
||||
|
||||
extern LogModule* GetMediaManagerLog();
|
||||
#define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
|
||||
|
||||
@ -117,253 +121,6 @@ public:
|
||||
const MediaEnginePrefs &aPrefs);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is an implementation of MediaStreamListener. This is used
|
||||
* to Start() and Stop() the underlying MediaEngineSource when MediaStreams
|
||||
* are assigned and deassigned in content.
|
||||
*/
|
||||
class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
|
||||
{
|
||||
public:
|
||||
// Create in an inactive state
|
||||
GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
|
||||
uint64_t aWindowID,
|
||||
const PrincipalHandle& aPrincipalHandle)
|
||||
: mMediaThread(aThread)
|
||||
, mMainThreadCheck(nullptr)
|
||||
, mWindowID(aWindowID)
|
||||
, mPrincipalHandle(aPrincipalHandle)
|
||||
, mStopped(false)
|
||||
, mFinished(false)
|
||||
, mRemoved(false)
|
||||
, mAudioStopped(false)
|
||||
, mVideoStopped(false) {}
|
||||
|
||||
~GetUserMediaCallbackMediaStreamListener()
|
||||
{
|
||||
Unused << mMediaThread;
|
||||
// It's OK to release mStream on any thread; they have thread-safe
|
||||
// refcounts.
|
||||
}
|
||||
|
||||
void Activate(already_AddRefed<SourceMediaStream> aStream,
|
||||
AudioDevice* aAudioDevice,
|
||||
VideoDevice* aVideoDevice)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mMainThreadCheck = PR_GetCurrentThread();
|
||||
mStream = aStream;
|
||||
mAudioDevice = aAudioDevice;
|
||||
mVideoDevice = aVideoDevice;
|
||||
|
||||
mStream->AddListener(this);
|
||||
}
|
||||
|
||||
MediaStream *Stream() // Can be used to test if Activate was called
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
SourceMediaStream *GetSourceStream()
|
||||
{
|
||||
NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
|
||||
if (!mStream) {
|
||||
return nullptr;
|
||||
}
|
||||
return mStream->AsSourceStream();
|
||||
}
|
||||
|
||||
void StopSharing();
|
||||
|
||||
void StopTrack(TrackID aID);
|
||||
|
||||
typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
|
||||
|
||||
already_AddRefed<PledgeVoid>
|
||||
ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
|
||||
TrackID aID,
|
||||
const dom::MediaTrackConstraints& aConstraints);
|
||||
|
||||
// mVideo/AudioDevice are set by Activate(), so we assume they're capturing
|
||||
// if set and represent a real capture device.
|
||||
bool CapturingVideo()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
|
||||
(!mVideoDevice->GetSource()->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingAudio()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mAudioDevice && !mStopped &&
|
||||
!mAudioDevice->GetSource()->IsAvailable() &&
|
||||
(!mAudioDevice->GetSource()->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingScreen()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
|
||||
}
|
||||
bool CapturingWindow()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
|
||||
}
|
||||
bool CapturingApplication()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
!mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
|
||||
}
|
||||
bool CapturingBrowser()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mVideoDevice && !mStopped &&
|
||||
mVideoDevice->GetSource()->IsAvailable() &&
|
||||
mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
|
||||
}
|
||||
|
||||
// implement in .cpp to avoid circular dependency with MediaOperationTask
|
||||
// Can be invoked from EITHER MainThread or MSG thread
|
||||
void Stop();
|
||||
|
||||
void
|
||||
AudioConfig(bool aEchoOn, uint32_t aEcho,
|
||||
bool aAgcOn, uint32_t aAGC,
|
||||
bool aNoiseOn, uint32_t aNoise,
|
||||
int32_t aPlayoutDelay);
|
||||
|
||||
void
|
||||
Remove()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// allow calling even if inactive (!mStream) for easier cleanup
|
||||
// Caller holds strong reference to us, so no death grip required
|
||||
if (mStream && !mRemoved) {
|
||||
MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
|
||||
mRemoved = true; // RemoveListener is async, avoid races
|
||||
// If it's destroyed, don't call - listener will be removed and we'll be notified!
|
||||
if (!mStream->IsDestroyed()) {
|
||||
mStream->RemoveListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Proxy NotifyPull() to sources
|
||||
void
|
||||
NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
|
||||
{
|
||||
// Currently audio sources ignore NotifyPull, but they could
|
||||
// watch it especially for fake audio.
|
||||
if (mAudioDevice) {
|
||||
mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
|
||||
aDesiredTime, mPrincipalHandle);
|
||||
}
|
||||
if (mVideoDevice) {
|
||||
mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
|
||||
aDesiredTime, mPrincipalHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent aEvent) override
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
|
||||
switch (aEvent) {
|
||||
case EVENT_FINISHED:
|
||||
rv = NS_GetMainThread(getter_AddRefs(thread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_ASSERTION(false, "Mainthread not available; running on current thread");
|
||||
// Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
|
||||
MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
|
||||
NotifyFinished();
|
||||
return;
|
||||
}
|
||||
thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
|
||||
NS_DISPATCH_NORMAL);
|
||||
break;
|
||||
case EVENT_REMOVED:
|
||||
rv = NS_GetMainThread(getter_AddRefs(thread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
NS_ASSERTION(false, "Mainthread not available; running on current thread");
|
||||
// Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
|
||||
MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
|
||||
NotifyRemoved();
|
||||
return;
|
||||
}
|
||||
thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
|
||||
NS_DISPATCH_NORMAL);
|
||||
break;
|
||||
case EVENT_HAS_DIRECT_LISTENERS:
|
||||
NotifyDirectListeners(aGraph, true);
|
||||
break;
|
||||
case EVENT_HAS_NO_DIRECT_LISTENERS:
|
||||
NotifyDirectListeners(aGraph, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotifyFinished();
|
||||
|
||||
void
|
||||
NotifyRemoved();
|
||||
|
||||
void
|
||||
NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
|
||||
|
||||
PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
|
||||
|
||||
private:
|
||||
// Set at construction
|
||||
base::Thread* mMediaThread;
|
||||
// never ever indirect off this; just for assertions
|
||||
PRThread* mMainThreadCheck;
|
||||
|
||||
uint64_t mWindowID;
|
||||
const PrincipalHandle mPrincipalHandle;
|
||||
|
||||
// true after this listener has sent MEDIA_STOP. MainThread only.
|
||||
bool mStopped;
|
||||
|
||||
// true after the stream this listener is listening to has finished in the
|
||||
// MediaStreamGraph. MainThread only.
|
||||
bool mFinished;
|
||||
|
||||
// true after this listener has been removed from its MediaStream.
|
||||
// MainThread only.
|
||||
bool mRemoved;
|
||||
|
||||
// true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
|
||||
// MainThread only.
|
||||
bool mAudioStopped;
|
||||
|
||||
// true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
|
||||
// MainThread only.
|
||||
bool mVideoStopped;
|
||||
|
||||
// Set at Activate on MainThread
|
||||
|
||||
// Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
|
||||
// No locking needed as they're only addrefed except on the MediaManager thread
|
||||
RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
|
||||
RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
|
||||
RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
|
||||
};
|
||||
|
||||
class GetUserMediaNotificationEvent: public Runnable
|
||||
{
|
||||
public:
|
||||
@ -374,22 +131,14 @@ class GetUserMediaNotificationEvent: public Runnable
|
||||
};
|
||||
GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
GetUserMediaStatus aStatus,
|
||||
bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
|
||||
: mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
|
||||
, mIsVideo(aIsVideo), mWindowID(aWindowID) {}
|
||||
bool aIsAudio, bool aIsVideo, uint64_t aWindowID);
|
||||
|
||||
GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
|
||||
already_AddRefed<DOMMediaStream> aStream,
|
||||
OnTracksAvailableCallback* aOnTracksAvailableCallback,
|
||||
bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
|
||||
: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
|
||||
mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
|
||||
mOnFailure(aError) {}
|
||||
virtual ~GetUserMediaNotificationEvent()
|
||||
{
|
||||
|
||||
}
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError);
|
||||
virtual ~GetUserMediaNotificationEvent();
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
@ -411,9 +160,6 @@ typedef enum {
|
||||
MEDIA_DIRECT_LISTENERS,
|
||||
} MediaOperation;
|
||||
|
||||
class MediaManager;
|
||||
class GetUserMediaTask;
|
||||
|
||||
class ReleaseMediaOperationResource : public Runnable
|
||||
{
|
||||
public:
|
||||
@ -575,7 +321,7 @@ private:
|
||||
|
||||
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
|
||||
media::CoatCheck<PledgeChar> mOutstandingCharPledges;
|
||||
media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
|
||||
media::CoatCheck<media::Pledge<bool, dom::MediaStreamError*>> mOutstandingVoidPledges;
|
||||
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
|
||||
RefPtr<nsDOMCameraManager> mCameraManager;
|
||||
#endif
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "AudioChannelService.h"
|
||||
#include "AudioNodeStream.h"
|
||||
#include "AudioNodeExternalInputStream.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "mozilla/dom/AudioContextBinding.h"
|
||||
#include "mozilla/media/MediaUtils.h"
|
||||
#include <algorithm>
|
||||
@ -60,6 +61,12 @@ LazyLogModule gMediaStreamGraphLog("MediaStreamGraph");
|
||||
# define LIFECYCLE_LOG(...)
|
||||
#endif
|
||||
|
||||
enum SourceMediaStream::TrackCommands : uint32_t {
|
||||
TRACK_CREATE = TrackEventCommand::TRACK_EVENT_CREATED,
|
||||
TRACK_END = TrackEventCommand::TRACK_EVENT_ENDED,
|
||||
TRACK_UNUSED = TrackEventCommand::TRACK_EVENT_UNUSED,
|
||||
};
|
||||
|
||||
/**
|
||||
* A hash table containing the graph instances, one per AudioChannel.
|
||||
*/
|
||||
@ -201,7 +208,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
|
||||
offset, *(static_cast<AudioSegment*>(data->mData.get())));
|
||||
}
|
||||
l->NotifyQueuedTrackChanges(this, data->mID,
|
||||
offset, data->mCommands, *data->mData);
|
||||
offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
|
||||
if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
|
||||
l->NotifyQueuedAudioData(this, data->mID,
|
||||
offset, *(static_cast<AudioSegment*>(data->mData.get())));
|
||||
@ -221,7 +228,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
|
||||
MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
|
||||
for (MediaStreamListener* l : aStream->mListeners) {
|
||||
l->NotifyQueuedTrackChanges(this, data->mID,
|
||||
offset, data->mCommands, *data->mData);
|
||||
offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
|
||||
}
|
||||
} else {
|
||||
// Fixme: This part will be removed in the bug 1201363. It will be
|
||||
@ -231,7 +238,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
|
||||
// Dealing with video and not TRACK_CREATE and TRACK_END case.
|
||||
for (MediaStreamListener* l : aStream->mListeners) {
|
||||
l->NotifyQueuedTrackChanges(this, data->mID,
|
||||
offset, data->mCommands, *data->mData);
|
||||
offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -356,7 +363,7 @@ MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime)
|
||||
SetStreamOrderDirty();
|
||||
for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = stream->mListeners[j];
|
||||
l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
|
||||
l->NotifyEvent(this, MediaStreamGraphEvent::EVENT_FINISHED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2001,6 +2008,14 @@ MediaStream::MediaStream()
|
||||
MOZ_COUNT_CTOR(MediaStream);
|
||||
}
|
||||
|
||||
MediaStream::~MediaStream()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MediaStream);
|
||||
NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
|
||||
NS_ASSERTION(mMainThreadListeners.IsEmpty(),
|
||||
"All main thread listeners should have been removed");
|
||||
}
|
||||
|
||||
size_t
|
||||
MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
@ -2105,7 +2120,7 @@ MediaStream::EnsureTrack(TrackID aTrackId)
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyQueuedTrackChanges(Graph(), aTrackId, 0,
|
||||
MediaStreamListener::TRACK_EVENT_CREATED,
|
||||
TrackEventCommand::TRACK_EVENT_CREATED,
|
||||
*segment);
|
||||
// TODO If we ever need to ensure several tracks at once, we will have to
|
||||
// change this.
|
||||
@ -2121,7 +2136,7 @@ MediaStream::RemoveAllListenersImpl()
|
||||
{
|
||||
for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
|
||||
RefPtr<MediaStreamListener> listener = mListeners[i].forget();
|
||||
listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
|
||||
listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
|
||||
}
|
||||
mListeners.Clear();
|
||||
}
|
||||
@ -2381,17 +2396,17 @@ MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
|
||||
MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
|
||||
}
|
||||
|
||||
uint32_t flags = MediaStreamListener::TRACK_EVENT_CREATED;
|
||||
uint32_t flags = TrackEventCommand::TRACK_EVENT_CREATED;
|
||||
if (it->IsEnded()) {
|
||||
flags |= MediaStreamListener::TRACK_EVENT_ENDED;
|
||||
flags |= TrackEventCommand::TRACK_EVENT_ENDED;
|
||||
}
|
||||
nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
|
||||
listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
|
||||
flags, *segment,
|
||||
static_cast<TrackEventCommand>(flags), *segment,
|
||||
inputStream, inputTrackID);
|
||||
}
|
||||
if (mNotifiedFinished) {
|
||||
listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
|
||||
listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_FINISHED);
|
||||
}
|
||||
if (mNotifiedHasCurrentData) {
|
||||
listener->NotifyHasCurrentData(GraphImpl());
|
||||
@ -2420,7 +2435,7 @@ MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
|
||||
// wouldn't need this if we could do it in the opposite order
|
||||
RefPtr<MediaStreamListener> listener(aListener);
|
||||
mListeners.RemoveElement(aListener);
|
||||
listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
|
||||
listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2513,56 +2528,56 @@ MediaStream::RemoveTrackListener(MediaStreamTrackListener* aListener,
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
|
||||
MediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
// Base implementation, for streams that don't support direct track listeners.
|
||||
RefPtr<MediaStreamTrackDirectListener> listener = aListener;
|
||||
RefPtr<DirectMediaStreamTrackListener> listener = aListener;
|
||||
listener->NotifyDirectListenerInstalled(
|
||||
MediaStreamTrackDirectListener::InstallationResult::STREAM_NOT_SUPPORTED);
|
||||
DirectMediaStreamTrackListener::InstallationResult::STREAM_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::AddDirectTrackListener(MediaStreamTrackDirectListener* aListener,
|
||||
MediaStream::AddDirectTrackListener(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaStream* aStream, MediaStreamTrackDirectListener* aListener,
|
||||
Message(MediaStream* aStream, DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID) :
|
||||
ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
|
||||
virtual void Run()
|
||||
{
|
||||
mStream->AddDirectTrackListenerImpl(mListener.forget(), mTrackID);
|
||||
}
|
||||
RefPtr<MediaStreamTrackDirectListener> mListener;
|
||||
RefPtr<DirectMediaStreamTrackListener> mListener;
|
||||
TrackID mTrackID;
|
||||
};
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
|
||||
MediaStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
// Base implementation, the listener was never added so nothing to do.
|
||||
RefPtr<MediaStreamTrackDirectListener> listener = aListener;
|
||||
RefPtr<DirectMediaStreamTrackListener> listener = aListener;
|
||||
}
|
||||
|
||||
void
|
||||
MediaStream::RemoveDirectTrackListener(MediaStreamTrackDirectListener* aListener,
|
||||
MediaStream::RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(MediaStream* aStream, MediaStreamTrackDirectListener* aListener,
|
||||
Message(MediaStream* aStream, DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID) :
|
||||
ControlMessage(aStream), mListener(aListener), mTrackID(aTrackID) {}
|
||||
virtual void Run()
|
||||
{
|
||||
mStream->RemoveDirectTrackListenerImpl(mListener, mTrackID);
|
||||
}
|
||||
RefPtr<MediaStreamTrackDirectListener> mListener;
|
||||
RefPtr<DirectMediaStreamTrackListener> mListener;
|
||||
TrackID mTrackID;
|
||||
};
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener, aTrackID));
|
||||
@ -2686,6 +2701,16 @@ MediaStream::AddMainThreadListener(MainThreadMediaStreamListener* aListener)
|
||||
NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
|
||||
}
|
||||
|
||||
SourceMediaStream::SourceMediaStream() :
|
||||
MediaStream(),
|
||||
mMutex("mozilla::media::SourceMediaStream"),
|
||||
mUpdateKnownTracksTime(0),
|
||||
mPullEnabled(false),
|
||||
mUpdateFinished(false),
|
||||
mNeedsMixing(false)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
SourceMediaStream::OpenAudioInput(int aID,
|
||||
AudioDataListener *aListener)
|
||||
@ -2750,6 +2775,13 @@ SourceMediaStream::AddTrackInternal(TrackID aID, TrackRate aRate, StreamTime aSt
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
|
||||
AudioSegment* aSegment, uint32_t aFlags)
|
||||
{
|
||||
AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::FinishAddTracks()
|
||||
{
|
||||
@ -2830,13 +2862,13 @@ SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack,
|
||||
MOZ_ASSERT(aTrack);
|
||||
|
||||
for (uint32_t j = 0; j < mDirectListeners.Length(); ++j) {
|
||||
MediaStreamDirectListener* l = mDirectListeners[j];
|
||||
DirectMediaStreamListener* l = mDirectListeners[j];
|
||||
StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
|
||||
l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID,
|
||||
offset, aTrack->mCommands, *aSegment);
|
||||
}
|
||||
|
||||
for (const TrackBound<MediaStreamTrackDirectListener>& source
|
||||
for (const TrackBound<DirectMediaStreamTrackListener>& source
|
||||
: mDirectTrackListeners) {
|
||||
if (aTrack->mID != source.mTrackID) {
|
||||
continue;
|
||||
@ -2848,7 +2880,7 @@ SourceMediaStream::NotifyDirectConsumers(TrackData *aTrack,
|
||||
|
||||
// These handle notifying all the listeners of an event
|
||||
void
|
||||
SourceMediaStream::NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent)
|
||||
SourceMediaStream::NotifyListenersEventImpl(MediaStreamGraphEvent aEvent)
|
||||
{
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
@ -2857,23 +2889,23 @@ SourceMediaStream::NotifyListenersEventImpl(MediaStreamListener::MediaStreamGrap
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aNewEvent)
|
||||
SourceMediaStream::NotifyListenersEvent(MediaStreamGraphEvent aNewEvent)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
public:
|
||||
Message(SourceMediaStream* aStream, MediaStreamListener::MediaStreamGraphEvent aEvent) :
|
||||
Message(SourceMediaStream* aStream, MediaStreamGraphEvent aEvent) :
|
||||
ControlMessage(aStream), mEvent(aEvent) {}
|
||||
void Run() override
|
||||
{
|
||||
mStream->AsSourceStream()->NotifyListenersEventImpl(mEvent);
|
||||
}
|
||||
MediaStreamListener::MediaStreamGraphEvent mEvent;
|
||||
MediaStreamGraphEvent mEvent;
|
||||
};
|
||||
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNewEvent));
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
|
||||
SourceMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
|
||||
{
|
||||
bool wasEmpty;
|
||||
{
|
||||
@ -2884,12 +2916,12 @@ SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
|
||||
|
||||
if (wasEmpty) {
|
||||
// Async
|
||||
NotifyListenersEvent(MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
|
||||
NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
|
||||
SourceMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
|
||||
{
|
||||
bool isEmpty;
|
||||
{
|
||||
@ -2900,28 +2932,33 @@ SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
|
||||
|
||||
if (isEmpty) {
|
||||
// Async
|
||||
NotifyListenersEvent(MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
|
||||
NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
|
||||
SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
MOZ_ASSERT(IsTrackIDExplicit(aTrackID));
|
||||
TrackData* data;
|
||||
bool found;
|
||||
bool isAudio;
|
||||
RefPtr<MediaStreamTrackDirectListener> listener = aListener;
|
||||
bool isVideo;
|
||||
RefPtr<DirectMediaStreamTrackListener> listener = aListener;
|
||||
STREAM_LOG(LogLevel::Debug, ("Adding direct track listener %p bound to track %d to source stream %p",
|
||||
listener.get(), aTrackID, this));
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
data = FindDataForTrack(aTrackID);
|
||||
found = !!data;
|
||||
isAudio = found && data->mData->GetType() == MediaSegment::AUDIO;
|
||||
if (found && isAudio) {
|
||||
TrackBound<MediaStreamTrackDirectListener>* sourceListener =
|
||||
if (found) {
|
||||
isAudio = data->mData->GetType() == MediaSegment::AUDIO;
|
||||
isVideo = data->mData->GetType() == MediaSegment::VIDEO;
|
||||
}
|
||||
if (found && (isAudio || isVideo)) {
|
||||
TrackBound<DirectMediaStreamTrackListener>* sourceListener =
|
||||
mDirectTrackListeners.AppendElement();
|
||||
sourceListener->mListener = listener;
|
||||
sourceListener->mTrackID = aTrackID;
|
||||
@ -2931,28 +2968,28 @@ SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackD
|
||||
STREAM_LOG(LogLevel::Warning, ("Couldn't find source track for direct track listener %p",
|
||||
listener.get()));
|
||||
listener->NotifyDirectListenerInstalled(
|
||||
MediaStreamTrackDirectListener::InstallationResult::TRACK_NOT_FOUND_AT_SOURCE);
|
||||
DirectMediaStreamTrackListener::InstallationResult::TRACK_NOT_FOUND_AT_SOURCE);
|
||||
return;
|
||||
}
|
||||
if (!isAudio) {
|
||||
STREAM_LOG(LogLevel::Warning, ("Source track for direct track listener %p is not audio",
|
||||
if (!isAudio && !isVideo) {
|
||||
STREAM_LOG(LogLevel::Warning, ("Source track for direct track listener %p is unknown",
|
||||
listener.get()));
|
||||
listener->NotifyDirectListenerInstalled(
|
||||
MediaStreamTrackDirectListener::InstallationResult::TRACK_TYPE_NOT_SUPPORTED);
|
||||
// It is not a video or audio track.
|
||||
MOZ_ASSERT(true);
|
||||
return;
|
||||
}
|
||||
STREAM_LOG(LogLevel::Debug, ("Added direct track listener %p", listener.get()));
|
||||
listener->NotifyDirectListenerInstalled(
|
||||
MediaStreamTrackDirectListener::InstallationResult::SUCCESS);
|
||||
DirectMediaStreamTrackListener::InstallationResult::SUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
|
||||
SourceMediaStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (int32_t i = mDirectTrackListeners.Length() - 1; i >= 0; --i) {
|
||||
const TrackBound<MediaStreamTrackDirectListener>& source =
|
||||
const TrackBound<DirectMediaStreamTrackListener>& source =
|
||||
mDirectTrackListeners[i];
|
||||
if (source.mListener == aListener && source.mTrackID == aTrackID) {
|
||||
aListener->NotifyDirectListenerUninstalled();
|
||||
@ -2979,7 +3016,7 @@ SourceMediaStream::EndTrack(TrackID aID)
|
||||
MutexAutoLock lock(mMutex);
|
||||
TrackData *track = FindDataForTrack(aID);
|
||||
if (track) {
|
||||
track->mCommands |= TRACK_END;
|
||||
track->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
|
||||
}
|
||||
if (auto graph = GraphImpl()) {
|
||||
graph->EnsureNextIteration();
|
||||
@ -3012,7 +3049,7 @@ SourceMediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
|
||||
{
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (TrackBound<MediaStreamTrackDirectListener>& l: mDirectTrackListeners) {
|
||||
for (TrackBound<DirectMediaStreamTrackListener>& l: mDirectTrackListeners) {
|
||||
if (l.mTrackID == aTrackID) {
|
||||
bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID);
|
||||
if (!oldEnabled && aEnabled) {
|
||||
@ -3038,13 +3075,17 @@ SourceMediaStream::EndAllTrackAndFinish()
|
||||
MutexAutoLock lock(mMutex);
|
||||
for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
|
||||
SourceMediaStream::TrackData* data = &mUpdateTracks[i];
|
||||
data->mCommands |= TRACK_END;
|
||||
data->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
|
||||
}
|
||||
mPendingTracks.Clear();
|
||||
FinishWithLockHeld();
|
||||
// we will call NotifyEvent() to let GetUserMedia know
|
||||
}
|
||||
|
||||
SourceMediaStream::~SourceMediaStream()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::RegisterForAudioMixing()
|
||||
{
|
||||
|
@ -12,14 +12,13 @@
|
||||
|
||||
#include "mozilla/dom/AudioChannelBinding.h"
|
||||
|
||||
#include "AudioSegment.h"
|
||||
#include "AudioStream.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "StreamTracks.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
#include "VideoSegment.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "StreamTracks.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include <speex/speex_resampler.h>
|
||||
@ -89,125 +88,6 @@ class MediaStreamGraphImpl;
|
||||
class ProcessedMediaStream;
|
||||
class SourceMediaStream;
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener callbacks.
|
||||
* Override methods to be notified of audio or video data or changes in stream
|
||||
* state.
|
||||
*
|
||||
* This can be used by stream recorders or network connections that receive
|
||||
* stream input. It could also be used for debugging.
|
||||
*
|
||||
* All notification methods are called from the media graph thread. Overriders
|
||||
* of these methods are responsible for all synchronization. Beware!
|
||||
* These methods are called without the media graph monitor held, so
|
||||
* reentry into media graph methods is possible, although very much discouraged!
|
||||
* You should do something non-blocking and non-reentrant (e.g. dispatch an
|
||||
* event to some thread) and return.
|
||||
* The listener is not allowed to add/remove any listeners from the stream.
|
||||
*
|
||||
* When a listener is first attached, we guarantee to send a NotifyBlockingChanged
|
||||
* callback to notify of the initial blocking state. Also, if a listener is
|
||||
* attached to a stream that has already finished, we'll call NotifyFinished.
|
||||
*/
|
||||
class MediaStreamListener {
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
virtual ~MediaStreamListener() {}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
|
||||
|
||||
/**
|
||||
* When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
|
||||
* control loop is ready to pull, this gets called. A NotifyPull implementation
|
||||
* is allowed to call the SourceMediaStream methods that alter track
|
||||
* data. It is not allowed to make other MediaStream API calls, including
|
||||
* calls to add or remove MediaStreamListeners. It is not allowed to block
|
||||
* for any length of time.
|
||||
* aDesiredTime is the stream time we would like to get data up to. Data
|
||||
* beyond this point will not be played until NotifyPull runs again, so there's
|
||||
* not much point in providing it. Note that if the stream is blocked for
|
||||
* some reason, then data before aDesiredTime may not be played immediately.
|
||||
*/
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
|
||||
|
||||
enum Blocking {
|
||||
BLOCKED,
|
||||
UNBLOCKED
|
||||
};
|
||||
/**
|
||||
* Notify that the blocking status of the stream changed. The initial state
|
||||
* is assumed to be BLOCKED.
|
||||
*/
|
||||
virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
|
||||
|
||||
/**
|
||||
* Notify that the stream has data in each track
|
||||
* for the stream's current time. Once this state becomes true, it will
|
||||
* always be true since we block stream time from progressing to times where
|
||||
* there isn't data in each track.
|
||||
*/
|
||||
virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
|
||||
|
||||
/**
|
||||
* Notify that the stream output is advancing. aCurrentTime is the graph's
|
||||
* current time. MediaStream::GraphTimeToStreamTime can be used to get the
|
||||
* stream time.
|
||||
*/
|
||||
virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
|
||||
|
||||
enum MediaStreamGraphEvent {
|
||||
EVENT_FINISHED,
|
||||
EVENT_REMOVED,
|
||||
EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
|
||||
EVENT_HAS_NO_DIRECT_LISTENERS, // transition to no direct listeners
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify that an event has occurred on the Stream
|
||||
*/
|
||||
virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
|
||||
|
||||
// maskable flags, not a simple enumerated value
|
||||
enum {
|
||||
TRACK_EVENT_CREATED = 0x01,
|
||||
TRACK_EVENT_ENDED = 0x02,
|
||||
TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
|
||||
};
|
||||
/**
|
||||
* Notify that changes to one of the stream tracks have been queued.
|
||||
* aTrackEvents can be any combination of TRACK_EVENT_CREATED and
|
||||
* TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
|
||||
* at aTrackOffset (relative to the start of the stream).
|
||||
* aInputStream and aInputTrackID will be set if the changes originated
|
||||
* from an input stream's track. In practice they will only be used for
|
||||
* ProcessedMediaStreams.
|
||||
*/
|
||||
virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream = nullptr,
|
||||
TrackID aInputTrackID = TRACK_INVALID) {}
|
||||
|
||||
/**
|
||||
* Notify queued audio data. Only audio data need to be queued. The video data
|
||||
* will be notified by MediaStreamVideoSink::SetCurrentFrame.
|
||||
*/
|
||||
virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
const AudioSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream = nullptr,
|
||||
TrackID aInputTrackID = TRACK_INVALID) {}
|
||||
|
||||
/**
|
||||
* Notify that all new tracks this iteration have been created.
|
||||
* This is to ensure that tracks added atomically to MediaStreamGraph
|
||||
* are also notified of atomically to MediaStreamListeners.
|
||||
*/
|
||||
virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
|
||||
};
|
||||
|
||||
class AudioDataListenerInterface {
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
@ -246,177 +126,6 @@ public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDataListener)
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener callbacks locked to
|
||||
* specific tracks. Override methods to be notified of audio or video data or
|
||||
* changes in track state.
|
||||
*
|
||||
* All notification methods are called from the media graph thread. Overriders
|
||||
* of these methods are responsible for all synchronization. Beware!
|
||||
* These methods are called without the media graph monitor held, so
|
||||
* reentry into media graph methods is possible, although very much discouraged!
|
||||
* You should do something non-blocking and non-reentrant (e.g. dispatch an
|
||||
* event to some thread) and return.
|
||||
* The listener is not allowed to add/remove any listeners from the parent
|
||||
* stream.
|
||||
*
|
||||
* If a listener is attached to a track that has already ended, we guarantee
|
||||
* to call NotifyEnded.
|
||||
*/
|
||||
class MediaStreamTrackListener
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
|
||||
|
||||
public:
|
||||
virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
const MediaSegment& aQueuedMedia) {}
|
||||
|
||||
virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
|
||||
const PrincipalHandle& aNewPrincipalHandle) {}
|
||||
|
||||
virtual void NotifyEnded() {}
|
||||
|
||||
virtual void NotifyRemoved() {}
|
||||
|
||||
protected:
|
||||
virtual ~MediaStreamTrackListener() {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener direct callbacks
|
||||
* from within AppendToTrack(). Note that your regular listener will
|
||||
* still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
|
||||
* you must be careful to ignore them if AddDirectListener was successful.
|
||||
*/
|
||||
class MediaStreamDirectListener : public MediaStreamListener
|
||||
{
|
||||
public:
|
||||
virtual ~MediaStreamDirectListener() {}
|
||||
|
||||
/*
|
||||
* This will be called on any MediaStreamDirectListener added to a
|
||||
* a SourceMediaStream when AppendToTrack() is called. The MediaSegment
|
||||
* will be the RawSegment (unresampled) if available in AppendToTrack().
|
||||
* Note that NotifyQueuedTrackChanges() calls will also still occur.
|
||||
*/
|
||||
virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aMedia) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener direct callbacks from
|
||||
* within AppendToTrack(). It is bound to a certain track and can only be
|
||||
* installed on audio tracks. Once added to a track on any stream in the graph,
|
||||
* the graph will try to install it at that track's source of media data.
|
||||
*
|
||||
* This works for TrackUnionStreams, which will forward the listener to the
|
||||
* track's input track if it exists, or wait for it to be created before
|
||||
* forwarding if it doesn't.
|
||||
* Once it reaches a SourceMediaStream, it can be successfully installed.
|
||||
* Other types of streams will fail installation since they are not supported.
|
||||
*
|
||||
* Note that this listener and others for the same track will still get
|
||||
* NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
|
||||
* to ignore them if this listener was successfully installed.
|
||||
*/
|
||||
class MediaStreamTrackDirectListener : public MediaStreamTrackListener
|
||||
{
|
||||
friend class SourceMediaStream;
|
||||
friend class TrackUnionStream;
|
||||
|
||||
public:
|
||||
/*
|
||||
* This will be called on any MediaStreamTrackDirectListener added to a
|
||||
* SourceMediaStream when AppendToTrack() is called for the listener's bound
|
||||
* track, using the thread of the AppendToTrack() caller. The MediaSegment
|
||||
* will be the RawSegment (unresampled) if available in AppendToTrack().
|
||||
* If the track is enabled at the source but has been disabled in one of the
|
||||
* streams in between the source and where it was originally added, aMedia
|
||||
* will be a disabled version of the one passed to AppendToTrack() as well.
|
||||
* Note that NotifyQueuedTrackChanges() calls will also still occur.
|
||||
*/
|
||||
virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
const MediaSegment& aMedia) {}
|
||||
|
||||
/**
|
||||
* When a direct listener is processed for installation by the
|
||||
* MediaStreamGraph it will be notified with whether the installation was
|
||||
* successful or not. The results of this installation are the following:
|
||||
* TRACK_NOT_FOUND_AT_SOURCE
|
||||
* We found the source stream of media data for this track, but the track
|
||||
* didn't exist. This should only happen if you try to install the listener
|
||||
* directly to a SourceMediaStream that doesn't contain the given TrackID.
|
||||
* TRACK_TYPE_NOT_SUPPORTED
|
||||
* This is the failure when you install the listener to a non-audio track.
|
||||
* STREAM_NOT_SUPPORTED
|
||||
* While looking for the data source of this track, we found a MediaStream
|
||||
* that is not a SourceMediaStream or a TrackUnionStream.
|
||||
* SUCCESS
|
||||
* Installation was successful and this listener will start receiving
|
||||
* NotifyRealtimeData on the next AppendToTrack().
|
||||
*/
|
||||
enum class InstallationResult {
|
||||
TRACK_NOT_FOUND_AT_SOURCE,
|
||||
TRACK_TYPE_NOT_SUPPORTED,
|
||||
STREAM_NOT_SUPPORTED,
|
||||
SUCCESS
|
||||
};
|
||||
virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
|
||||
virtual void NotifyDirectListenerUninstalled() {}
|
||||
|
||||
protected:
|
||||
virtual ~MediaStreamTrackDirectListener() {}
|
||||
|
||||
void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo)
|
||||
{
|
||||
aTo.Clear();
|
||||
aTo.AppendNullData(aFrom.GetDuration());
|
||||
}
|
||||
|
||||
void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
MediaSegment& aMedia)
|
||||
{
|
||||
if (mDisabledCount == 0) {
|
||||
NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mMedia) {
|
||||
mMedia = aMedia.CreateEmptyClone();
|
||||
}
|
||||
if (aMedia.GetType() == MediaSegment::AUDIO) {
|
||||
MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
|
||||
static_cast<AudioSegment&>(*mMedia));
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported media type");
|
||||
}
|
||||
NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
|
||||
}
|
||||
|
||||
void IncreaseDisabled()
|
||||
{
|
||||
++mDisabledCount;
|
||||
}
|
||||
void DecreaseDisabled()
|
||||
{
|
||||
--mDisabledCount;
|
||||
MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
|
||||
}
|
||||
|
||||
// Matches the number of disabled streams to which this listener is attached.
|
||||
// The number of streams are those between the stream the listener was added
|
||||
// and the SourceMediaStream that is the input of the data.
|
||||
Atomic<int32_t> mDisabledCount;
|
||||
|
||||
nsAutoPtr<MediaSegment> mMedia;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a base class for main-thread listener callbacks.
|
||||
* This callback is invoked on the main thread when the main-thread-visible
|
||||
@ -447,6 +156,23 @@ struct AudioNodeSizes
|
||||
nsCString mNodeType;
|
||||
};
|
||||
|
||||
class AudioNodeEngine;
|
||||
class AudioNodeExternalInputStream;
|
||||
class AudioNodeStream;
|
||||
class AudioSegment;
|
||||
class CameraPreviewMediaStream;
|
||||
class DirectMediaStreamListener;
|
||||
class DirectMediaStreamTrackListener;
|
||||
class MediaInputPort;
|
||||
class MediaStreamGraphImpl;
|
||||
class MediaStreamListener;
|
||||
class MediaStreamTrackListener;
|
||||
class ProcessedMediaStream;
|
||||
class SourceMediaStream;
|
||||
|
||||
enum MediaStreamGraphEvent : uint32_t;
|
||||
enum TrackEventCommand : uint32_t;
|
||||
|
||||
/**
|
||||
* Helper struct for binding a track listener to a specific TrackID.
|
||||
*/
|
||||
@ -536,13 +262,7 @@ public:
|
||||
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
virtual ~MediaStream()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MediaStream);
|
||||
NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
|
||||
NS_ASSERTION(mMainThreadListeners.IsEmpty(),
|
||||
"All main thread listeners should have been removed");
|
||||
}
|
||||
virtual ~MediaStream();
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -599,7 +319,7 @@ public:
|
||||
* Note that the listener will be notified on the MediaStreamGraph thread
|
||||
* with whether the installation of it at the source was successful or not.
|
||||
*/
|
||||
virtual void AddDirectTrackListener(MediaStreamTrackDirectListener* aListener,
|
||||
virtual void AddDirectTrackListener(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID);
|
||||
|
||||
/**
|
||||
@ -610,7 +330,7 @@ public:
|
||||
* the source cannot be found, or when the listener had already been removed
|
||||
* does nothing.
|
||||
*/
|
||||
virtual void RemoveDirectTrackListener(MediaStreamTrackDirectListener* aListener,
|
||||
virtual void RemoveDirectTrackListener(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID);
|
||||
|
||||
// A disabled track has video replaced by black, and audio replaced by
|
||||
@ -713,9 +433,9 @@ public:
|
||||
TrackID aTrackID);
|
||||
virtual void RemoveTrackListenerImpl(MediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID);
|
||||
virtual void AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
|
||||
virtual void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
|
||||
TrackID aTrackID);
|
||||
virtual void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
|
||||
virtual void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID);
|
||||
virtual void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
|
||||
|
||||
@ -864,7 +584,7 @@ protected:
|
||||
float mVolume;
|
||||
};
|
||||
nsTArray<AudioOutput> mAudioOutputs;
|
||||
nsTArray<RefPtr<VideoFrameContainer> > mVideoOutputs;
|
||||
nsTArray<RefPtr<VideoFrameContainer>> mVideoOutputs;
|
||||
// We record the last played video frame to avoid playing the frame again
|
||||
// with a different frame id.
|
||||
VideoFrame mLastPlayedVideoFrame;
|
||||
@ -953,14 +673,7 @@ protected:
|
||||
class SourceMediaStream : public MediaStream
|
||||
{
|
||||
public:
|
||||
explicit SourceMediaStream() :
|
||||
MediaStream(),
|
||||
mMutex("mozilla::media::SourceMediaStream"),
|
||||
mUpdateKnownTracksTime(0),
|
||||
mPullEnabled(false),
|
||||
mUpdateFinished(false),
|
||||
mNeedsMixing(false)
|
||||
{}
|
||||
explicit SourceMediaStream();
|
||||
|
||||
SourceMediaStream* AsSourceStream() override { return this; }
|
||||
|
||||
@ -992,10 +705,10 @@ public:
|
||||
* synchronization delays for e.g. PeerConnection, which wants the data ASAP
|
||||
* and lets the far-end handle sync and playout timing.
|
||||
*/
|
||||
void NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent);
|
||||
void NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aEvent);
|
||||
void AddDirectListener(MediaStreamDirectListener* aListener);
|
||||
void RemoveDirectListener(MediaStreamDirectListener* aListener);
|
||||
void NotifyListenersEventImpl(MediaStreamGraphEvent aEvent);
|
||||
void NotifyListenersEvent(MediaStreamGraphEvent aEvent);
|
||||
void AddDirectListener(DirectMediaStreamListener* aListener);
|
||||
void RemoveDirectListener(DirectMediaStreamListener* aListener);
|
||||
|
||||
enum {
|
||||
ADDTRACK_QUEUED = 0x01 // Queue track add until FinishAddTracks()
|
||||
@ -1016,10 +729,7 @@ public:
|
||||
* Like AddTrack, but resamples audio from aRate to the graph rate.
|
||||
*/
|
||||
void AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
|
||||
AudioSegment* aSegment, uint32_t aFlags = 0)
|
||||
{
|
||||
AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
|
||||
}
|
||||
AudioSegment* aSegment, uint32_t aFlags = 0);
|
||||
|
||||
/**
|
||||
* Call after a series of AddTrack or AddAudioTrack calls to implement
|
||||
@ -1095,11 +805,10 @@ public:
|
||||
friend class MediaStreamGraphImpl;
|
||||
|
||||
protected:
|
||||
enum TrackCommands {
|
||||
TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
|
||||
TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED,
|
||||
TRACK_UNUSED = MediaStreamListener::TRACK_EVENT_UNUSED,
|
||||
};
|
||||
enum TrackCommands : uint32_t;
|
||||
|
||||
virtual ~SourceMediaStream();
|
||||
|
||||
/**
|
||||
* Data for each track that hasn't ended.
|
||||
*/
|
||||
@ -1126,9 +835,9 @@ protected:
|
||||
|
||||
void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
|
||||
|
||||
void AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
|
||||
void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
|
||||
TrackID aTrackID) override;
|
||||
void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
|
||||
void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID) override;
|
||||
|
||||
void AddTrackInternal(TrackID aID, TrackRate aRate,
|
||||
@ -1168,8 +877,8 @@ protected:
|
||||
StreamTime mUpdateKnownTracksTime;
|
||||
nsTArray<TrackData> mUpdateTracks;
|
||||
nsTArray<TrackData> mPendingTracks;
|
||||
nsTArray<RefPtr<MediaStreamDirectListener> > mDirectListeners;
|
||||
nsTArray<TrackBound<MediaStreamTrackDirectListener>> mDirectTrackListeners;
|
||||
nsTArray<RefPtr<DirectMediaStreamListener>> mDirectListeners;
|
||||
nsTArray<TrackBound<DirectMediaStreamTrackListener>> mDirectTrackListeners;
|
||||
bool mPullEnabled;
|
||||
bool mUpdateFinished;
|
||||
bool mNeedsMixing;
|
||||
|
58
dom/media/MediaStreamListener.cpp
Normal file
58
dom/media/MediaStreamListener.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/* -*- 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 "MediaStreamListener.h"
|
||||
|
||||
#include "AudioSegment.h"
|
||||
#include "VideoSegment.h"
|
||||
#include "StreamTracks.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
DirectMediaStreamTrackListener::MirrorAndDisableSegment(AudioSegment& aFrom,
|
||||
AudioSegment& aTo)
|
||||
{
|
||||
aTo.Clear();
|
||||
aTo.AppendNullData(aFrom.GetDuration());
|
||||
}
|
||||
|
||||
void
|
||||
DirectMediaStreamTrackListener::MirrorAndDisableSegment(VideoSegment& aFrom,
|
||||
VideoSegment& aTo)
|
||||
{
|
||||
aTo.Clear();
|
||||
for (VideoSegment::ChunkIterator it(aFrom); !it.IsEnded(); it.Next()) {
|
||||
aTo.AppendFrame(do_AddRef(it->mFrame.GetImage()), it->GetDuration(),
|
||||
it->mFrame.GetIntrinsicSize(), it->GetPrincipalHandle(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DirectMediaStreamTrackListener::NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
MediaSegment& aMedia)
|
||||
{
|
||||
if (mDisabledCount == 0) {
|
||||
NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mMedia) {
|
||||
mMedia = aMedia.CreateEmptyClone();
|
||||
}
|
||||
if (aMedia.GetType() == MediaSegment::AUDIO) {
|
||||
MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
|
||||
static_cast<AudioSegment&>(*mMedia));
|
||||
} else if (aMedia.GetType() == MediaSegment::VIDEO) {
|
||||
MirrorAndDisableSegment(static_cast<VideoSegment&>(aMedia),
|
||||
static_cast<VideoSegment&>(*mMedia));
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported media type");
|
||||
}
|
||||
NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
286
dom/media/MediaStreamListener.h
Normal file
286
dom/media/MediaStreamListener.h
Normal file
@ -0,0 +1,286 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_MEDIASTREAMLISTENER_h_
|
||||
#define MOZILLA_MEDIASTREAMLISTENER_h_
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaStream;
|
||||
class MediaStreamGraph;
|
||||
|
||||
enum MediaStreamGraphEvent : uint32_t {
|
||||
EVENT_FINISHED,
|
||||
EVENT_REMOVED,
|
||||
EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
|
||||
EVENT_HAS_NO_DIRECT_LISTENERS, // transition to no direct listeners
|
||||
};
|
||||
|
||||
// maskable flags, not a simple enumerated value
|
||||
enum TrackEventCommand : uint32_t {
|
||||
TRACK_EVENT_NONE = 0x00,
|
||||
TRACK_EVENT_CREATED = 0x01,
|
||||
TRACK_EVENT_ENDED = 0x02,
|
||||
TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener callbacks.
|
||||
* Override methods to be notified of audio or video data or changes in stream
|
||||
* state.
|
||||
*
|
||||
* This can be used by stream recorders or network connections that receive
|
||||
* stream input. It could also be used for debugging.
|
||||
*
|
||||
* All notification methods are called from the media graph thread. Overriders
|
||||
* of these methods are responsible for all synchronization. Beware!
|
||||
* These methods are called without the media graph monitor held, so
|
||||
* reentry into media graph methods is possible, although very much discouraged!
|
||||
* You should do something non-blocking and non-reentrant (e.g. dispatch an
|
||||
* event to some thread) and return.
|
||||
* The listener is not allowed to add/remove any listeners from the stream.
|
||||
*
|
||||
* When a listener is first attached, we guarantee to send a NotifyBlockingChanged
|
||||
* callback to notify of the initial blocking state. Also, if a listener is
|
||||
* attached to a stream that has already finished, we'll call NotifyFinished.
|
||||
*/
|
||||
class MediaStreamListener {
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
virtual ~MediaStreamListener() {}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
|
||||
|
||||
/**
|
||||
* When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
|
||||
* control loop is ready to pull, this gets called. A NotifyPull implementation
|
||||
* is allowed to call the SourceMediaStream methods that alter track
|
||||
* data. It is not allowed to make other MediaStream API calls, including
|
||||
* calls to add or remove MediaStreamListeners. It is not allowed to block
|
||||
* for any length of time.
|
||||
* aDesiredTime is the stream time we would like to get data up to. Data
|
||||
* beyond this point will not be played until NotifyPull runs again, so there's
|
||||
* not much point in providing it. Note that if the stream is blocked for
|
||||
* some reason, then data before aDesiredTime may not be played immediately.
|
||||
*/
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
|
||||
|
||||
enum Blocking {
|
||||
BLOCKED,
|
||||
UNBLOCKED
|
||||
};
|
||||
/**
|
||||
* Notify that the blocking status of the stream changed. The initial state
|
||||
* is assumed to be BLOCKED.
|
||||
*/
|
||||
virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
|
||||
|
||||
/**
|
||||
* Notify that the stream has data in each track
|
||||
* for the stream's current time. Once this state becomes true, it will
|
||||
* always be true since we block stream time from progressing to times where
|
||||
* there isn't data in each track.
|
||||
*/
|
||||
virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
|
||||
|
||||
/**
|
||||
* Notify that the stream output is advancing. aCurrentTime is the graph's
|
||||
* current time. MediaStream::GraphTimeToStreamTime can be used to get the
|
||||
* stream time.
|
||||
*/
|
||||
virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
|
||||
|
||||
/**
|
||||
* Notify that an event has occurred on the Stream
|
||||
*/
|
||||
virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
|
||||
|
||||
/**
|
||||
* Notify that changes to one of the stream tracks have been queued.
|
||||
* aTrackEvents can be any combination of TRACK_EVENT_CREATED and
|
||||
* TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
|
||||
* at aTrackOffset (relative to the start of the stream).
|
||||
* aInputStream and aInputTrackID will be set if the changes originated
|
||||
* from an input stream's track. In practice they will only be used for
|
||||
* ProcessedMediaStreams.
|
||||
*/
|
||||
virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
TrackEventCommand aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream = nullptr,
|
||||
TrackID aInputTrackID = TRACK_INVALID) {}
|
||||
|
||||
/**
|
||||
* Notify queued audio data. Only audio data need to be queued. The video data
|
||||
* will be notified by MediaStreamVideoSink::SetCurrentFrame.
|
||||
*/
|
||||
virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
const AudioSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream = nullptr,
|
||||
TrackID aInputTrackID = TRACK_INVALID) {}
|
||||
|
||||
/**
|
||||
* Notify that all new tracks this iteration have been created.
|
||||
* This is to ensure that tracks added atomically to MediaStreamGraph
|
||||
* are also notified of atomically to MediaStreamListeners.
|
||||
*/
|
||||
virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener callbacks locked to
|
||||
* specific tracks. Override methods to be notified of audio or video data or
|
||||
* changes in track state.
|
||||
*
|
||||
* All notification methods are called from the media graph thread. Overriders
|
||||
* of these methods are responsible for all synchronization. Beware!
|
||||
* These methods are called without the media graph monitor held, so
|
||||
* reentry into media graph methods is possible, although very much discouraged!
|
||||
* You should do something non-blocking and non-reentrant (e.g. dispatch an
|
||||
* event to some thread) and return.
|
||||
* The listener is not allowed to add/remove any listeners from the parent
|
||||
* stream.
|
||||
*
|
||||
* If a listener is attached to a track that has already ended, we guarantee
|
||||
* to call NotifyEnded.
|
||||
*/
|
||||
class MediaStreamTrackListener
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
|
||||
|
||||
public:
|
||||
virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
const MediaSegment& aQueuedMedia) {}
|
||||
|
||||
virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
|
||||
const PrincipalHandle& aNewPrincipalHandle) {}
|
||||
|
||||
virtual void NotifyEnded() {}
|
||||
|
||||
virtual void NotifyRemoved() {}
|
||||
|
||||
protected:
|
||||
virtual ~MediaStreamTrackListener() {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener direct callbacks
|
||||
* from within AppendToTrack(). Note that your regular listener will
|
||||
* still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
|
||||
* you must be careful to ignore them if AddDirectListener was successful.
|
||||
*/
|
||||
class DirectMediaStreamListener : public MediaStreamListener
|
||||
{
|
||||
public:
|
||||
virtual ~DirectMediaStreamListener() {}
|
||||
|
||||
/*
|
||||
* This will be called on any DirectMediaStreamListener added to a
|
||||
* a SourceMediaStream when AppendToTrack() is called. The MediaSegment
|
||||
* will be the RawSegment (unresampled) if available in AppendToTrack().
|
||||
* Note that NotifyQueuedTrackChanges() calls will also still occur.
|
||||
*/
|
||||
virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aMedia) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a base class for media graph thread listener direct callbacks from
|
||||
* within AppendToTrack(). It is bound to a certain track and can only be
|
||||
* installed on audio tracks. Once added to a track on any stream in the graph,
|
||||
* the graph will try to install it at that track's source of media data.
|
||||
*
|
||||
* This works for TrackUnionStreams, which will forward the listener to the
|
||||
* track's input track if it exists, or wait for it to be created before
|
||||
* forwarding if it doesn't.
|
||||
* Once it reaches a SourceMediaStream, it can be successfully installed.
|
||||
* Other types of streams will fail installation since they are not supported.
|
||||
*
|
||||
* Note that this listener and others for the same track will still get
|
||||
* NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
|
||||
* to ignore them if this listener was successfully installed.
|
||||
*/
|
||||
class DirectMediaStreamTrackListener : public MediaStreamTrackListener
|
||||
{
|
||||
friend class SourceMediaStream;
|
||||
friend class TrackUnionStream;
|
||||
|
||||
public:
|
||||
/*
|
||||
* This will be called on any DirectMediaStreamTrackListener added to a
|
||||
* SourceMediaStream when AppendToTrack() is called for the listener's bound
|
||||
* track, using the thread of the AppendToTrack() caller. The MediaSegment
|
||||
* will be the RawSegment (unresampled) if available in AppendToTrack().
|
||||
* If the track is enabled at the source but has been disabled in one of the
|
||||
* streams in between the source and where it was originally added, aMedia
|
||||
* will be a disabled version of the one passed to AppendToTrack() as well.
|
||||
* Note that NotifyQueuedTrackChanges() calls will also still occur.
|
||||
*/
|
||||
virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
const MediaSegment& aMedia) {}
|
||||
|
||||
/**
|
||||
* When a direct listener is processed for installation by the
|
||||
* MediaStreamGraph it will be notified with whether the installation was
|
||||
* successful or not. The results of this installation are the following:
|
||||
* TRACK_NOT_FOUND_AT_SOURCE
|
||||
* We found the source stream of media data for this track, but the track
|
||||
* didn't exist. This should only happen if you try to install the listener
|
||||
* directly to a SourceMediaStream that doesn't contain the given TrackID.
|
||||
* STREAM_NOT_SUPPORTED
|
||||
* While looking for the data source of this track, we found a MediaStream
|
||||
* that is not a SourceMediaStream or a TrackUnionStream.
|
||||
* SUCCESS
|
||||
* Installation was successful and this listener will start receiving
|
||||
* NotifyRealtimeData on the next AppendToTrack().
|
||||
*/
|
||||
enum class InstallationResult {
|
||||
TRACK_NOT_FOUND_AT_SOURCE,
|
||||
TRACK_TYPE_NOT_SUPPORTED,
|
||||
STREAM_NOT_SUPPORTED,
|
||||
SUCCESS
|
||||
};
|
||||
virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
|
||||
virtual void NotifyDirectListenerUninstalled() {}
|
||||
|
||||
protected:
|
||||
virtual ~DirectMediaStreamTrackListener() {}
|
||||
|
||||
void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo);
|
||||
void MirrorAndDisableSegment(VideoSegment& aFrom, VideoSegment& aTo);
|
||||
void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
|
||||
StreamTime aTrackOffset,
|
||||
MediaSegment& aMedia);
|
||||
|
||||
void IncreaseDisabled()
|
||||
{
|
||||
++mDisabledCount;
|
||||
}
|
||||
void DecreaseDisabled()
|
||||
{
|
||||
--mDisabledCount;
|
||||
MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
|
||||
}
|
||||
|
||||
// Matches the number of disabled streams to which this listener is attached.
|
||||
// The number of streams are those between the stream the listener was added
|
||||
// and the SourceMediaStream that is the input of the data.
|
||||
Atomic<int32_t> mDisabledCount;
|
||||
|
||||
nsAutoPtr<MediaSegment> mMedia;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_MEDIASTREAMLISTENER_h_
|
@ -9,6 +9,7 @@
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MediaStreamListener.h"
|
||||
|
||||
#ifdef LOG
|
||||
#undef LOG
|
||||
@ -161,6 +162,12 @@ MediaStreamTrack::Destroy()
|
||||
mPrincipalHandleListener->Forget();
|
||||
mPrincipalHandleListener = nullptr;
|
||||
}
|
||||
for (auto l : mTrackListeners) {
|
||||
RemoveListener(l);
|
||||
}
|
||||
for (auto l : mDirectTrackListeners) {
|
||||
RemoveDirectListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
|
||||
@ -387,6 +394,11 @@ MediaStreamTrack::GetInputStream()
|
||||
ProcessedMediaStream*
|
||||
MediaStreamTrack::GetOwnedStream()
|
||||
{
|
||||
if (!mOwningStream)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mOwningStream->GetOwnedStream();
|
||||
}
|
||||
|
||||
@ -395,8 +407,10 @@ MediaStreamTrack::AddListener(MediaStreamTrackListener* aListener)
|
||||
{
|
||||
LOG(LogLevel::Debug, ("MediaStreamTrack %p adding listener %p",
|
||||
this, aListener));
|
||||
MOZ_ASSERT(GetOwnedStream());
|
||||
|
||||
GetOwnedStream()->AddTrackListener(aListener, mTrackID);
|
||||
mTrackListeners.AppendElement(aListener);
|
||||
}
|
||||
|
||||
void
|
||||
@ -405,27 +419,35 @@ MediaStreamTrack::RemoveListener(MediaStreamTrackListener* aListener)
|
||||
LOG(LogLevel::Debug, ("MediaStreamTrack %p removing listener %p",
|
||||
this, aListener));
|
||||
|
||||
GetOwnedStream()->RemoveTrackListener(aListener, mTrackID);
|
||||
if (GetOwnedStream()) {
|
||||
GetOwnedStream()->RemoveTrackListener(aListener, mTrackID);
|
||||
mTrackListeners.RemoveElement(aListener);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamTrack::AddDirectListener(MediaStreamTrackDirectListener *aListener)
|
||||
MediaStreamTrack::AddDirectListener(DirectMediaStreamTrackListener *aListener)
|
||||
{
|
||||
LOG(LogLevel::Debug, ("MediaStreamTrack %p (%s) adding direct listener %p to "
|
||||
"stream %p, track %d",
|
||||
this, AsAudioStreamTrack() ? "audio" : "video",
|
||||
aListener, GetOwnedStream(), mTrackID));
|
||||
MOZ_ASSERT(GetOwnedStream());
|
||||
|
||||
GetOwnedStream()->AddDirectTrackListener(aListener, mTrackID);
|
||||
mDirectTrackListeners.AppendElement(aListener);
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamTrack::RemoveDirectListener(MediaStreamTrackDirectListener *aListener)
|
||||
MediaStreamTrack::RemoveDirectListener(DirectMediaStreamTrackListener *aListener)
|
||||
{
|
||||
LOG(LogLevel::Debug, ("MediaStreamTrack %p removing direct listener %p from stream %p",
|
||||
this, aListener, GetOwnedStream()));
|
||||
|
||||
GetOwnedStream()->RemoveDirectTrackListener(aListener, mTrackID);
|
||||
if (GetOwnedStream()) {
|
||||
GetOwnedStream()->RemoveDirectTrackListener(aListener, mTrackID);
|
||||
mDirectTrackListeners.RemoveElement(aListener);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<MediaInputPort>
|
||||
|
@ -24,7 +24,7 @@ class MediaStream;
|
||||
class MediaStreamGraph;
|
||||
class MediaStreamGraphImpl;
|
||||
class MediaStreamTrackListener;
|
||||
class MediaStreamTrackDirectListener;
|
||||
class DirectMediaStreamTrackListener;
|
||||
class PeerConnectionImpl;
|
||||
class PeerConnectionMedia;
|
||||
class PeerIdentity;
|
||||
@ -363,8 +363,8 @@ public:
|
||||
* the listener succeeded (tracks originating from SourceMediaStreams) or
|
||||
* failed (e.g., WebAudio originated tracks).
|
||||
*/
|
||||
void AddDirectListener(MediaStreamTrackDirectListener *aListener);
|
||||
void RemoveDirectListener(MediaStreamTrackDirectListener *aListener);
|
||||
void AddDirectListener(DirectMediaStreamTrackListener *aListener);
|
||||
void RemoveDirectListener(DirectMediaStreamTrackListener *aListener);
|
||||
|
||||
/**
|
||||
* Sets up a MediaInputPort from the underlying track that this
|
||||
@ -378,6 +378,8 @@ public:
|
||||
*/
|
||||
bool IsForwardedThrough(MediaInputPort* aPort);
|
||||
|
||||
void SetMediaStreamSizeListener(DirectMediaStreamTrackListener* aListener);
|
||||
|
||||
protected:
|
||||
virtual ~MediaStreamTrack();
|
||||
|
||||
@ -416,6 +418,10 @@ protected:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIPrincipal> mPendingPrincipal;
|
||||
RefPtr<PrincipalHandleListener> mPrincipalHandleListener;
|
||||
// Keep tracking MediaStreamTrackListener and DirectMediaStreamTrackListener,
|
||||
// so we can remove them in |Destory|.
|
||||
nsTArray<RefPtr<MediaStreamTrackListener>> mTrackListeners;
|
||||
nsTArray<RefPtr<DirectMediaStreamTrackListener>> mDirectTrackListeners;
|
||||
nsString mID;
|
||||
MediaStreamTrackState mReadyState;
|
||||
bool mEnabled;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/dom/AudioTrack.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
#include "mozilla/dom/VideoTrack.h"
|
||||
#include "mozilla/dom/TrackEvent.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -106,9 +107,10 @@ already_AddRefed<VideoTrack>
|
||||
MediaTrackList::CreateVideoTrack(const nsAString& aId,
|
||||
const nsAString& aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aLanguage)
|
||||
const nsAString& aLanguage,
|
||||
VideoStreamTrack* aVideoTrack)
|
||||
{
|
||||
RefPtr<VideoTrack> track = new VideoTrack(aId, aKind, aLabel, aLanguage);
|
||||
RefPtr<VideoTrack> track = new VideoTrack(aId, aKind, aLabel, aLanguage, aVideoTrack);
|
||||
return track.forget();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ class AudioTrackList;
|
||||
class VideoTrackList;
|
||||
class AudioTrack;
|
||||
class VideoTrack;
|
||||
class VideoStreamTrack;
|
||||
|
||||
/**
|
||||
* Base class of AudioTrackList and VideoTrackList. The AudioTrackList and
|
||||
@ -58,11 +59,14 @@ public:
|
||||
const nsAString& aLanguage,
|
||||
bool aEnabled);
|
||||
|
||||
// For the case of src of HTMLMediaElement is non-MediaStream, leave the
|
||||
// aVideoTrack as default(nullptr).
|
||||
static already_AddRefed<VideoTrack>
|
||||
CreateVideoTrack(const nsAString& aId,
|
||||
const nsAString& aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aLanguage);
|
||||
const nsAString& aLanguage,
|
||||
VideoStreamTrack* aVideoTrack = nullptr);
|
||||
|
||||
virtual void EmptyTracks();
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MediaStreamGraphImpl.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
@ -200,7 +201,7 @@ TrackUnionStream::TrackUnionStream() :
|
||||
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = mListeners[j];
|
||||
l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
|
||||
MediaStreamListener::TRACK_EVENT_CREATED,
|
||||
TrackEventCommand::TRACK_EVENT_CREATED,
|
||||
*segment,
|
||||
aPort->GetSource(), aTrack->GetID());
|
||||
}
|
||||
@ -221,7 +222,7 @@ TrackUnionStream::TrackUnionStream() :
|
||||
map->mSegment = aTrack->GetSegment()->CreateEmptyClone();
|
||||
|
||||
for (int32_t i = mPendingDirectTrackListeners.Length() - 1; i >= 0; --i) {
|
||||
TrackBound<MediaStreamTrackDirectListener>& bound =
|
||||
TrackBound<DirectMediaStreamTrackListener>& bound =
|
||||
mPendingDirectTrackListeners[i];
|
||||
if (bound.mTrackID != map->mOutputTrackID) {
|
||||
continue;
|
||||
@ -256,7 +257,7 @@ TrackUnionStream::TrackUnionStream() :
|
||||
nsAutoPtr<MediaSegment> segment;
|
||||
segment = outputTrack->GetSegment()->CreateEmptyClone();
|
||||
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
|
||||
MediaStreamListener::TRACK_EVENT_ENDED,
|
||||
TrackEventCommand::TRACK_EVENT_ENDED,
|
||||
*segment,
|
||||
mTrackMap[aIndex].mInputPort->GetSource(),
|
||||
mTrackMap[aIndex].mInputTrackID);
|
||||
@ -334,7 +335,7 @@ TrackUnionStream::TrackUnionStream() :
|
||||
} else {
|
||||
// This part will be removed in bug 1201363.
|
||||
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
|
||||
outputStart, 0, *segment,
|
||||
outputStart, TrackEventCommand::TRACK_EVENT_NONE, *segment,
|
||||
map->mInputPort->GetSource(),
|
||||
map->mInputTrackID);
|
||||
}
|
||||
@ -355,7 +356,7 @@ TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) {
|
||||
if (entry.mOutputTrackID == aTrackID) {
|
||||
STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s",
|
||||
this, aTrackID, aEnabled ? "enabled" : "disabled"));
|
||||
for (MediaStreamTrackDirectListener* listener : entry.mOwnedDirectListeners) {
|
||||
for (DirectMediaStreamTrackListener* listener : entry.mOwnedDirectListeners) {
|
||||
bool oldEnabled = !mDisabledTrackIDs.Contains(aTrackID);
|
||||
if (!oldEnabled && aEnabled) {
|
||||
STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
|
||||
@ -399,10 +400,10 @@ TrackUnionStream::GetInputTrackIDFor(TrackID aTrackID)
|
||||
}
|
||||
|
||||
void
|
||||
TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
|
||||
TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
RefPtr<MediaStreamTrackDirectListener> listener = aListener;
|
||||
RefPtr<DirectMediaStreamTrackListener> listener = aListener;
|
||||
|
||||
for (TrackMapEntry& entry : mTrackMap) {
|
||||
if (entry.mOutputTrackID == aTrackID) {
|
||||
@ -422,14 +423,14 @@ TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDi
|
||||
}
|
||||
}
|
||||
|
||||
TrackBound<MediaStreamTrackDirectListener>* bound =
|
||||
TrackBound<DirectMediaStreamTrackListener>* bound =
|
||||
mPendingDirectTrackListeners.AppendElement();
|
||||
bound->mListener = listener.forget();
|
||||
bound->mTrackID = aTrackID;
|
||||
}
|
||||
|
||||
void
|
||||
TrackUnionStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
|
||||
TrackUnionStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID)
|
||||
{
|
||||
for (TrackMapEntry& entry : mTrackMap) {
|
||||
@ -460,7 +461,7 @@ TrackUnionStream::RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener*
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mPendingDirectTrackListeners.Length(); ++i) {
|
||||
TrackBound<MediaStreamTrackDirectListener>& bound =
|
||||
TrackBound<DirectMediaStreamTrackListener>& bound =
|
||||
mPendingDirectTrackListeners[i];
|
||||
if (bound.mListener == aListener && bound.mTrackID == aTrackID) {
|
||||
mPendingDirectTrackListeners.RemoveElementAt(i);
|
||||
|
@ -53,7 +53,7 @@ protected:
|
||||
// These are direct track listeners that have been added to this
|
||||
// TrackUnionStream-track and forwarded to the input track. We will update
|
||||
// these when this track's disabled status changes.
|
||||
nsTArray<RefPtr<MediaStreamTrackDirectListener>> mOwnedDirectListeners;
|
||||
nsTArray<RefPtr<DirectMediaStreamTrackListener>> mOwnedDirectListeners;
|
||||
};
|
||||
|
||||
// Add the track to this stream, retaining its TrackID if it has never
|
||||
@ -65,9 +65,9 @@ protected:
|
||||
uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo,
|
||||
bool* aOutputTrackFinished);
|
||||
|
||||
void AddDirectTrackListenerImpl(already_AddRefed<MediaStreamTrackDirectListener> aListener,
|
||||
void AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
|
||||
TrackID aTrackID) override;
|
||||
void RemoveDirectTrackListenerImpl(MediaStreamTrackDirectListener* aListener,
|
||||
void RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
|
||||
TrackID aTrackID) override;
|
||||
|
||||
nsTArray<TrackMapEntry> mTrackMap;
|
||||
@ -81,7 +81,7 @@ protected:
|
||||
|
||||
// Direct track listeners that have not been forwarded to their input stream
|
||||
// yet. We'll forward these as their inputs become available.
|
||||
nsTArray<TrackBound<MediaStreamTrackDirectListener>> mPendingDirectTrackListeners;
|
||||
nsTArray<TrackBound<DirectMediaStreamTrackListener>> mPendingDirectTrackListeners;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -5,6 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
#include "mozilla/dom/VideoTrack.h"
|
||||
#include "mozilla/dom/VideoTrackBinding.h"
|
||||
#include "mozilla/dom/VideoTrackList.h"
|
||||
@ -15,12 +16,25 @@ namespace dom {
|
||||
VideoTrack::VideoTrack(const nsAString& aId,
|
||||
const nsAString& aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aLanguage)
|
||||
const nsAString& aLanguage,
|
||||
VideoStreamTrack* aStreamTarck)
|
||||
: MediaTrack(aId, aKind, aLabel, aLanguage)
|
||||
, mSelected(false)
|
||||
, mVideoStreamTrack(aStreamTarck)
|
||||
{
|
||||
}
|
||||
|
||||
VideoTrack::~VideoTrack()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(VideoTrack, MediaTrack, mVideoStreamTrack)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(VideoTrack, MediaTrack)
|
||||
NS_IMPL_RELEASE_INHERITED(VideoTrack, MediaTrack)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(VideoTrack)
|
||||
NS_INTERFACE_MAP_END_INHERITING(MediaTrack)
|
||||
|
||||
JSObject*
|
||||
VideoTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
|
@ -13,6 +13,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class VideoTrackList;
|
||||
class VideoStreamTrack;
|
||||
|
||||
class VideoTrack : public MediaTrack
|
||||
{
|
||||
@ -20,7 +21,11 @@ public:
|
||||
VideoTrack(const nsAString& aId,
|
||||
const nsAString& aKind,
|
||||
const nsAString& aLabel,
|
||||
const nsAString& aLanguage);
|
||||
const nsAString& aLanguage,
|
||||
VideoStreamTrack* aStreamTarck = nullptr);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(VideoTrack, MediaTrack)
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
@ -36,6 +41,11 @@ public:
|
||||
// aFlags contains FIRE_NO_EVENTS because no events are fired in such cases.
|
||||
void SetEnabledInternal(bool aEnabled, int aFlags) override;
|
||||
|
||||
// Get associated video stream track when the video track comes from
|
||||
// MediaStream. This might be nullptr when the src of owning HTMLMediaElement
|
||||
// is not MediaStream.
|
||||
VideoStreamTrack* GetVideoStreamTrack() { return mVideoStreamTrack; }
|
||||
|
||||
// WebIDL
|
||||
bool Selected() const
|
||||
{
|
||||
@ -48,7 +58,10 @@ public:
|
||||
void SetSelected(bool aSelected);
|
||||
|
||||
private:
|
||||
virtual ~VideoTrack();
|
||||
|
||||
bool mSelected;
|
||||
RefPtr<VideoStreamTrack> mVideoStreamTrack;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -31,18 +31,18 @@ VideoTrackList::RemoveTrack(const RefPtr<MediaTrack>& aTrack)
|
||||
// need to be done after RemoveTrack. Also the call of
|
||||
// |MediaTrackList::RemoveTrack| is necessary even when mSelectedIndex = -1.
|
||||
bool found;
|
||||
VideoTrack* videoTrack = IndexedGetter(mSelectedIndex, found);
|
||||
VideoTrack* selectedVideoTrack = IndexedGetter(mSelectedIndex, found);
|
||||
MediaTrackList::RemoveTrack(aTrack);
|
||||
if (mSelectedIndex == -1) {
|
||||
// There was no selected track and we don't select another track on removal.
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(found, "When mSelectedIndex is set it should point to a track");
|
||||
MOZ_ASSERT(videoTrack, "The mSelectedIndex should be set to video track only");
|
||||
MOZ_ASSERT(selectedVideoTrack, "The mSelectedIndex should be set to video track only");
|
||||
|
||||
// Let the caller of RemoveTrack deal with choosing the new selected track if
|
||||
// it removes the currently-selected track.
|
||||
if (aTrack == videoTrack) {
|
||||
if (aTrack == selectedVideoTrack) {
|
||||
mSelectedIndex = -1;
|
||||
return;
|
||||
}
|
||||
@ -51,7 +51,7 @@ VideoTrackList::RemoveTrack(const RefPtr<MediaTrack>& aTrack)
|
||||
// currently-selected video track. We need to find the new location of the
|
||||
// selected track.
|
||||
for (size_t ix = 0; ix < mTracks.Length(); ix++) {
|
||||
if (mTracks[ix] == videoTrack) {
|
||||
if (mTracks[ix] == selectedVideoTrack) {
|
||||
mSelectedIndex = ix;
|
||||
return;
|
||||
}
|
||||
|
@ -21,9 +21,6 @@
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define strtoll _strtoi64
|
||||
#if _MSC_VER < 1900
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -66,7 +66,7 @@ void
|
||||
MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
TrackEventCommand aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream,
|
||||
TrackID aInputTrackID)
|
||||
@ -74,7 +74,7 @@ MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
if (!mDirectConnected) {
|
||||
NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, aQueuedMedia);
|
||||
} else {
|
||||
if (aTrackEvents != 0) {
|
||||
if (aTrackEvents != TrackEventCommand::TRACK_EVENT_NONE) {
|
||||
// forward events (TRACK_EVENT_ENDED) but not the media
|
||||
if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
|
||||
VideoSegment segment;
|
||||
@ -125,7 +125,7 @@ MediaEncoder::NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
|
||||
|
||||
void
|
||||
MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event)
|
||||
MediaStreamGraphEvent event)
|
||||
{
|
||||
// In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
|
||||
LOG(LogLevel::Debug, ("NotifyRemoved in [MediaEncoder]."));
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "TrackEncoder.h"
|
||||
#include "ContainerWriter.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
@ -51,7 +52,7 @@ namespace mozilla {
|
||||
* 4) To stop encoding, remove this component from its source stream.
|
||||
* => sourceStream->RemoveListener(encoder);
|
||||
*/
|
||||
class MediaEncoder : public MediaStreamDirectListener
|
||||
class MediaEncoder : public DirectMediaStreamListener
|
||||
{
|
||||
public :
|
||||
enum {
|
||||
@ -125,7 +126,7 @@ public :
|
||||
*/
|
||||
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
|
||||
StreamTime aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
TrackEventCommand aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia,
|
||||
MediaStream* aInputStream,
|
||||
TrackID aInputTrackID) override;
|
||||
@ -144,7 +145,7 @@ public :
|
||||
* * Notified the stream is being removed.
|
||||
*/
|
||||
void NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event) override;
|
||||
MediaStreamGraphEvent event) override;
|
||||
|
||||
/**
|
||||
* Creates an encoder with a given MIME type. Returns null if we are unable
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "TrackEncoder.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
@ -41,6 +42,14 @@ TrackEncoder::TrackEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
void TrackEncoder::NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamGraphEvent event)
|
||||
{
|
||||
if (event == MediaStreamGraphEvent::EVENT_REMOVED) {
|
||||
NotifyEndOfStream();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
TrackID aID,
|
||||
@ -91,7 +100,7 @@ AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
|
||||
|
||||
// The stream has stopped and reached the end of track.
|
||||
if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
|
||||
if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
|
||||
LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED .");
|
||||
NotifyEndOfStream();
|
||||
}
|
||||
@ -232,7 +241,7 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
AppendVideoSegment(video);
|
||||
|
||||
// The stream has stopped and reached the end of track.
|
||||
if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
|
||||
if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
|
||||
LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED .");
|
||||
NotifyEndOfStream();
|
||||
}
|
||||
|
@ -48,12 +48,7 @@ public:
|
||||
* MediaStreamGraph. Called on the MediaStreamGraph thread.
|
||||
*/
|
||||
void NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event)
|
||||
{
|
||||
if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
|
||||
NotifyEndOfStream();
|
||||
}
|
||||
}
|
||||
MediaStreamGraphEvent event);
|
||||
|
||||
/**
|
||||
* Creates and sets up meta data for a specific codec, called on the worker
|
||||
|
@ -23,8 +23,6 @@ GMPContentChild::GMPContentChild(GMPChild* aChild)
|
||||
GMPContentChild::~GMPContentChild()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GMPContentChild);
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
MessageLoop*
|
||||
|
@ -44,8 +44,6 @@ GMPContentParent::GMPContentParent(GMPParent* aParent)
|
||||
|
||||
GMPContentParent::~GMPContentParent()
|
||||
{
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
class ReleaseGMPContentParent : public Runnable
|
||||
|
@ -266,8 +266,6 @@ GMPServiceChild::GMPServiceChild()
|
||||
|
||||
GMPServiceChild::~GMPServiceChild()
|
||||
{
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
PGMPContentParent*
|
||||
|
@ -1835,8 +1835,6 @@ GeckoMediaPluginServiceParent::GetById(uint32_t aPluginId)
|
||||
|
||||
GMPServiceParent::~GMPServiceParent()
|
||||
{
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "VP8TrackEncoder.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA
|
||||
|
||||
using ::testing::TestWithParam;
|
||||
@ -296,7 +297,7 @@ TEST(VP8VideoTrackEncoder, EncodeComplete)
|
||||
|
||||
// track end notification.
|
||||
VideoSegment segment;
|
||||
encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment);
|
||||
encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, TrackEventCommand::TRACK_EVENT_ENDED, segment);
|
||||
|
||||
// Pull Encoded Data back from encoder. Since we have sent
|
||||
// EOS to encoder, encoder.GetEncodedTrack should return
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define CAPTURETASK_H
|
||||
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "PrincipalChangeObserver.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "MediaData.h"
|
||||
#include "MediaQueue.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "OutputStreamManager.h"
|
||||
#include "SharedBuffer.h"
|
||||
#include "VideoSegment.h"
|
||||
@ -30,7 +31,6 @@ struct PlaybackInfoInit {
|
||||
};
|
||||
|
||||
class DecodedStreamGraphListener : public MediaStreamListener {
|
||||
typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
|
||||
public:
|
||||
DecodedStreamGraphListener(MediaStream* aStream,
|
||||
MozPromiseHolder<GenericPromise>&& aPromise)
|
||||
@ -52,7 +52,7 @@ public:
|
||||
|
||||
void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
|
||||
{
|
||||
if (event == EVENT_FINISHED) {
|
||||
if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
|
||||
|
@ -127,6 +127,7 @@ EXPORTS += [
|
||||
'MediaSegment.h',
|
||||
'MediaStatistics.h',
|
||||
'MediaStreamGraph.h',
|
||||
'MediaStreamListener.h',
|
||||
'MediaTimer.h',
|
||||
'MediaTrack.h',
|
||||
'MediaTrackList.h',
|
||||
@ -238,6 +239,7 @@ UNIFIED_SOURCES += [
|
||||
'MediaShutdownManager.cpp',
|
||||
'MediaStreamError.cpp',
|
||||
'MediaStreamGraph.cpp',
|
||||
'MediaStreamListener.cpp',
|
||||
'MediaStreamTrack.cpp',
|
||||
'MediaTimer.cpp',
|
||||
'MediaTrack.cpp',
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "AudioNodeStream.h"
|
||||
|
||||
#include "MediaStreamGraphImpl.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "ThreeDPoint.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
@ -647,7 +648,7 @@ AudioNodeStream::AdvanceOutputSegment()
|
||||
AudioSegment tmpSegment;
|
||||
tmpSegment.AppendAndConsumeChunk(©Chunk);
|
||||
l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
|
||||
segment->GetDuration(), 0, tmpSegment);
|
||||
segment->GetDuration(), TrackEventCommand::TRACK_EVENT_NONE, tmpSegment);
|
||||
}
|
||||
}
|
||||
|
||||
@ -662,7 +663,7 @@ AudioNodeStream::FinishOutput()
|
||||
AudioSegment emptySegment;
|
||||
l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
|
||||
track->GetSegment()->GetDuration(),
|
||||
MediaStreamListener::TRACK_EVENT_ENDED, emptySegment);
|
||||
TrackEventCommand::TRACK_EVENT_ENDED, emptySegment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ SpeechStreamListener::ConvertAndDispatchAudioChunk(int aDuration, float aVolume,
|
||||
|
||||
void
|
||||
SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event)
|
||||
MediaStreamGraphEvent event)
|
||||
{
|
||||
// TODO dispatch SpeechEnd event so services can be informed
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_SpeechStreamListener_h
|
||||
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "AudioSegment.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -31,7 +32,7 @@ public:
|
||||
TrackID aInputTrackID) override;
|
||||
|
||||
void NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event) override;
|
||||
MediaStreamGraphEvent event) override;
|
||||
|
||||
private:
|
||||
template<typename SampleFormatType>
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include "AudioChannelAgent.h"
|
||||
#include "AudioChannelService.h"
|
||||
#include "AudioSegment.h"
|
||||
#include "MediaStreamListener.h"
|
||||
#include "nsSpeechTask.h"
|
||||
#include "nsSynthVoiceRegistry.h"
|
||||
#include "SharedBuffer.h"
|
||||
#include "SpeechSynthesis.h"
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
@ -53,10 +55,10 @@ public:
|
||||
}
|
||||
|
||||
void NotifyEvent(MediaStreamGraph* aGraph,
|
||||
MediaStreamListener::MediaStreamGraphEvent event) override
|
||||
MediaStreamGraphEvent event) override
|
||||
{
|
||||
switch (event) {
|
||||
case EVENT_FINISHED:
|
||||
case MediaStreamGraphEvent::EVENT_FINISHED:
|
||||
{
|
||||
if (!mStarted) {
|
||||
mStarted = true;
|
||||
@ -70,7 +72,7 @@ public:
|
||||
aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
|
||||
}
|
||||
break;
|
||||
case EVENT_REMOVED:
|
||||
case MediaStreamGraphEvent::EVENT_REMOVED:
|
||||
mSpeechTask = nullptr;
|
||||
// Dereference MediaStream to destroy safety
|
||||
mStream = nullptr;
|
||||
|
@ -13,6 +13,9 @@
|
||||
#include "nsISpeechService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class SharedBuffer;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class SpeechSynthesisUtterance;
|
||||
|
@ -62,6 +62,7 @@ DIRS += [
|
||||
'fmradio',
|
||||
'gamepad',
|
||||
'geolocation',
|
||||
'grid',
|
||||
'html',
|
||||
'icc',
|
||||
'inputport',
|
||||
|
@ -108,7 +108,6 @@ static const TCHAR kPluginIgnoreSubclassProperty[] = TEXT("PluginIgnoreSubclassP
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include "nsCocoaFeatures.h"
|
||||
#include "PluginUtilsOSX.h"
|
||||
#endif // defined(XP_MACOSX)
|
||||
|
||||
@ -3534,10 +3533,8 @@ PluginInstanceChild::EnsureCurrentBuffer(void)
|
||||
void *caLayer = nullptr;
|
||||
if (mDrawingModel == NPDrawingModelCoreGraphics) {
|
||||
if (!mCGLayer) {
|
||||
bool avoidCGCrashes = !nsCocoaFeatures::OnMountainLionOrLater() &&
|
||||
(GetQuirks() & QUIRK_FLASH_AVOID_CGMODE_CRASHES);
|
||||
caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(CallCGDraw, this,
|
||||
avoidCGCrashes,
|
||||
caLayer = mozilla::plugins::PluginUtilsOSX::GetCGLayer(CallCGDraw,
|
||||
this, false,
|
||||
mContentsScaleFactor);
|
||||
|
||||
if (!caLayer) {
|
||||
|
@ -154,15 +154,6 @@ PluginModuleChild::PluginModuleChild(bool aIsChrome)
|
||||
|
||||
PluginModuleChild::~PluginModuleChild()
|
||||
{
|
||||
if (mTransport) {
|
||||
// For some reason IPDL doesn't automatically delete the channel for a
|
||||
// bridged protocol (bug 1090570). So we have to do it ourselves. This
|
||||
// code is only invoked for PluginModuleChild instances created via
|
||||
// bridging; otherwise mTransport is null.
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTransport);
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
}
|
||||
|
||||
if (mIsChrome) {
|
||||
MOZ_ASSERT(gChromeInstance == this);
|
||||
|
||||
|
@ -723,10 +723,6 @@ PluginModuleContentParent::PluginModuleContentParent(bool aAllowAsyncInit)
|
||||
|
||||
PluginModuleContentParent::~PluginModuleContentParent()
|
||||
{
|
||||
RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(GetTransport());
|
||||
XRE_GetIOMessageLoop()->PostTask(task.forget());
|
||||
|
||||
|
||||
Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
|
||||
}
|
||||
|
||||
|
@ -36,10 +36,6 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
|
||||
[test_manyWindows.html]
|
||||
skip-if = buildapp == 'b2g'
|
||||
[test_mozsettings.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android'
|
||||
[test_mozsettingsWatch.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android'
|
||||
[test_optional_api_params.html]
|
||||
skip-if = buildapp == 'b2g'
|
||||
[test_shutdown.html]
|
||||
|
@ -1,92 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=478911
|
||||
-->
|
||||
<head>
|
||||
<title>Test for getCurrentPosition </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="geolocation_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777594">Mozilla Bug 777594</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
var timeToWaitMs = 1000;
|
||||
|
||||
resume_geolocationProvider(function() {
|
||||
force_prompt(true, test1);
|
||||
});
|
||||
|
||||
SpecialPowers.importInMainProcess("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
|
||||
function test1() {
|
||||
//This pushPermissions call is after pushPrefEnv call and pushPrefEnv calls follow after this
|
||||
SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document},
|
||||
{'type': 'settings-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-read', 'allow': true, 'context': document}
|
||||
], test2);
|
||||
}
|
||||
|
||||
function test2() {
|
||||
ok(navigator.geolocation, "get geolocation object");
|
||||
|
||||
toggleGeolocationSetting(false, function() {
|
||||
ok(true, "turned off geolocation via mozSettings");
|
||||
setTimeout(function() {
|
||||
navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOff,
|
||||
failureCallbackAfterMozsettingOff);
|
||||
}, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
|
||||
});
|
||||
}
|
||||
|
||||
function successCallbackAfterMozsettingOff(position) {
|
||||
ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
|
||||
|
||||
toggleGeolocationSetting(true, function() {
|
||||
ok(true, "turned on geolocation via mozSettings");
|
||||
setTimeout(function() {
|
||||
navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
|
||||
failureCallbackAfterMozsettingOn);
|
||||
}, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
|
||||
});
|
||||
}
|
||||
|
||||
function failureCallbackAfterMozsettingOff(error) {
|
||||
ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
|
||||
|
||||
toggleGeolocationSetting(true, function() {
|
||||
ok(true, "turned on geolocation via mozSettings");
|
||||
setTimeout(function() {
|
||||
navigator.geolocation.getCurrentPosition(successCallbackAfterMozsettingOn,
|
||||
failureCallbackAfterMozsettingOn);
|
||||
}, timeToWaitMs); // need to wait a bit for all of these async callbacks to finish
|
||||
});
|
||||
}
|
||||
|
||||
function successCallbackAfterMozsettingOn(position) {
|
||||
ok(true, "Geolocation worked after setting geolocation.enabled to true.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function failureCallbackAfterMozsettingOn(error) {
|
||||
ok(false, "Geolocation didn't work after setting geolocation.enabled to true.");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,97 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=478911
|
||||
-->
|
||||
<head>
|
||||
<title>Test for getCurrentPosition </title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="geolocation_common.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=777594">Mozilla Bug 777594</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
resume_geolocationProvider(function() {
|
||||
force_prompt(true, test1);
|
||||
});
|
||||
|
||||
SpecialPowers.importInMainProcess("resource://gre/modules/SettingsRequestManager.jsm");
|
||||
|
||||
function test1() {
|
||||
//This pushPermissions call is after pushPrefEnv call and pushPrefEnv calls follow after this
|
||||
SpecialPowers.pushPermissions([{'type': 'settings-read', 'allow': true, 'context': document},
|
||||
{'type': 'settings-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-write', 'allow': true, 'context': document},
|
||||
{'type': 'settings-api-read', 'allow': true, 'context': document}
|
||||
], test2);
|
||||
}
|
||||
|
||||
var watchId;
|
||||
function test2() {
|
||||
ok(navigator.geolocation, "get geolocation object");
|
||||
|
||||
toggleGeolocationSetting(false, function() {
|
||||
ok(true, "turned off geolocation via mozSettings");
|
||||
setTimeout(function() {
|
||||
watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOff,
|
||||
failureCallbackAfterMozsettingOff);
|
||||
}, 500); // need to wait a bit for all of these async callbacks to finish
|
||||
});
|
||||
}
|
||||
|
||||
function successCallbackAfterMozsettingOff(position) {
|
||||
ok(false, "Success callback should not have been called after setting geolocation.enabled to false.");
|
||||
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
toggleGeolocationSetting(true, function() {
|
||||
ok(true, "turned on geolocation via mozSettings");
|
||||
setTimeout(function() {
|
||||
watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOn,
|
||||
failureCallbackAfterMozsettingOn);
|
||||
}, 500); // need to wait a bit for all of these async callbacks to finish
|
||||
});
|
||||
}
|
||||
|
||||
function failureCallbackAfterMozsettingOff(error) {
|
||||
ok(true, "Geolocation didn't work after setting geolocation.enabled to false.");
|
||||
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
toggleGeolocationSetting(true, function() {
|
||||
ok(true, "turned on geolocation via mozSettings");
|
||||
setTimeout(function() {
|
||||
watchId = navigator.geolocation.watchPosition(successCallbackAfterMozsettingOn,
|
||||
failureCallbackAfterMozsettingOn);
|
||||
}, 500); // need to wait a bit for all of these async callbacks to finish
|
||||
});
|
||||
}
|
||||
|
||||
function successCallbackAfterMozsettingOn(position) {
|
||||
ok(true, "Geolocation worked after setting geolocation.enabled to true.");
|
||||
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function failureCallbackAfterMozsettingOn(error) {
|
||||
ok(false, "Geolocation didn't work after setting geolocation.enabled to true.");
|
||||
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -25,6 +25,9 @@ interface WindowClient : Client {
|
||||
|
||||
[Throws, NewObject]
|
||||
Promise<WindowClient> focus();
|
||||
|
||||
[Throws, NewObject]
|
||||
Promise<WindowClient> navigate(USVString url);
|
||||
};
|
||||
|
||||
enum FrameType {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user