зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c, a=merge
This commit is contained in:
Коммит
09f1e96e74
|
@ -13,7 +13,6 @@ DEFFILE = SRCDIR + '/IA2Marshal.def'
|
|||
OS_LIBS += [
|
||||
'uuid',
|
||||
'kernel32',
|
||||
'rpcns4',
|
||||
'rpcrt4',
|
||||
'ole32',
|
||||
'oleaut32',
|
||||
|
|
|
@ -22,7 +22,6 @@ DEFFILE = SRCDIR + '/AccessibleMarshal.def'
|
|||
|
||||
OS_LIBS += [
|
||||
'kernel32',
|
||||
'rpcns4',
|
||||
'rpcrt4',
|
||||
'oleaut32',
|
||||
]
|
||||
|
|
|
@ -202,7 +202,7 @@ var Connection = Class({
|
|||
},
|
||||
poolFor: function(id) {
|
||||
for (let pool of this.pools.values()) {
|
||||
if pool.has(id)
|
||||
if (pool.has(id))
|
||||
return pool;
|
||||
}
|
||||
},
|
||||
|
@ -797,7 +797,7 @@ var Tab = Client.from({
|
|||
"storageActor": "storage",
|
||||
"gcliActor": "gcli",
|
||||
"memoryActor": "memory",
|
||||
"eventLoopLag": "eventLoopLag"
|
||||
"eventLoopLag": "eventLoopLag",
|
||||
|
||||
"trace": "trace", // missing
|
||||
}
|
||||
|
|
|
@ -322,7 +322,6 @@ pref("media.fragmented-mp4.gonk.enabled", true);
|
|||
pref("media.video-queue.default-size", 3);
|
||||
|
||||
// optimize images' memory usage
|
||||
pref("image.downscale-during-decode.enabled", true);
|
||||
pref("image.mem.decodeondraw", true);
|
||||
pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
|
||||
// Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
MOZ_AUTOMATION_L10N_CHECK=0
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
. "$topsrcdir/build/unix/mozconfig.linux32"
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
MOZ_AUTOMATION_L10N_CHECK=0
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
. "$topsrcdir/build/unix/mozconfig.linux"
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
|
||||
# Use sccache
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
MOZ_AUTOMATION_L10N_CHECK=0
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
|
||||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
MOZ_AUTOMATION_L10N_CHECK=0
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
|
||||
|
||||
ac_add_options --enable-application=b2g/dev
|
||||
|
|
|
@ -2,6 +2,7 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0
|
|||
MOZ_AUTOMATION_PACKAGE_TESTS=0
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. $topsrcdir/build/macosx/mozconfig.common
|
||||
|
||||
ac_add_options --enable-application=b2g/dev
|
||||
|
|
|
@ -4,6 +4,7 @@ MOZ_AUTOMATION_PACKAGE_TESTS=0
|
|||
MOZ_AUTOMATION_INSTALLER=0
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=0
|
||||
MOZ_AUTOMATION_SDK=0
|
||||
. "$topsrcdir/browser/config/mozconfigs/win32/nightly"
|
||||
|
||||
ac_add_options --enable-application=b2g/dev
|
||||
|
|
|
@ -266,7 +266,7 @@ var gSanitizePromptDialog = {
|
|||
this.showItemList();
|
||||
else
|
||||
this.hideItemList();
|
||||
}
|
||||
},
|
||||
|
||||
#ifdef CRH_DIALOG_TREE_VIEW
|
||||
// A duration value; used in the same context as Sanitizer.TIMESPAN_HOUR,
|
||||
|
|
|
@ -83,6 +83,9 @@
|
|||
<field name="PlacesUtils" readonly="true">
|
||||
(Components.utils.import("resource://gre/modules/PlacesUtils.jsm", {})).PlacesUtils;
|
||||
</field>
|
||||
<field name="AppConstants" readonly="true">
|
||||
(Components.utils.import("resource://gre/modules/AppConstants.jsm", {})).AppConstants;
|
||||
</field>
|
||||
<field name="mTabBox" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
|
||||
</field>
|
||||
|
@ -120,11 +123,7 @@
|
|||
new Map();
|
||||
</field>
|
||||
<field name="arrowKeysShouldWrap" readonly="true">
|
||||
#ifdef XP_MACOSX
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
this.AppConstants.platform == "macosx";
|
||||
</field>
|
||||
|
||||
<field name="_autoScrollPopup">
|
||||
|
@ -2722,18 +2721,19 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
<!-- Opens a given tab to a non-remote window. -->
|
||||
<method name="openNonRemoteWindow">
|
||||
<parameter name="aTab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!this.AppConstants.E10S_TESTING_ONLY) {
|
||||
throw "This method is intended only for e10s testing!";
|
||||
}
|
||||
let url = aTab.linkedBrowser.currentURI.spec;
|
||||
return window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no,non-remote", url);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
#endif
|
||||
|
||||
<method name="moveTabTo">
|
||||
<parameter name="aTab"/>
|
||||
|
@ -3094,14 +3094,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
|
||||
aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
|
||||
!this.mCurrentTab.pinned) {
|
||||
this.removeCurrentTab({animate: true});
|
||||
aEvent.preventDefault();
|
||||
if (this.AppConstants.platform != "macosx") {
|
||||
if (aEvent.ctrlKey && !aEvent.shiftKey && !aEvent.metaKey &&
|
||||
aEvent.keyCode == KeyEvent.DOM_VK_F4 &&
|
||||
!this.mCurrentTab.pinned) {
|
||||
this.removeCurrentTab({animate: true});
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -3133,21 +3133,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
if (!aEvent.metaKey)
|
||||
return;
|
||||
if (this.AppConstants.platform == "macosx") {
|
||||
if (!aEvent.metaKey)
|
||||
return;
|
||||
|
||||
var offset = 1;
|
||||
switch (aEvent.charCode) {
|
||||
case '}'.charCodeAt(0):
|
||||
offset = -1;
|
||||
case '{'.charCodeAt(0):
|
||||
if (window.getComputedStyle(this, null).direction == "ltr")
|
||||
offset *= -1;
|
||||
this.tabContainer.advanceSelectedTab(offset, true);
|
||||
aEvent.preventDefault();
|
||||
var offset = 1;
|
||||
switch (aEvent.charCode) {
|
||||
case '}'.charCodeAt(0):
|
||||
offset = -1;
|
||||
case '{'.charCodeAt(0):
|
||||
if (window.getComputedStyle(this, null).direction == "ltr")
|
||||
offset *= -1;
|
||||
this.tabContainer.advanceSelectedTab(offset, true);
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -3755,15 +3755,16 @@
|
|||
</xul:hbox>
|
||||
<xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1"
|
||||
style="min-width: 1px;"
|
||||
#ifndef XP_MACOSX
|
||||
clicktoscroll="true"
|
||||
#endif
|
||||
class="tabbrowser-arrowscrollbox">
|
||||
# This is a hack to circumvent bug 472020, otherwise the tabs show up on the
|
||||
# right of the newtab button.
|
||||
<!--
|
||||
This is a hack to circumvent bug 472020, otherwise the tabs show up on the
|
||||
right of the newtab button.
|
||||
-->
|
||||
<children includes="tab"/>
|
||||
# This is to ensure anything extensions put here will go before the newtab
|
||||
# button, necessary due to the previous hack.
|
||||
<!--
|
||||
This is to ensure anything extensions put here will go before the newtab
|
||||
button, necessary due to the previous hack.
|
||||
-->
|
||||
<children/>
|
||||
<xul:toolbarbutton class="tabs-newtab-button"
|
||||
anonid="tabs-newtab-button"
|
||||
|
@ -4397,11 +4398,8 @@
|
|||
sourceNode.ownerDocument.defaultView.gMultiProcessBrowser)
|
||||
return dt.effectAllowed = "none";
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
return dt.effectAllowed = event.altKey ? "copy" : "move";
|
||||
#else
|
||||
return dt.effectAllowed = event.ctrlKey ? "copy" : "move";
|
||||
#endif
|
||||
let copyModifier = this.AppConstants.platform == "macosx" ? event.altKey : event.ctrlKey;
|
||||
return dt.effectAllowed = copyModifier ? "copy" : "move";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4556,13 +4554,13 @@
|
|||
]]></handler>
|
||||
|
||||
<handler event="dblclick"><![CDATA[
|
||||
#ifndef XP_MACOSX
|
||||
// When the tabbar has an unified appearance with the titlebar
|
||||
// and menubar, a double-click in it should have the same behavior
|
||||
// as double-clicking the titlebar
|
||||
if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
|
||||
return;
|
||||
#endif
|
||||
if (gBrowser.AppConstants.platform != "macosx") {
|
||||
// When the tabbar has an unified appearance with the titlebar
|
||||
// and menubar, a double-click in it should have the same behavior
|
||||
// as double-clicking the titlebar
|
||||
if (TabsInTitlebar.enabled || this.parentNode._dragBindingAlive)
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.button != 0 ||
|
||||
event.originalTarget.localName != "box")
|
||||
|
@ -4646,12 +4644,17 @@
|
|||
]]></handler>
|
||||
|
||||
<handler event="keydown" group="system"><![CDATA[
|
||||
if (event.altKey || event.shiftKey ||
|
||||
#ifdef XP_MACOSX
|
||||
!event.metaKey)
|
||||
#else
|
||||
!event.ctrlKey || event.metaKey)
|
||||
#endif
|
||||
if (event.altKey || event.shiftKey)
|
||||
return;
|
||||
|
||||
let wrongModifiers;
|
||||
if (this.tabbrowser.AppConstants.platform == "macosx") {
|
||||
wrongModifiers = !event.metaKey;
|
||||
} else {
|
||||
wrongModifiers = !event.ctrlKey || event.metaKey;
|
||||
}
|
||||
|
||||
if (wrongModifiers)
|
||||
return;
|
||||
|
||||
// Don't check if the event was already consumed because tab navigation
|
||||
|
@ -4978,13 +4981,12 @@
|
|||
window.moveTo(left, top);
|
||||
window.focus();
|
||||
} else {
|
||||
this.tabbrowser.replaceTabWithWindow(draggedTab, { screenX: left,
|
||||
screenY: top,
|
||||
#ifndef XP_WIN
|
||||
outerWidth: winWidth,
|
||||
outerHeight: winHeight
|
||||
#endif
|
||||
});
|
||||
let props = { screenX: left, screenY: top };
|
||||
if (this.tabbrowser.AppConstants.platform != "win") {
|
||||
props.outerWidth = winWidth;
|
||||
props.outerHeight = winHeight;
|
||||
}
|
||||
this.tabbrowser.replaceTabWithWindow(draggedTab, props);
|
||||
}
|
||||
event.stopPropagation();
|
||||
]]></handler>
|
||||
|
|
|
@ -141,7 +141,7 @@ browser.jar:
|
|||
content/browser/searchSuggestionUI.js (content/searchSuggestionUI.js)
|
||||
content/browser/searchSuggestionUI.css (content/searchSuggestionUI.css)
|
||||
content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||
* content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
* content/browser/utilityOverlay.js (content/utilityOverlay.js)
|
||||
content/browser/web-panels.js (content/web-panels.js)
|
||||
|
|
|
@ -55,6 +55,7 @@ def getUrlProperties(filename):
|
|||
('completeMarUrl', lambda m: m.endswith('.complete.mar')),
|
||||
('partialMarUrl', lambda m: m.endswith('.mar') and '.partial.' in m),
|
||||
('codeCoverageURL', lambda m: m.endswith('code-coverage-gcno.zip')),
|
||||
('sdkUrl', lambda m: m.endswith(('sdk.tar.bz2', 'sdk.zip'))),
|
||||
# packageUrl must be last!
|
||||
('packageUrl', lambda m: True),
|
||||
]
|
||||
|
|
|
@ -8,6 +8,7 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then
|
|||
# Some nightlies (eg: Mulet) don't want these set.
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
|
||||
MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
|
||||
fi
|
||||
. "$topsrcdir/build/mozconfig.common"
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then
|
|||
# Some nightlies (eg: Mulet) don't want these set.
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
|
||||
MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
|
||||
fi
|
||||
|
||||
# Some builds (eg: Mulet) don't want the installer, so only set this if it
|
||||
|
|
|
@ -2,6 +2,7 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then
|
|||
# Some nightlies (eg: Mulet) don't want these set.
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
|
||||
MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
|
||||
fi
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common"
|
||||
|
|
|
@ -3629,7 +3629,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
|
|||
_USE_SYSTEM_NSS=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSS"; then
|
||||
AM_PATH_NSS(3.17.4, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
AM_PATH_NSS(3.18, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSS"; then
|
||||
|
|
|
@ -859,6 +859,7 @@ nsDocShell::nsDocShell()
|
|||
, mAllowDNSPrefetch(true)
|
||||
, mAllowWindowControl(true)
|
||||
, mAllowContentRetargeting(true)
|
||||
, mAllowContentRetargetingOnChildren(true)
|
||||
, mCreatingDocument(false)
|
||||
, mUseErrorPages(false)
|
||||
, mObserveErrorPages(true)
|
||||
|
@ -2587,10 +2588,25 @@ nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting)
|
|||
NS_IMETHODIMP
|
||||
nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting)
|
||||
{
|
||||
mAllowContentRetargetingOnChildren = aAllowContentRetargeting;
|
||||
mAllowContentRetargeting = aAllowContentRetargeting;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetAllowContentRetargetingOnChildren(bool* aAllowContentRetargetingOnChildren)
|
||||
{
|
||||
*aAllowContentRetargetingOnChildren = mAllowContentRetargetingOnChildren;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetAllowContentRetargetingOnChildren(bool aAllowContentRetargetingOnChildren)
|
||||
{
|
||||
mAllowContentRetargetingOnChildren = aAllowContentRetargetingOnChildren;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetFullscreenAllowed(bool* aFullscreenAllowed)
|
||||
{
|
||||
|
@ -3461,7 +3477,7 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
|
|||
if (NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
|
||||
SetAllowWindowControl(value);
|
||||
}
|
||||
SetAllowContentRetargeting(parentAsDocShell->GetAllowContentRetargeting());
|
||||
SetAllowContentRetargeting(parentAsDocShell->GetAllowContentRetargetingOnChildren());
|
||||
if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
|
||||
SetIsActive(value);
|
||||
}
|
||||
|
@ -8738,6 +8754,7 @@ nsDocShell::RestoreFromHistory()
|
|||
childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
|
||||
|
||||
bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
|
||||
bool allowContentRetargetingOnChildren = childShell->GetAllowContentRetargetingOnChildren();
|
||||
|
||||
uint32_t defaultLoadFlags;
|
||||
childShell->GetDefaultLoadFlags(&defaultLoadFlags);
|
||||
|
@ -8756,6 +8773,7 @@ nsDocShell::RestoreFromHistory()
|
|||
childShell->SetAllowMedia(allowMedia);
|
||||
childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
|
||||
childShell->SetAllowContentRetargeting(allowContentRetargeting);
|
||||
childShell->SetAllowContentRetargetingOnChildren(allowContentRetargetingOnChildren);
|
||||
childShell->SetDefaultLoadFlags(defaultLoadFlags);
|
||||
|
||||
rv = childShell->BeginRestore(nullptr, false);
|
||||
|
|
|
@ -883,6 +883,7 @@ protected:
|
|||
bool mAllowDNSPrefetch;
|
||||
bool mAllowWindowControl;
|
||||
bool mAllowContentRetargeting;
|
||||
bool mAllowContentRetargetingOnChildren;
|
||||
bool mCreatingDocument; // (should be) debugging only
|
||||
bool mUseErrorPages;
|
||||
bool mObserveErrorPages;
|
||||
|
|
|
@ -54,7 +54,7 @@ interface nsITabParent;
|
|||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(f84b1ae4-2f78-4bad-b36a-6a8516ee6e40)]
|
||||
[scriptable, builtinclass, uuid(68ba7610-e33d-47ce-8fa2-af07af2422bc)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
|
@ -289,6 +289,12 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
*/
|
||||
[infallible] attribute boolean allowContentRetargeting;
|
||||
|
||||
/**
|
||||
* True if new child docshells should allow content retargeting.
|
||||
* Setting allowContentRetargeting also overwrites this value.
|
||||
*/
|
||||
[infallible] attribute boolean allowContentRetargetingOnChildren;
|
||||
|
||||
/**
|
||||
* Get an enumerator over this docShell and its children.
|
||||
*
|
||||
|
|
|
@ -252,16 +252,16 @@ Animation::IsInEffect() const
|
|||
return computedTiming.mTimeFraction != ComputedTiming::kNullTimeFraction;
|
||||
}
|
||||
|
||||
bool
|
||||
Animation::HasAnimationOfProperty(nsCSSProperty aProperty) const
|
||||
const AnimationProperty*
|
||||
Animation::GetAnimationOfProperty(nsCSSProperty aProperty) const
|
||||
{
|
||||
for (size_t propIdx = 0, propEnd = mProperties.Length();
|
||||
propIdx != propEnd; ++propIdx) {
|
||||
if (aProperty == mProperties[propIdx].mProperty) {
|
||||
return true;
|
||||
return &mProperties[propIdx];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -297,6 +297,16 @@ Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!prop.mWinsInCascade) {
|
||||
// This isn't the winning declaration, so don't add it to style.
|
||||
// For transitions, this is important, because it's how we
|
||||
// implement the rule that CSS transitions don't run when a CSS
|
||||
// animation is running on the same property and element. For
|
||||
// animations, this is only skipping things that will otherwise be
|
||||
// overridden.
|
||||
continue;
|
||||
}
|
||||
|
||||
aSetProperties.AddProperty(prop.mProperty);
|
||||
|
||||
MOZ_ASSERT(prop.mSegments.Length() > 0,
|
||||
|
|
|
@ -150,10 +150,25 @@ struct AnimationPropertySegment
|
|||
struct AnimationProperty
|
||||
{
|
||||
nsCSSProperty mProperty;
|
||||
|
||||
// Does this property win in the CSS Cascade?
|
||||
//
|
||||
// For CSS transitions, this is true as long as a CSS animation on the
|
||||
// same property and element is not running, in which case we set this
|
||||
// to false so that the animation (lower in the cascade) can win. We
|
||||
// then use this to decide whether to apply the style both in the CSS
|
||||
// cascade and for OMTA.
|
||||
//
|
||||
// FIXME (bug 847287): For CSS Animations, which are overridden by
|
||||
// !important rules in the cascade, we actually determine this from
|
||||
// the CSS cascade computations, and then use it for OMTA.
|
||||
bool mWinsInCascade;
|
||||
|
||||
InfallibleTArray<AnimationPropertySegment> mSegments;
|
||||
|
||||
bool operator==(const AnimationProperty& aOther) const {
|
||||
return mProperty == aOther.mProperty &&
|
||||
mWinsInCascade == aOther.mWinsInCascade &&
|
||||
mSegments == aOther.mSegments;
|
||||
}
|
||||
bool operator!=(const AnimationProperty& aOther) const {
|
||||
|
@ -290,7 +305,11 @@ public:
|
|||
bool IsCurrent() const;
|
||||
bool IsInEffect() const;
|
||||
|
||||
bool HasAnimationOfProperty(nsCSSProperty aProperty) const;
|
||||
const AnimationProperty*
|
||||
GetAnimationOfProperty(nsCSSProperty aProperty) const;
|
||||
bool HasAnimationOfProperty(nsCSSProperty aProperty) const {
|
||||
return GetAnimationOfProperty(aProperty) != nullptr;
|
||||
}
|
||||
const InfallibleTArray<AnimationProperty>& Properties() const {
|
||||
return mProperties;
|
||||
}
|
||||
|
|
|
@ -54,10 +54,9 @@ AnimationPlayer::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
|
|||
Nullable<TimeDuration> previousCurrentTime = GetCurrentTime();
|
||||
mStartTime = aNewStartTime;
|
||||
if (!aNewStartTime.IsNull()) {
|
||||
// Until bug 1127380 (playbackRate) is implemented, the rate is essentially
|
||||
// one. Once that bug is fixed we should only SetNull() if the rate is not
|
||||
// zero.
|
||||
mHoldTime.SetNull();
|
||||
if (mPlaybackRate != 0.0) {
|
||||
mHoldTime.SetNull();
|
||||
}
|
||||
} else {
|
||||
mHoldTime = previousCurrentTime;
|
||||
}
|
||||
|
@ -89,7 +88,8 @@ AnimationPlayer::GetCurrentTime() const
|
|||
if (!mStartTime.IsNull()) {
|
||||
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
|
||||
if (!timelineTime.IsNull()) {
|
||||
result.SetValue(timelineTime.Value() - mStartTime.Value());
|
||||
result.SetValue((timelineTime.Value() - mStartTime.Value())
|
||||
.MultDouble(mPlaybackRate));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -101,15 +101,16 @@ AnimationPlayer::SilentlySetCurrentTime(const TimeDuration& aSeekTime)
|
|||
{
|
||||
if (!mHoldTime.IsNull() ||
|
||||
!mTimeline ||
|
||||
mTimeline->GetCurrentTime().IsNull()
|
||||
/*or, once supported, playback rate is 0, or have pending pause task*/) {
|
||||
mTimeline->GetCurrentTime().IsNull() ||
|
||||
mPlaybackRate == 0.0
|
||||
/*or, once supported, if we have a pending pause task*/) {
|
||||
mHoldTime.SetValue(aSeekTime);
|
||||
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
|
||||
mStartTime.SetNull();
|
||||
}
|
||||
} else {
|
||||
// once playback rate is supported, need to account for that here
|
||||
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() - aSeekTime);
|
||||
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
|
||||
(aSeekTime / mPlaybackRate));
|
||||
}
|
||||
|
||||
// Once AnimationPlayers store a previous current time, set that to
|
||||
|
@ -132,6 +133,30 @@ AnimationPlayer::SetCurrentTime(const TimeDuration& aSeekTime)
|
|||
// http://w3c.github.io/web-animations/#update-a-players-finished-state
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::SetPlaybackRate(double aPlaybackRate)
|
||||
{
|
||||
Nullable<TimeDuration> previousTime = GetCurrentTime();
|
||||
mPlaybackRate = aPlaybackRate;
|
||||
if (!previousTime.IsNull()) {
|
||||
ErrorResult rv;
|
||||
SetCurrentTime(previousTime.Value());
|
||||
MOZ_ASSERT(!rv.Failed(), "Should not assert for non-null time");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::SilentlySetPlaybackRate(double aPlaybackRate)
|
||||
{
|
||||
Nullable<TimeDuration> previousTime = GetCurrentTime();
|
||||
mPlaybackRate = aPlaybackRate;
|
||||
if (!previousTime.IsNull()) {
|
||||
ErrorResult rv;
|
||||
SilentlySetCurrentTime(previousTime.Value());
|
||||
MOZ_ASSERT(!rv.Failed(), "Should not assert for non-null time");
|
||||
}
|
||||
}
|
||||
|
||||
AnimationPlayState
|
||||
AnimationPlayer::PlayState() const
|
||||
{
|
||||
|
@ -148,7 +173,8 @@ AnimationPlayer::PlayState() const
|
|||
return AnimationPlayState::Paused;
|
||||
}
|
||||
|
||||
if (currentTime.Value() >= SourceContentEnd()) {
|
||||
if ((mPlaybackRate > 0.0 && currentTime.Value() >= SourceContentEnd()) ||
|
||||
(mPlaybackRate < 0.0 && currentTime.Value().ToMilliseconds() <= 0.0)) {
|
||||
return AnimationPlayState::Finished;
|
||||
}
|
||||
|
||||
|
@ -399,10 +425,17 @@ AnimationPlayer::DoPlay()
|
|||
// animation-play-state we *don't* trigger finishing behavior.
|
||||
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
if (currentTime.IsNull()) {
|
||||
if (mPlaybackRate > 0.0 &&
|
||||
(currentTime.IsNull())) {
|
||||
mHoldTime.SetValue(TimeDuration(0));
|
||||
} else if (mHoldTime.IsNull()) {
|
||||
// If the hold time is null, we are already playing normally
|
||||
} else if (mPlaybackRate < 0.0 &&
|
||||
(currentTime.IsNull())) {
|
||||
mHoldTime.SetValue(TimeDuration(SourceContentEnd()));
|
||||
} else if (mPlaybackRate == 0.0 && currentTime.IsNull()) {
|
||||
mHoldTime.SetValue(TimeDuration(0));
|
||||
}
|
||||
|
||||
if (mHoldTime.IsNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -460,8 +493,12 @@ AnimationPlayer::ResumeAt(const TimeDuration& aResumeTime)
|
|||
MOZ_ASSERT(!mHoldTime.IsNull(),
|
||||
"A player in the pending state should have a resolved hold time");
|
||||
|
||||
mStartTime.SetValue(aResumeTime - mHoldTime.Value());
|
||||
mHoldTime.SetNull();
|
||||
if (mPlaybackRate != 0) {
|
||||
mStartTime.SetValue(aResumeTime - (mHoldTime.Value() / mPlaybackRate));
|
||||
mHoldTime.SetNull();
|
||||
} else {
|
||||
mStartTime.SetValue(aResumeTime);
|
||||
}
|
||||
mIsPending = false;
|
||||
|
||||
UpdateSourceContent();
|
||||
|
|
|
@ -53,6 +53,7 @@ protected:
|
|||
public:
|
||||
explicit AnimationPlayer(AnimationTimeline* aTimeline)
|
||||
: mTimeline(aTimeline)
|
||||
, mPlaybackRate(1.0)
|
||||
, mIsPending(false)
|
||||
, mIsRunningOnCompositor(false)
|
||||
, mIsPreviousStateFinished(false)
|
||||
|
@ -77,6 +78,9 @@ public:
|
|||
Nullable<TimeDuration> GetCurrentTime() const;
|
||||
void SilentlySetCurrentTime(const TimeDuration& aNewCurrentTime);
|
||||
void SetCurrentTime(const TimeDuration& aNewCurrentTime);
|
||||
double PlaybackRate() const { return mPlaybackRate; }
|
||||
void SetPlaybackRate(double aPlaybackRate);
|
||||
void SilentlySetPlaybackRate(double aPlaybackRate);
|
||||
AnimationPlayState PlayState() const;
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
virtual void Play();
|
||||
|
@ -241,6 +245,7 @@ protected:
|
|||
Nullable<TimeDuration> mStartTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mHoldTime; // Player timescale
|
||||
Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
|
||||
double mPlaybackRate;
|
||||
|
||||
// A Promise that is replaced on each call to Play() (and in future Pause())
|
||||
// and fulfilled when Play() is successfully completed.
|
||||
|
|
|
@ -408,28 +408,43 @@ Element::GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult)
|
|||
JSObject*
|
||||
Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, aGivenProto));
|
||||
JS::Rooted<JSObject*> givenProto(aCx, aGivenProto);
|
||||
JS::Rooted<JSObject*> customProto(aCx);
|
||||
|
||||
if (!givenProto) {
|
||||
// Custom element prototype swizzling.
|
||||
CustomElementData* data = GetCustomElementData();
|
||||
if (data) {
|
||||
// If this is a registered custom element then fix the prototype.
|
||||
nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
|
||||
document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &customProto);
|
||||
if (customProto &&
|
||||
NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(customProto))) {
|
||||
// Just go ahead and create with the right proto up front. Set
|
||||
// customProto to null to flag that we don't need to do any post-facto
|
||||
// proto fixups here.
|
||||
givenProto = customProto;
|
||||
customProto = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, givenProto));
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Custom element prototype swizzling.
|
||||
CustomElementData* data = GetCustomElementData();
|
||||
if (obj && data) {
|
||||
// If this is a registered custom element then fix the prototype.
|
||||
nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
|
||||
JS::Rooted<JSObject*> prototype(aCx);
|
||||
document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &prototype);
|
||||
if (prototype) {
|
||||
// We want to set the custom prototype in the compartment where it was
|
||||
// registered. In the case that |obj| and |prototype| are in different
|
||||
// compartments, this will set the prototype on the |obj|'s wrapper and
|
||||
// thus only visible in the wrapper's compartment.
|
||||
JSAutoCompartment ac(aCx, prototype);
|
||||
if (!JS_WrapObject(aCx, &obj) || !JS_SetPrototype(aCx, obj, prototype)) {
|
||||
dom::Throw(aCx, NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
if (customProto) {
|
||||
// We want to set the custom prototype in the compartment where it was
|
||||
// registered. In the case that |obj| and |prototype| are in different
|
||||
// compartments, this will set the prototype on the |obj|'s wrapper and
|
||||
// thus only visible in the wrapper's compartment, since we know obj's
|
||||
// principal does not subsume customProto's in this case.
|
||||
JSAutoCompartment ac(aCx, customProto);
|
||||
JS::Rooted<JSObject*> wrappedObj(aCx, obj);
|
||||
if (!JS_WrapObject(aCx, &wrappedObj) ||
|
||||
!JS_SetPrototype(aCx, wrappedObj, customProto)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1982,8 +1982,8 @@ Navigator::OnNavigation()
|
|||
}
|
||||
|
||||
#ifdef MOZ_MEDIA_NAVIGATOR
|
||||
// Inform MediaManager in case there are live streams or pending callbacks.
|
||||
MediaManager *manager = MediaManager::Get();
|
||||
// If MediaManager is open let it inform any live streams or pending callbacks
|
||||
MediaManager *manager = MediaManager::GetIfExists();
|
||||
if (manager) {
|
||||
manager->OnNavigation(mWindow->WindowID());
|
||||
}
|
||||
|
|
|
@ -1557,7 +1557,7 @@ nsMessageManagerScriptExecutor::LoadScriptInternal(const nsAString& aURL,
|
|||
JSContext* cx = aes.cx();
|
||||
if (script) {
|
||||
if (aRunInGlobalScope) {
|
||||
JS::CloneAndExecuteScript(cx, global, script);
|
||||
JS::CloneAndExecuteScript(cx, script);
|
||||
} else {
|
||||
JS::Rooted<JSObject*> scope(cx);
|
||||
bool ok = js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope);
|
||||
|
@ -1644,8 +1644,9 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
// We can't clone compile-and-go scripts.
|
||||
options.setCompileAndGo(false);
|
||||
// We're going to run these against some non-global scope.
|
||||
options.setCompileAndGo(false)
|
||||
.setHasPollutedScope(true);
|
||||
if (!JS::Compile(cx, options, srcBuf, &script)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -9,13 +9,22 @@
|
|||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/dom/cache/ActorUtils.h"
|
||||
#include "mozilla/dom/cache/PCacheTypes.h"
|
||||
#include "mozilla/dom/cache/ReadStream.h"
|
||||
#include "mozilla/ipc/FileDescriptorSetChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/PFileDescriptorSetChild.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
using mozilla::ipc::FileDescriptorSetChild;
|
||||
using mozilla::ipc::OptionalFileDescriptorSet;
|
||||
using mozilla::ipc::PFileDescriptorSetChild;
|
||||
|
||||
// declared in ActorUtils.h
|
||||
PCacheStreamControlChild*
|
||||
AllocPCacheStreamControlChild()
|
||||
|
@ -42,30 +51,6 @@ CacheStreamControlChild::~CacheStreamControlChild()
|
|||
MOZ_COUNT_DTOR(cache::CacheStreamControlChild);
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::AddListener(ReadStream* aListener)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
MOZ_ASSERT(aListener);
|
||||
MOZ_ASSERT(!mListeners.Contains(aListener));
|
||||
mListeners.AppendElement(aListener);
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::RemoveListener(ReadStream* aListener)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
MOZ_ASSERT(aListener);
|
||||
MOZ_ALWAYS_TRUE(mListeners.RemoveElement(aListener));
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::NoteClosed(const nsID& aId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
unused << SendNoteClosed(aId);
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::StartDestroy()
|
||||
{
|
||||
|
@ -83,19 +68,73 @@ CacheStreamControlChild::StartDestroy()
|
|||
RecvCloseAll();
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::SerializeControl(PCacheReadStream* aReadStreamOut)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
aReadStreamOut->controlParent() = nullptr;
|
||||
aReadStreamOut->controlChild() = this;
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<FileDescriptor>& aFds)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
PFileDescriptorSetChild* fdSet = nullptr;
|
||||
if (!aFds.IsEmpty()) {
|
||||
fdSet = Manager()->SendPFileDescriptorSetConstructor(aFds[0]);
|
||||
for (uint32_t i = 1; i < aFds.Length(); ++i) {
|
||||
unused << fdSet->SendAddFileDescriptor(aFds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (fdSet) {
|
||||
aReadStreamOut->fds() = fdSet;
|
||||
} else {
|
||||
aReadStreamOut->fds() = void_t();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::DeserializeFds(const PCacheReadStream& aReadStream,
|
||||
nsTArray<FileDescriptor>& aFdsOut)
|
||||
{
|
||||
if (aReadStream.fds().type() !=
|
||||
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto fdSetActor = static_cast<FileDescriptorSetChild*>(
|
||||
aReadStream.fds().get_PFileDescriptorSetChild());
|
||||
MOZ_ASSERT(fdSetActor);
|
||||
|
||||
fdSetActor->ForgetFileDescriptors(aFdsOut);
|
||||
MOZ_ASSERT(!aFdsOut.IsEmpty());
|
||||
|
||||
unused << fdSetActor->Send__delete__(fdSetActor);
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
unused << SendNoteClosed(aId);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
CacheStreamControlChild::AssertOwningThread()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CacheStreamControlChild::ActorDestroy(ActorDestroyReason aReason)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
// Note, we cannot trigger IPC traffic here. So use
|
||||
// CloseStreamWithoutReporting().
|
||||
ReadStreamList::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream> stream = iter.GetNext();
|
||||
stream->CloseStreamWithoutReporting();
|
||||
}
|
||||
mListeners.Clear();
|
||||
|
||||
CloseAllReadStreamsWithoutReporting();
|
||||
RemoveFeature();
|
||||
}
|
||||
|
||||
|
@ -103,20 +142,7 @@ bool
|
|||
CacheStreamControlChild::RecvClose(const nsID& aId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
DebugOnly<uint32_t> closedCount = 0;
|
||||
|
||||
ReadStreamList::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream> stream = iter.GetNext();
|
||||
// note, multiple streams may exist for same ID
|
||||
if (stream->MatchId(aId)) {
|
||||
stream->CloseStream();
|
||||
closedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(closedCount > 0);
|
||||
|
||||
CloseReadStreams(aId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -124,11 +150,7 @@ bool
|
|||
CacheStreamControlChild::RecvCloseAll()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
|
||||
ReadStreamList::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream> stream = iter.GetNext();
|
||||
stream->CloseStream();
|
||||
}
|
||||
CloseAllReadStreams();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/dom/cache/ActorChild.h"
|
||||
#include "mozilla/dom/cache/PCacheStreamControlChild.h"
|
||||
#include "mozilla/dom/cache/StreamControl.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -18,29 +19,42 @@ namespace cache {
|
|||
class ReadStream;
|
||||
|
||||
class CacheStreamControlChild MOZ_FINAL : public PCacheStreamControlChild
|
||||
, public StreamControl
|
||||
, public ActorChild
|
||||
{
|
||||
public:
|
||||
CacheStreamControlChild();
|
||||
~CacheStreamControlChild();
|
||||
|
||||
void AddListener(ReadStream* aListener);
|
||||
void RemoveListener(ReadStream* aListener);
|
||||
|
||||
void NoteClosed(const nsID& aId);
|
||||
|
||||
// ActorChild methods
|
||||
virtual void StartDestroy() MOZ_OVERRIDE;
|
||||
|
||||
// StreamControl methods
|
||||
virtual void
|
||||
SerializeControl(PCacheReadStream* aReadStreamOut) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<mozilla::ipc::FileDescriptor>& aFds) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
DeserializeFds(const PCacheReadStream& aReadStream,
|
||||
nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual void
|
||||
NoteClosedAfterForget(const nsID& aId) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void
|
||||
AssertOwningThread() MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
// PCacheStreamControlChild methods
|
||||
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
|
||||
virtual bool RecvClose(const nsID& aId) MOZ_OVERRIDE;
|
||||
virtual bool RecvCloseAll() MOZ_OVERRIDE;
|
||||
|
||||
typedef nsTObserverArray<ReadStream*> ReadStreamList;
|
||||
ReadStreamList mListeners;
|
||||
|
||||
bool mDestroyStarted;
|
||||
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
|
|
@ -8,14 +8,23 @@
|
|||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/dom/cache/PCacheTypes.h"
|
||||
#include "mozilla/dom/cache/ReadStream.h"
|
||||
#include "mozilla/dom/cache/StreamList.h"
|
||||
#include "mozilla/ipc/FileDescriptorSetParent.h"
|
||||
#include "mozilla/ipc/PBackgroundParent.h"
|
||||
#include "mozilla/ipc/PFileDescriptorSetParent.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
using mozilla::ipc::FileDescriptorSetParent;
|
||||
using mozilla::ipc::OptionalFileDescriptorSet;
|
||||
using mozilla::ipc::PFileDescriptorSetParent;
|
||||
|
||||
// declared in ActorUtils.h
|
||||
void
|
||||
DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor)
|
||||
|
@ -36,33 +45,75 @@ CacheStreamControlParent::~CacheStreamControlParent()
|
|||
}
|
||||
|
||||
void
|
||||
CacheStreamControlParent::AddListener(ReadStream* aListener)
|
||||
CacheStreamControlParent::SerializeControl(PCacheReadStream* aReadStreamOut)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
MOZ_ASSERT(aListener);
|
||||
MOZ_ASSERT(!mListeners.Contains(aListener));
|
||||
mListeners.AppendElement(aListener);
|
||||
aReadStreamOut->controlChild() = nullptr;
|
||||
aReadStreamOut->controlParent() = this;
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlParent::RemoveListener(ReadStream* aListener)
|
||||
CacheStreamControlParent::SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<FileDescriptor>& aFds)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
MOZ_ASSERT(aListener);
|
||||
DebugOnly<bool> removed = mListeners.RemoveElement(aListener);
|
||||
MOZ_ASSERT(removed);
|
||||
PFileDescriptorSetParent* fdSet = nullptr;
|
||||
if (!aFds.IsEmpty()) {
|
||||
fdSet = Manager()->SendPFileDescriptorSetConstructor(aFds[0]);
|
||||
for (uint32_t i = 1; i < aFds.Length(); ++i) {
|
||||
unused << fdSet->SendAddFileDescriptor(aFds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (fdSet) {
|
||||
aReadStreamOut->fds() = fdSet;
|
||||
} else {
|
||||
aReadStreamOut->fds() = void_t();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlParent::DeserializeFds(const PCacheReadStream& aReadStream,
|
||||
nsTArray<FileDescriptor>& aFdsOut)
|
||||
{
|
||||
if (aReadStream.fds().type() !=
|
||||
OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileDescriptorSetParent* fdSetActor =
|
||||
static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
|
||||
MOZ_ASSERT(fdSetActor);
|
||||
|
||||
fdSetActor->ForgetFileDescriptors(aFdsOut);
|
||||
MOZ_ASSERT(!aFdsOut.IsEmpty());
|
||||
|
||||
if (!fdSetActor->Send__delete__(fdSetActor)) {
|
||||
// child process is gone, warn and allow actor to clean up normally
|
||||
NS_WARNING("Cache failed to delete fd set actor.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlParent::NoteClosedAfterForget(const nsID& aId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
RecvNoteClosed(aId);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
CacheStreamControlParent::AssertOwningThread()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CacheStreamControlParent::ActorDestroy(ActorDestroyReason aReason)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
MOZ_ASSERT(mStreamList);
|
||||
ReadStreamList::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream> stream = iter.GetNext();
|
||||
stream->CloseStreamWithoutReporting();
|
||||
}
|
||||
CloseAllReadStreamsWithoutReporting();
|
||||
mStreamList->RemoveStreamControl(this);
|
||||
mStreamList->NoteClosedAll();
|
||||
mStreamList = nullptr;
|
||||
|
@ -116,30 +167,14 @@ void
|
|||
CacheStreamControlParent::NotifyClose(const nsID& aId)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
DebugOnly<uint32_t> closedCount = 0;
|
||||
|
||||
ReadStreamList::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream> stream = iter.GetNext();
|
||||
// note, multiple streams may exist for same ID
|
||||
if (stream->MatchId(aId)) {
|
||||
stream->CloseStream();
|
||||
closedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(closedCount > 0);
|
||||
CloseReadStreams(aId);
|
||||
}
|
||||
|
||||
void
|
||||
CacheStreamControlParent::NotifyCloseAll()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
|
||||
ReadStreamList::ForwardIterator iter(mListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream> stream = iter.GetNext();
|
||||
stream->CloseStream();
|
||||
}
|
||||
CloseAllReadStreams();
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define mozilla_dom_cache_CacheStreamControlParent_h
|
||||
|
||||
#include "mozilla/dom/cache/PCacheStreamControlParent.h"
|
||||
#include "mozilla/dom/cache/StreamControl.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -18,24 +19,42 @@ class ReadStream;
|
|||
class StreamList;
|
||||
|
||||
class CacheStreamControlParent : public PCacheStreamControlParent
|
||||
, public StreamControl
|
||||
{
|
||||
public:
|
||||
CacheStreamControlParent();
|
||||
~CacheStreamControlParent();
|
||||
|
||||
void AddListener(ReadStream* aListener);
|
||||
void RemoveListener(ReadStream* aListener);
|
||||
|
||||
void SetStreamList(StreamList* aStreamList);
|
||||
void Close(const nsID& aId);
|
||||
void CloseAll();
|
||||
void Shutdown();
|
||||
|
||||
// StreamControl methods
|
||||
virtual void
|
||||
SerializeControl(PCacheReadStream* aReadStreamOut) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<mozilla::ipc::FileDescriptor>& aFds) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
DeserializeFds(const PCacheReadStream& aReadStream,
|
||||
nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual void
|
||||
NoteClosedAfterForget(const nsID& aId) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void
|
||||
AssertOwningThread() MOZ_OVERRIDE;
|
||||
#endif
|
||||
|
||||
// PCacheStreamControlParent methods
|
||||
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
|
||||
virtual bool RecvNoteClosed(const nsID& aId) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
void NotifyClose(const nsID& aId);
|
||||
void NotifyCloseAll();
|
||||
|
||||
|
@ -44,9 +63,6 @@ private:
|
|||
// StreamList::RemoveStreamControl() to clear the weak ref.
|
||||
nsRefPtr<StreamList> mStreamList;
|
||||
|
||||
typedef nsTObserverArray<ReadStream*> ReadStreamList;
|
||||
ReadStreamList mListeners;
|
||||
|
||||
NS_DECL_OWNINGTHREAD
|
||||
};
|
||||
|
||||
|
|
|
@ -9,234 +9,121 @@
|
|||
#include "mozilla/unused.h"
|
||||
#include "mozilla/dom/cache/CacheStreamControlChild.h"
|
||||
#include "mozilla/dom/cache/CacheStreamControlParent.h"
|
||||
#include "mozilla/dom/cache/PCacheStreamControlChild.h"
|
||||
#include "mozilla/dom/cache/PCacheStreamControlParent.h"
|
||||
#include "mozilla/dom/cache/PCacheTypes.h"
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
#include "mozilla/ipc/FileDescriptorSetChild.h"
|
||||
#include "mozilla/ipc/FileDescriptorSetParent.h"
|
||||
#include "mozilla/ipc/InputStreamParams.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundParent.h"
|
||||
#include "mozilla/ipc/PFileDescriptorSetChild.h"
|
||||
#include "mozilla/ipc/PFileDescriptorSetParent.h"
|
||||
#include "mozilla/SnappyUncompressInputStream.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using mozilla::unused;
|
||||
using mozilla::void_t;
|
||||
using mozilla::dom::cache::CacheStreamControlChild;
|
||||
using mozilla::dom::cache::CacheStreamControlParent;
|
||||
using mozilla::dom::cache::PCacheReadStream;
|
||||
using mozilla::dom::cache::PCacheStreamControlChild;
|
||||
using mozilla::dom::cache::PCacheStreamControlParent;
|
||||
using mozilla::dom::cache::ReadStream;
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
using mozilla::ipc::PFileDescriptorSetChild;
|
||||
using mozilla::ipc::PFileDescriptorSetParent;
|
||||
|
||||
// There are separate concrete implementations of ReadStream for the child
|
||||
// and parent processes. This is unfortunately necessary because the
|
||||
// actor types are distinct for these two cases. Also, the interface for
|
||||
// reporting the close event differs slightly for the child and parent
|
||||
// StreamControl actors.
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class ReadStreamChild MOZ_FINAL : public ReadStream
|
||||
{
|
||||
public:
|
||||
ReadStreamChild(PCacheStreamControlChild* aControl, const nsID& aId,
|
||||
nsIInputStream* aStream)
|
||||
: ReadStream(aId, aStream)
|
||||
, mControl(static_cast<CacheStreamControlChild*>(aControl))
|
||||
{
|
||||
MOZ_ASSERT(mControl);
|
||||
mControl->AddListener(this);
|
||||
}
|
||||
|
||||
virtual ~ReadStreamChild()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
NoteClosed();
|
||||
}
|
||||
|
||||
virtual void NoteClosedOnOwningThread() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
mControl->RemoveListener(this);
|
||||
mControl->NoteClosed(mId);
|
||||
}
|
||||
|
||||
virtual void ForgetOnOwningThread() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
mControl->RemoveListener(this);
|
||||
}
|
||||
|
||||
virtual void SerializeControl(PCacheReadStream* aReadStreamOut) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(aReadStreamOut);
|
||||
MOZ_ASSERT(!mClosed);
|
||||
aReadStreamOut->controlParent() = nullptr;
|
||||
aReadStreamOut->controlChild() = mControl;
|
||||
}
|
||||
|
||||
virtual void
|
||||
SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<FileDescriptor>& fds) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(!mClosed);
|
||||
PFileDescriptorSetChild* fdSet = nullptr;
|
||||
if (!fds.IsEmpty()) {
|
||||
fdSet = mControl->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
|
||||
for (uint32_t i = 1; i < fds.Length(); ++i) {
|
||||
unused << fdSet->SendAddFileDescriptor(fds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (fdSet) {
|
||||
aReadStreamOut->fds() = fdSet;
|
||||
} else {
|
||||
aReadStreamOut->fds() = void_t();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CacheStreamControlChild* mControl;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class ReadStreamParent MOZ_FINAL : public ReadStream
|
||||
{
|
||||
public:
|
||||
ReadStreamParent(PCacheStreamControlParent* aControl, const nsID& aId,
|
||||
nsIInputStream* aStream)
|
||||
: ReadStream(aId, aStream)
|
||||
, mControl(static_cast<CacheStreamControlParent*>(aControl))
|
||||
{
|
||||
MOZ_ASSERT(mControl);
|
||||
mControl->AddListener(this);
|
||||
}
|
||||
|
||||
virtual ~ReadStreamParent()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
NoteClosed();
|
||||
}
|
||||
|
||||
virtual void NoteClosedOnOwningThread() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
mControl->RemoveListener(this);
|
||||
// This can cause mControl to be destructed
|
||||
mControl->RecvNoteClosed(mId);
|
||||
mControl = nullptr;
|
||||
}
|
||||
|
||||
virtual void ForgetOnOwningThread() MOZ_OVERRIDE
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
// This can cause mControl to be destroyed
|
||||
mControl->RemoveListener(this);
|
||||
mControl = nullptr;
|
||||
}
|
||||
|
||||
virtual void SerializeControl(PCacheReadStream* aReadStreamOut) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(aReadStreamOut);
|
||||
MOZ_ASSERT(!mClosed);
|
||||
MOZ_ASSERT(mControl);
|
||||
aReadStreamOut->controlChild() = nullptr;
|
||||
aReadStreamOut->controlParent() = mControl;
|
||||
}
|
||||
|
||||
virtual void
|
||||
SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<FileDescriptor>& fds) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(!mClosed);
|
||||
MOZ_ASSERT(mControl);
|
||||
PFileDescriptorSetParent* fdSet = nullptr;
|
||||
if (!fds.IsEmpty()) {
|
||||
fdSet = mControl->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
|
||||
for (uint32_t i = 1; i < fds.Length(); ++i) {
|
||||
unused << fdSet->SendAddFileDescriptor(fds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (fdSet) {
|
||||
aReadStreamOut->fds() = fdSet;
|
||||
} else {
|
||||
aReadStreamOut->fds() = void_t();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CacheStreamControlParent* mControl;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
using mozilla::unused;
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
using mozilla::ipc::FileDescriptorSetChild;
|
||||
using mozilla::ipc::FileDescriptorSetParent;
|
||||
using mozilla::ipc::InputStreamParams;
|
||||
using mozilla::ipc::OptionalFileDescriptorSet;
|
||||
using mozilla::ipc::PFileDescriptorSetChild;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// The inner stream class. This is where all of the real work is done. As
|
||||
// an invariant Inner::Close() must be called before ~Inner(). This is
|
||||
// guaranteed by our outer ReadStream class.
|
||||
class ReadStream::Inner MOZ_FINAL : public ReadStream::Controllable
|
||||
{
|
||||
public:
|
||||
Inner(StreamControl* aControl, const nsID& aId,
|
||||
nsIInputStream* aStream);
|
||||
|
||||
void
|
||||
Serialize(PCacheReadStreamOrVoid* aReadStreamOut);
|
||||
|
||||
void
|
||||
Serialize(PCacheReadStream* aReadStreamOut);
|
||||
|
||||
// ReadStream::Controllable methods
|
||||
virtual void
|
||||
CloseStream() MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
CloseStreamWithoutReporting() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
MatchId(const nsID& aId) const MOZ_OVERRIDE;
|
||||
|
||||
// Simulate nsIInputStream methods, but we don't actually inherit from it
|
||||
NS_METHOD
|
||||
Close();
|
||||
|
||||
NS_METHOD
|
||||
Available(uint64_t *aNumAvailableOut);
|
||||
|
||||
NS_METHOD
|
||||
Read(char *aBuf, uint32_t aCount, uint32_t *aNumReadOut);
|
||||
|
||||
NS_METHOD
|
||||
ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, uint32_t aCount,
|
||||
uint32_t *aNumReadOut);
|
||||
|
||||
NS_METHOD
|
||||
IsNonBlocking(bool *aNonBlockingOut);
|
||||
|
||||
private:
|
||||
class NoteClosedRunnable;
|
||||
class ForgetRunnable;
|
||||
|
||||
~Inner();
|
||||
|
||||
void
|
||||
NoteClosed();
|
||||
|
||||
void
|
||||
Forget();
|
||||
|
||||
void
|
||||
NoteClosedOnOwningThread();
|
||||
|
||||
void
|
||||
ForgetOnOwningThread();
|
||||
|
||||
// Weak ref to the stream control actor. The actor will always call either
|
||||
// CloseStream() or CloseStreamWithoutReporting() before it's destroyed. The
|
||||
// weak ref is cleared in the resulting NoteClosedOnOwningThread() or
|
||||
// ForgetOnOwningThread() method call.
|
||||
StreamControl* mControl;
|
||||
|
||||
const nsID mId;
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
nsCOMPtr<nsIInputStream> mSnappyStream;
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
|
||||
enum State
|
||||
{
|
||||
Open,
|
||||
Closed,
|
||||
NumStates
|
||||
};
|
||||
Atomic<State> mState;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::ReadStream::Inner, MOZ_OVERRIDE)
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Runnable to notify actors that the ReadStream has closed. This must
|
||||
// be done on the thread associated with the PBackground actor. Must be
|
||||
// cancelable to execute on Worker threads (which can occur when the
|
||||
// ReadStream is constructed on a child process Worker thread).
|
||||
class ReadStream::NoteClosedRunnable MOZ_FINAL : public nsCancelableRunnable
|
||||
class ReadStream::Inner::NoteClosedRunnable MOZ_FINAL : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit NoteClosedRunnable(ReadStream* aStream)
|
||||
explicit NoteClosedRunnable(ReadStream::Inner* aStream)
|
||||
: mStream(aStream)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
mStream->NoteClosedOnOwningThread();
|
||||
mStream = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -251,24 +138,27 @@ public:
|
|||
private:
|
||||
~NoteClosedRunnable() { }
|
||||
|
||||
nsRefPtr<ReadStream> mStream;
|
||||
nsRefPtr<ReadStream::Inner> mStream;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Runnable to clear actors without reporting that the ReadStream has
|
||||
// closed. Since this can trigger actor destruction, we need to do
|
||||
// it on the thread associated with the PBackground actor. Must be
|
||||
// cancelable to execute on Worker threads (which can occur when the
|
||||
// ReadStream is constructed on a child process Worker thread).
|
||||
class ReadStream::ForgetRunnable MOZ_FINAL : public nsCancelableRunnable
|
||||
class ReadStream::Inner::ForgetRunnable MOZ_FINAL : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit ForgetRunnable(ReadStream* aStream)
|
||||
explicit ForgetRunnable(ReadStream::Inner* aStream)
|
||||
: mStream(aStream)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
mStream->ForgetOnOwningThread();
|
||||
mStream = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -283,11 +173,216 @@ public:
|
|||
private:
|
||||
~ForgetRunnable() { }
|
||||
|
||||
nsRefPtr<ReadStream> mStream;
|
||||
nsRefPtr<ReadStream::Inner> mStream;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(mozilla::dom::cache::ReadStream, nsIInputStream,
|
||||
ReadStream);
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
ReadStream::Inner::Inner(StreamControl* aControl, const nsID& aId,
|
||||
nsIInputStream* aStream)
|
||||
: mControl(aControl)
|
||||
, mId(aId)
|
||||
, mStream(aStream)
|
||||
, mSnappyStream(new SnappyUncompressInputStream(aStream))
|
||||
, mOwningThread(NS_GetCurrentThread())
|
||||
, mState(Open)
|
||||
{
|
||||
MOZ_ASSERT(mStream);
|
||||
MOZ_ASSERT(mControl);
|
||||
mControl->AddReadStream(this);
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::Serialize(PCacheReadStreamOrVoid* aReadStreamOut)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
MOZ_ASSERT(aReadStreamOut);
|
||||
PCacheReadStream stream;
|
||||
Serialize(&stream);
|
||||
*aReadStreamOut = stream;
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::Serialize(PCacheReadStream* aReadStreamOut)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
MOZ_ASSERT(aReadStreamOut);
|
||||
MOZ_ASSERT(mState == Open);
|
||||
MOZ_ASSERT(mControl);
|
||||
|
||||
aReadStreamOut->id() = mId;
|
||||
mControl->SerializeControl(aReadStreamOut);
|
||||
|
||||
nsAutoTArray<FileDescriptor, 4> fds;
|
||||
SerializeInputStream(mStream, aReadStreamOut->params(), fds);
|
||||
|
||||
mControl->SerializeFds(aReadStreamOut, fds);
|
||||
|
||||
// We're passing ownership across the IPC barrier with the control, so
|
||||
// do not signal that the stream is closed here.
|
||||
Forget();
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::CloseStream()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
Close();
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::CloseStreamWithoutReporting()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
Forget();
|
||||
}
|
||||
|
||||
bool
|
||||
ReadStream::Inner::MatchId(const nsID& aId) const
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
return mId.Equals(aId);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Inner::Close()
|
||||
{
|
||||
// stream ops can happen on any thread
|
||||
nsresult rv = mStream->Close();
|
||||
NoteClosed();
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Inner::Available(uint64_t* aNumAvailableOut)
|
||||
{
|
||||
// stream ops can happen on any thread
|
||||
nsresult rv = mSnappyStream->Available(aNumAvailableOut);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Inner::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
|
||||
{
|
||||
// stream ops can happen on any thread
|
||||
MOZ_ASSERT(aNumReadOut);
|
||||
|
||||
nsresult rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
|
||||
|
||||
if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
|
||||
*aNumReadOut == 0) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Inner::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t* aNumReadOut)
|
||||
{
|
||||
// stream ops can happen on any thread
|
||||
MOZ_ASSERT(aNumReadOut);
|
||||
|
||||
nsresult rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount,
|
||||
aNumReadOut);
|
||||
|
||||
if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK &&
|
||||
rv != NS_ERROR_NOT_IMPLEMENTED) || *aNumReadOut == 0) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Inner::IsNonBlocking(bool* aNonBlockingOut)
|
||||
{
|
||||
// stream ops can happen on any thread
|
||||
return mSnappyStream->IsNonBlocking(aNonBlockingOut);
|
||||
}
|
||||
|
||||
ReadStream::Inner::~Inner()
|
||||
{
|
||||
// Any thread
|
||||
MOZ_ASSERT(mState == Closed);
|
||||
MOZ_ASSERT(!mControl);
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::NoteClosed()
|
||||
{
|
||||
// Any thread
|
||||
if (mState == Closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_GetCurrentThread() == mOwningThread) {
|
||||
NoteClosedOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new NoteClosedRunnable(this);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::Forget()
|
||||
{
|
||||
// Any thread
|
||||
if (mState == Closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_GetCurrentThread() == mOwningThread) {
|
||||
ForgetOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new ForgetRunnable(this);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::NoteClosedOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
|
||||
// Mark closed and do nothing if we were already closed
|
||||
if (!mState.compareExchange(Open, Closed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mControl);
|
||||
mControl->NoteClosed(this, mId);
|
||||
mControl = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Inner::ForgetOnOwningThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
|
||||
|
||||
// Mark closed and do nothing if we were already closed
|
||||
if (!mState.compareExchange(Open, Closed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mControl);
|
||||
mControl->ForgetReadStream(this);
|
||||
mControl = nullptr;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS(cache::ReadStream, nsIInputStream, ReadStream);
|
||||
|
||||
// static
|
||||
already_AddRefed<ReadStream>
|
||||
|
@ -311,33 +406,20 @@ ReadStream::Create(const PCacheReadStream& aReadStream)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoTArray<FileDescriptor, 4> fds;
|
||||
if (aReadStream.fds().type() ==
|
||||
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
|
||||
|
||||
FileDescriptorSetChild* fdSetActor =
|
||||
static_cast<FileDescriptorSetChild*>(aReadStream.fds().get_PFileDescriptorSetChild());
|
||||
MOZ_ASSERT(fdSetActor);
|
||||
|
||||
fdSetActor->ForgetFileDescriptors(fds);
|
||||
MOZ_ASSERT(!fds.IsEmpty());
|
||||
|
||||
unused << fdSetActor->Send__delete__(fdSetActor);
|
||||
} else if (aReadStream.fds().type() ==
|
||||
OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
|
||||
|
||||
FileDescriptorSetParent* fdSetActor =
|
||||
static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
|
||||
MOZ_ASSERT(fdSetActor);
|
||||
|
||||
fdSetActor->ForgetFileDescriptors(fds);
|
||||
MOZ_ASSERT(!fds.IsEmpty());
|
||||
|
||||
if (!fdSetActor->Send__delete__(fdSetActor)) {
|
||||
// child process is gone, warn and allow actor to clean up normally
|
||||
NS_WARNING("Cache failed to delete fd set actor.");
|
||||
}
|
||||
// Control is guaranteed to survive this method as ActorDestroy() cannot
|
||||
// run on this thread until we complete.
|
||||
StreamControl* control;
|
||||
if (aReadStream.controlChild()) {
|
||||
auto actor = static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
|
||||
control = actor;
|
||||
} else {
|
||||
auto actor = static_cast<CacheStreamControlParent*>(aReadStream.controlParent());
|
||||
control = actor;
|
||||
}
|
||||
MOZ_ASSERT(control);
|
||||
|
||||
nsAutoTArray<FileDescriptor, 4> fds;
|
||||
control->DeserializeFds(aReadStream, fds);
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream =
|
||||
DeserializeInputStream(aReadStream.params(), fds);
|
||||
|
@ -349,16 +431,8 @@ ReadStream::Create(const PCacheReadStream& aReadStream)
|
|||
MOZ_ASSERT(!asyncStream);
|
||||
#endif
|
||||
|
||||
nsRefPtr<ReadStream> ref;
|
||||
|
||||
if (aReadStream.controlChild()) {
|
||||
ref = new ReadStreamChild(aReadStream.controlChild(), aReadStream.id(),
|
||||
stream);
|
||||
} else {
|
||||
ref = new ReadStreamParent(aReadStream.controlParent(), aReadStream.id(),
|
||||
stream);
|
||||
}
|
||||
|
||||
nsRefPtr<Inner> inner = new Inner(control, aReadStream.id(), stream);
|
||||
nsRefPtr<ReadStream> ref = new ReadStream(inner);
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
|
@ -367,170 +441,67 @@ already_AddRefed<ReadStream>
|
|||
ReadStream::Create(PCacheStreamControlParent* aControl, const nsID& aId,
|
||||
nsIInputStream* aStream)
|
||||
{
|
||||
nsRefPtr<ReadStream> ref = new ReadStreamParent(aControl, aId, aStream);
|
||||
MOZ_ASSERT(aControl);
|
||||
auto actor = static_cast<CacheStreamControlParent*>(aControl);
|
||||
nsRefPtr<Inner> inner = new Inner(actor, aId, aStream);
|
||||
nsRefPtr<ReadStream> ref = new ReadStream(inner);
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Serialize(PCacheReadStreamOrVoid* aReadStreamOut)
|
||||
{
|
||||
MOZ_ASSERT(aReadStreamOut);
|
||||
PCacheReadStream stream;
|
||||
Serialize(&stream);
|
||||
*aReadStreamOut = stream;
|
||||
mInner->Serialize(aReadStreamOut);
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Serialize(PCacheReadStream* aReadStreamOut)
|
||||
{
|
||||
MOZ_ASSERT(aReadStreamOut);
|
||||
MOZ_ASSERT(!mClosed);
|
||||
|
||||
aReadStreamOut->id() = mId;
|
||||
SerializeControl(aReadStreamOut);
|
||||
|
||||
nsAutoTArray<FileDescriptor, 4> fds;
|
||||
SerializeInputStream(mStream, aReadStreamOut->params(), fds);
|
||||
|
||||
SerializeFds(aReadStreamOut, fds);
|
||||
|
||||
// We're passing ownership across the IPC barrier with the control, so
|
||||
// do not signal that the stream is closed here.
|
||||
Forget();
|
||||
mInner->Serialize(aReadStreamOut);
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::CloseStream()
|
||||
ReadStream::ReadStream(ReadStream::Inner* aInner)
|
||||
: mInner(aInner)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::CloseStreamWithoutReporting()
|
||||
{
|
||||
Forget();
|
||||
}
|
||||
|
||||
bool
|
||||
ReadStream::MatchId(const nsID& aId) const
|
||||
{
|
||||
return mId.Equals(aId);
|
||||
}
|
||||
|
||||
ReadStream::ReadStream(const nsID& aId, nsIInputStream* aStream)
|
||||
: mId(aId)
|
||||
, mStream(aStream)
|
||||
, mSnappyStream(new SnappyUncompressInputStream(aStream))
|
||||
, mOwningThread(NS_GetCurrentThread())
|
||||
, mClosed(false)
|
||||
{
|
||||
MOZ_ASSERT(mStream);
|
||||
MOZ_ASSERT(mInner);
|
||||
}
|
||||
|
||||
ReadStream::~ReadStream()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(ReadStream);
|
||||
|
||||
// We cannot directly call NoteClosed() here. The concrete subclasses
|
||||
// destructors must do this because it takes code paths through virtual
|
||||
// methods. We don't want to execute these while partially destroyed.
|
||||
MOZ_ASSERT(mClosed);
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::NoteClosed()
|
||||
{
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_GetCurrentThread() == mOwningThread) {
|
||||
NoteClosedOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new NoteClosedRunnable(this);
|
||||
nsresult rv = mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch Cache ReadStream NoteClosed() runnable.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ReadStream::Forget()
|
||||
{
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_GetCurrentThread() == mOwningThread) {
|
||||
ForgetOnOwningThread();
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new ForgetRunnable(this);
|
||||
nsresult rv = mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch Cache ReadStream Forget() runnable.");
|
||||
}
|
||||
// Explicitly close the inner stream so that it does not have to
|
||||
// deal with implicitly closing at destruction time.
|
||||
mInner->Close();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Close()
|
||||
{
|
||||
nsresult rv = mStream->Close();
|
||||
NoteClosed();
|
||||
return rv;
|
||||
return mInner->Close();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Available(uint64_t* aNumAvailableOut)
|
||||
{
|
||||
nsresult rv = mSnappyStream->Available(aNumAvailableOut);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return rv;
|
||||
return mInner->Available(aNumAvailableOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
|
||||
{
|
||||
MOZ_ASSERT(aNumReadOut);
|
||||
|
||||
nsresult rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
|
||||
|
||||
if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
|
||||
*aNumReadOut == 0) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return rv;
|
||||
return mInner->Read(aBuf, aCount, aNumReadOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
||||
uint32_t aCount, uint32_t* aNumReadOut)
|
||||
{
|
||||
MOZ_ASSERT(aNumReadOut);
|
||||
|
||||
nsresult rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount,
|
||||
aNumReadOut);
|
||||
|
||||
if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK &&
|
||||
rv != NS_ERROR_NOT_IMPLEMENTED) || *aNumReadOut == 0) {
|
||||
Close();
|
||||
}
|
||||
|
||||
return rv;
|
||||
return mInner->ReadSegments(aWriter, aClosure, aCount, aNumReadOut);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ReadStream::IsNonBlocking(bool* aNonBlockingOut)
|
||||
{
|
||||
return mSnappyStream->IsNonBlocking(aNonBlockingOut);
|
||||
return mInner->IsNonBlocking(aNonBlockingOut);
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsID.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
class nsIThread;
|
||||
|
@ -29,6 +30,7 @@ class PCacheStreamControlParent;
|
|||
{0x8e5da7c9, 0x0940, 0x4f1d, \
|
||||
{0x97, 0x25, 0x5c, 0x59, 0x38, 0xdd, 0xb9, 0x9f}}
|
||||
|
||||
|
||||
// Custom stream class for Request and Response bodies being read from
|
||||
// a Cache. The main purpose of this class is to report back to the
|
||||
// Cache's Manager when the stream is closed. This allows the Cache to
|
||||
|
@ -40,9 +42,34 @@ class PCacheStreamControlParent;
|
|||
// stream channel. For example, Cache.put() can detect that the content
|
||||
// script is passing a Cache-originated-stream back into the Cache
|
||||
// again. This enables certain optimizations.
|
||||
class ReadStream : public nsIInputStream
|
||||
class ReadStream MOZ_FINAL : public nsIInputStream
|
||||
{
|
||||
public:
|
||||
// Interface that lets the StreamControl classes interact with
|
||||
// our private inner stream.
|
||||
class Controllable
|
||||
{
|
||||
public:
|
||||
// Closes the stream, notifies the stream control, and then forgets
|
||||
// the stream control.
|
||||
virtual void
|
||||
CloseStream() = 0;
|
||||
|
||||
// Closes the stream and then forgets the stream control. Does not
|
||||
// notify.
|
||||
virtual void
|
||||
CloseStreamWithoutReporting() = 0;
|
||||
|
||||
virtual bool
|
||||
MatchId(const nsID& aId) const = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
AddRef(void) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
Release(void) = 0;
|
||||
};
|
||||
|
||||
static already_AddRefed<ReadStream>
|
||||
Create(const PCacheReadStreamOrVoid& aReadStreamOrVoid);
|
||||
|
||||
|
@ -56,39 +83,21 @@ public:
|
|||
void Serialize(PCacheReadStreamOrVoid* aReadStreamOut);
|
||||
void Serialize(PCacheReadStream* aReadStreamOut);
|
||||
|
||||
// methods called from the child and parent CacheStreamControl actors
|
||||
void CloseStream();
|
||||
void CloseStreamWithoutReporting();
|
||||
bool MatchId(const nsID& aId) const;
|
||||
private:
|
||||
class Inner;
|
||||
|
||||
protected:
|
||||
class NoteClosedRunnable;
|
||||
class ForgetRunnable;
|
||||
explicit ReadStream(Inner* aInner);
|
||||
~ReadStream();
|
||||
|
||||
ReadStream(const nsID& aId, nsIInputStream* aStream);
|
||||
virtual ~ReadStream();
|
||||
|
||||
void NoteClosed();
|
||||
void Forget();
|
||||
|
||||
virtual void NoteClosedOnOwningThread() = 0;
|
||||
virtual void ForgetOnOwningThread() = 0;
|
||||
virtual void SerializeControl(PCacheReadStream* aReadStreamOut) = 0;
|
||||
|
||||
virtual void
|
||||
SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<mozilla::ipc::FileDescriptor>& fds) = 0;
|
||||
|
||||
const nsID mId;
|
||||
nsCOMPtr<nsIInputStream> mStream;
|
||||
nsCOMPtr<nsIInputStream> mSnappyStream;
|
||||
nsCOMPtr<nsIThread> mOwningThread;
|
||||
bool mClosed;
|
||||
// Hold a strong ref to an inner class that actually implements the
|
||||
// majority of the stream logic. Before releasing this ref the outer
|
||||
// ReadStream guarantees it will call Close() on the inner stream.
|
||||
// This is essential for the inner stream to avoid dealing with the
|
||||
// implicit close that can happen when a stream is destroyed.
|
||||
nsRefPtr<Inner> mInner;
|
||||
|
||||
public:
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_CACHE_READSTREAM_IID);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
};
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- 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 "mozilla/dom/cache/StreamControl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
void
|
||||
StreamControl::AddReadStream(ReadStream::Controllable* aReadStream)
|
||||
{
|
||||
AssertOwningThread();
|
||||
MOZ_ASSERT(aReadStream);
|
||||
MOZ_ASSERT(!mReadStreamList.Contains(aReadStream));
|
||||
mReadStreamList.AppendElement(aReadStream);
|
||||
}
|
||||
|
||||
void
|
||||
StreamControl::ForgetReadStream(ReadStream::Controllable* aReadStream)
|
||||
{
|
||||
AssertOwningThread();
|
||||
MOZ_ALWAYS_TRUE(mReadStreamList.RemoveElement(aReadStream));
|
||||
}
|
||||
|
||||
void
|
||||
StreamControl::NoteClosed(ReadStream::Controllable* aReadStream,
|
||||
const nsID& aId)
|
||||
{
|
||||
AssertOwningThread();
|
||||
ForgetReadStream(aReadStream);
|
||||
NoteClosedAfterForget(aId);
|
||||
}
|
||||
|
||||
StreamControl::~StreamControl()
|
||||
{
|
||||
// owning thread only, but can't call virtual AssertOwningThread in destructor
|
||||
MOZ_ASSERT(mReadStreamList.IsEmpty());
|
||||
}
|
||||
|
||||
void
|
||||
StreamControl::CloseReadStreams(const nsID& aId)
|
||||
{
|
||||
AssertOwningThread();
|
||||
DebugOnly<uint32_t> closedCount = 0;
|
||||
|
||||
ReadStreamList::ForwardIterator iter(mReadStreamList);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream::Controllable> stream = iter.GetNext();
|
||||
if (stream->MatchId(aId)) {
|
||||
stream->CloseStream();
|
||||
closedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(closedCount > 0);
|
||||
}
|
||||
|
||||
void
|
||||
StreamControl::CloseAllReadStreams()
|
||||
{
|
||||
AssertOwningThread();
|
||||
|
||||
ReadStreamList::ForwardIterator iter(mReadStreamList);
|
||||
while (iter.HasMore()) {
|
||||
iter.GetNext()->CloseStream();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamControl::CloseAllReadStreamsWithoutReporting()
|
||||
{
|
||||
AssertOwningThread();
|
||||
|
||||
ReadStreamList::ForwardIterator iter(mReadStreamList);
|
||||
while (iter.HasMore()) {
|
||||
nsRefPtr<ReadStream::Controllable> stream = iter.GetNext();
|
||||
// Note, we cannot trigger IPC traffic here. So use
|
||||
// CloseStreamWithoutReporting().
|
||||
stream->CloseStreamWithoutReporting();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,93 @@
|
|||
/* -*- 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_cache_StreamControl_h
|
||||
#define mozilla_dom_cache_StreamControl_h
|
||||
|
||||
#include "mozilla/dom/cache/ReadStream.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
struct nsID;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
namespace dom {
|
||||
namespace cache {
|
||||
|
||||
class PCacheReadStream;
|
||||
|
||||
// Abstract class to help implement the stream control Child and Parent actors.
|
||||
// This provides an interface to partly help with serialization of IPC types,
|
||||
// but also an implementation for tracking ReadStream objects.
|
||||
class StreamControl
|
||||
{
|
||||
public:
|
||||
// abstract interface that must be implemented by child class
|
||||
virtual void
|
||||
SerializeControl(PCacheReadStream* aReadStreamOut) = 0;
|
||||
|
||||
virtual void
|
||||
SerializeFds(PCacheReadStream* aReadStreamOut,
|
||||
const nsTArray<mozilla::ipc::FileDescriptor>& aFds) = 0;
|
||||
|
||||
virtual void
|
||||
DeserializeFds(const PCacheReadStream& aReadStream,
|
||||
nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) = 0;
|
||||
|
||||
// inherited implementation of the ReadStream::Controllable list
|
||||
|
||||
// Begin controlling the given ReadStream. This causes a strong ref to
|
||||
// be held by the control. The ReadStream must call NoteClosed() or
|
||||
// ForgetReadStream() to release this ref.
|
||||
void
|
||||
AddReadStream(ReadStream::Controllable* aReadStream);
|
||||
|
||||
// Forget the ReadStream without notifying the actor.
|
||||
void
|
||||
ForgetReadStream(ReadStream::Controllable* aReadStream);
|
||||
|
||||
// Forget the ReadStream and then notify the actor the stream is closed.
|
||||
void
|
||||
NoteClosed(ReadStream::Controllable* aReadStream, const nsID& aId);
|
||||
|
||||
protected:
|
||||
~StreamControl();
|
||||
|
||||
void
|
||||
CloseReadStreams(const nsID& aId);
|
||||
|
||||
void
|
||||
CloseAllReadStreams();
|
||||
|
||||
void
|
||||
CloseAllReadStreamsWithoutReporting();
|
||||
|
||||
// protected parts of the abstract interface
|
||||
virtual void
|
||||
NoteClosedAfterForget(const nsID& aId) = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
virtual void
|
||||
AssertOwningThread() = 0;
|
||||
#else
|
||||
void AssertOwningThread() { }
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Hold strong references to ReadStream object. When the stream is closed
|
||||
// it should call NoteClosed() or ForgetReadStream() to release this ref.
|
||||
typedef nsTObserverArray<nsRefPtr<ReadStream::Controllable>> ReadStreamList;
|
||||
ReadStreamList mReadStreamList;
|
||||
};
|
||||
|
||||
} // namespace cache
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_cache_StreamControl_h
|
|
@ -30,6 +30,7 @@ EXPORTS.mozilla.dom.cache += [
|
|||
'QuotaClient.h',
|
||||
'ReadStream.h',
|
||||
'SavedTypes.h',
|
||||
'StreamControl.h',
|
||||
'StreamList.h',
|
||||
'StreamUtils.h',
|
||||
'Types.h',
|
||||
|
@ -59,6 +60,7 @@ UNIFIED_SOURCES += [
|
|||
'PrincipalVerifier.cpp',
|
||||
'QuotaClient.cpp',
|
||||
'ReadStream.cpp',
|
||||
'StreamControl.cpp',
|
||||
'StreamList.cpp',
|
||||
'StreamUtils.cpp',
|
||||
'TypeUtils.cpp',
|
||||
|
|
|
@ -101,7 +101,7 @@ function testRequest(request1, request2, request3, unknownRequest,
|
|||
return checkResponse(r[0], response1, response1Text);
|
||||
}).then(function() {
|
||||
return c.matchAll(requestWithAlternateQueryString,
|
||||
{ignoreSearch: true, cacheName: name});
|
||||
{ignoreSearch: true});
|
||||
}).then(function(r) {
|
||||
is(r.length, 2, "Should find 2 items");
|
||||
return Promise.all([
|
||||
|
@ -122,16 +122,25 @@ function testRequest(request1, request2, request3, unknownRequest,
|
|||
checkResponse(r[1], response3, response3Text)
|
||||
]);
|
||||
}).then(function() {
|
||||
return c.matchAll({cacheName: name + "mambojambo"});
|
||||
}).then(function(r) {
|
||||
is(r.length, 0, "Searching in the wrong cache should not succeed");
|
||||
return caches.match(request1, {cacheName: name + "mambojambo"})
|
||||
.then(function() {
|
||||
ok(false, "Promise should be rejected");
|
||||
}, function(err) {
|
||||
is(err.name, "NotFoundError", "Searching in the wrong cache should not succeed");
|
||||
});
|
||||
}).then(function() {
|
||||
return c.matchAll(unknownRequest);
|
||||
}).then(function(r) {
|
||||
is(r.length, 0, "Searching for an unknown request should not succeed");
|
||||
return c.matchAll(unknownRequest, {cacheName: name});
|
||||
return caches.match(unknownRequest, {cacheName: name});
|
||||
}).then(function(r) {
|
||||
is(r.length, 0, "Searching for an unknown request should not succeed");
|
||||
is(typeof r, "undefined", "Searching for an unknown request should not succeed");
|
||||
// Make sure that cacheName is ignored on Cache
|
||||
return c.matchAll(request1, {cacheName: name + "mambojambo"});
|
||||
}).then(function(r) {
|
||||
is(r.length, 1, "Should only find 1 item");
|
||||
return checkResponse(r[0], response1, response1Text);
|
||||
}).then(function() {
|
||||
return caches.delete(name);
|
||||
}).then(function(success) {
|
||||
ok(success, "We should be able to delete the cache successfully");
|
||||
|
|
|
@ -102,6 +102,11 @@ function testRequest(request, unknownRequest, requestWithAlternateQueryString,
|
|||
}, function(err) {
|
||||
is(err.name, "NotFoundError", "Searching in the wrong cache should not succeed");
|
||||
});
|
||||
}).then(function() {
|
||||
// Make sure that cacheName is ignored on Cache
|
||||
return c.match(request, {cacheName: name + "mambojambo"});
|
||||
}).then(function(r) {
|
||||
return checkResponse(r);
|
||||
}).then(function() {
|
||||
return c.match(unknownRequest);
|
||||
}).then(function(r) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# Mark failing tests in mochi-single.html.
|
||||
|
||||
[DEFAULT]
|
||||
subsuite = webgl
|
||||
skip-if = e10s || os == 'b2g' || ((os == 'linux') && (buildapp == 'b2g')) || ((os == 'linux') && (buildapp == 'mulet'))
|
||||
|
||||
support-files = webgl-conformance/../webgl-mochitest/driver-info.js
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = webgl
|
||||
|
||||
[include:_webgl-conformance.ini]
|
||||
[include:webgl-mochitest.ini]
|
||||
|
|
|
@ -30,8 +30,9 @@ EXTRA_SUPPORT_FILES = [
|
|||
]
|
||||
|
||||
ACCEPTABLE_ERRATA_KEYS = set([
|
||||
'skip-if',
|
||||
'fail-if',
|
||||
'skip-if',
|
||||
'subsuite',
|
||||
])
|
||||
|
||||
GENERATED_HEADER = '''
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# See python/mozbuild/mozbuild/mozinfo.py for incoming data.
|
||||
|
||||
[DEFAULT]
|
||||
subsuite = webgl
|
||||
# No e10s yet.
|
||||
# 'B2G Desktop Linux' fails to create WebGL contexts.
|
||||
# Also skip B2G for now, until we get a handle on the longer tail of emulator
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[DEFAULT]
|
||||
subsuite = webgl
|
||||
skip-if = ((os == 'linux') && (buildapp == 'b2g'))
|
||||
|
||||
support-files =
|
||||
|
|
|
@ -183,3 +183,4 @@ skip-if = buildapp == 'b2g' || e10s
|
|||
support-files =
|
||||
bug1096146_embedded.html
|
||||
[test_offsetxy.html]
|
||||
[test_eventhandler_scoping.html]
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Test for event handler scoping</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
var queryResult;
|
||||
test(function() {
|
||||
var d = document.createElement("div");
|
||||
d.setAttribute("onclick", "queryResult = querySelector('span')");
|
||||
var s = document.createElement("span");
|
||||
d.appendChild(s);
|
||||
d.dispatchEvent(new Event("click"));
|
||||
assert_equals(queryResult, s, "Should have gotten the right object");
|
||||
}, "Test for bareword calls in an event handler using the element as 'this'");
|
||||
</script>
|
|
@ -1335,7 +1335,7 @@ nsresult
|
|||
IDBDatabase::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
{
|
||||
nsresult rv =
|
||||
IndexedDatabaseManager::CommonPostHandleEvent(this, mFactory, aVisitor);
|
||||
IndexedDatabaseManager::CommonPostHandleEvent(aVisitor, mFactory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,12 @@ namespace indexedDB {
|
|||
using namespace mozilla::dom::workers;
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace {
|
||||
|
||||
NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID);
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
IDBRequest::IDBRequest(IDBDatabase* aDatabase)
|
||||
: IDBWrapperCache(aDatabase)
|
||||
{
|
||||
|
@ -407,6 +413,9 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest)
|
||||
if (aIID.Equals(kIDBRequestIID)) {
|
||||
foundInterface = this;
|
||||
} else
|
||||
NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache)
|
||||
|
@ -563,7 +572,7 @@ nsresult
|
|||
IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
{
|
||||
nsresult rv =
|
||||
IndexedDatabaseManager::CommonPostHandleEvent(this, mFactory, aVisitor);
|
||||
IndexedDatabaseManager::CommonPostHandleEvent(aVisitor, mFactory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
#include "nsAutoPtr.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
#define PRIVATE_IDBREQUEST_IID \
|
||||
{0xe68901e5, 0x1d50, 0x4ee9, {0xaf, 0x49, 0x90, 0x99, 0x4a, 0xff, 0xc8, 0x39}}
|
||||
|
||||
class nsPIDOMWindow;
|
||||
struct PRThread;
|
||||
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIDiskSpaceWatcher.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
|
@ -117,6 +119,8 @@ private:
|
|||
|
||||
namespace {
|
||||
|
||||
NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID);
|
||||
|
||||
#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
|
||||
|
||||
const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
|
||||
|
@ -370,38 +374,41 @@ IndexedDatabaseManager::Destroy()
|
|||
|
||||
// static
|
||||
nsresult
|
||||
IndexedDatabaseManager::CommonPostHandleEvent(
|
||||
DOMEventTargetHelper* aEventTarget,
|
||||
IDBFactory* aFactory,
|
||||
EventChainPostVisitor& aVisitor)
|
||||
IndexedDatabaseManager::CommonPostHandleEvent(EventChainPostVisitor& aVisitor,
|
||||
IDBFactory* aFactory)
|
||||
{
|
||||
MOZ_ASSERT(aEventTarget);
|
||||
MOZ_ASSERT(aFactory);
|
||||
MOZ_ASSERT(aVisitor.mDOMEvent);
|
||||
MOZ_ASSERT(aFactory);
|
||||
|
||||
if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString type;
|
||||
nsresult rv = aVisitor.mDOMEvent->GetType(type);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
Event* internalEvent = aVisitor.mDOMEvent->InternalDOMEvent();
|
||||
MOZ_ASSERT(internalEvent);
|
||||
|
||||
NS_NAMED_LITERAL_STRING(errorType, "error");
|
||||
|
||||
MOZ_ASSERT(nsDependentString(kErrorEventType) == errorType);
|
||||
|
||||
if (type != errorType) {
|
||||
if (!internalEvent->IsTrusted()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<EventTarget> eventTarget =
|
||||
aVisitor.mDOMEvent->InternalDOMEvent()->GetTarget();
|
||||
nsString type;
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(internalEvent->GetType(type)));
|
||||
|
||||
MOZ_ASSERT(nsDependentString(kErrorEventType).EqualsLiteral("error"));
|
||||
if (!type.EqualsLiteral("error")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<EventTarget> eventTarget = internalEvent->GetTarget();
|
||||
MOZ_ASSERT(eventTarget);
|
||||
|
||||
auto* request = static_cast<IDBRequest*>(eventTarget.get());
|
||||
// Only mess with events that were originally targeted to an IDBRequest.
|
||||
nsRefPtr<IDBRequest> request;
|
||||
if (NS_FAILED(eventTarget->QueryInterface(kIDBRequestIID,
|
||||
getter_AddRefs(request))) ||
|
||||
!request) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMError> error = request->GetErrorAfterResult();
|
||||
|
||||
|
@ -421,7 +428,7 @@ IndexedDatabaseManager::CommonPostHandleEvent(
|
|||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
if (nsPIDOMWindow* window = aEventTarget->GetOwner()) {
|
||||
if (nsIDOMWindow* window = eventTarget->GetOwnerGlobal()) {
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
|
||||
MOZ_ASSERT(sgo);
|
||||
|
||||
|
@ -441,7 +448,9 @@ IndexedDatabaseManager::CommonPostHandleEvent(
|
|||
MOZ_ASSERT(globalScope);
|
||||
|
||||
nsRefPtr<ErrorEvent> errorEvent =
|
||||
ErrorEvent::Constructor(globalScope, errorType, init);
|
||||
ErrorEvent::Constructor(globalScope,
|
||||
nsDependentString(kErrorEventType),
|
||||
init);
|
||||
MOZ_ASSERT(errorEvent);
|
||||
|
||||
errorEvent->SetTrusted(true);
|
||||
|
|
|
@ -158,9 +158,7 @@ public:
|
|||
}
|
||||
|
||||
static nsresult
|
||||
CommonPostHandleEvent(DOMEventTargetHelper* aEventTarget,
|
||||
IDBFactory* aFactory,
|
||||
EventChainPostVisitor& aVisitor);
|
||||
CommonPostHandleEvent(EventChainPostVisitor& aVisitor, IDBFactory* aFactory);
|
||||
|
||||
static bool
|
||||
TabContextMayAccessOrigin(const mozilla::dom::TabContext& aContext,
|
||||
|
|
|
@ -551,19 +551,20 @@ InitOnContentProcessCreated()
|
|||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static void
|
||||
ResetTransports(void* aUnused) {
|
||||
ResetTransports(void* aUnused)
|
||||
{
|
||||
ContentChild* child = ContentChild::GetSingleton();
|
||||
mozilla::ipc::Transport* transport = child->GetTransport();
|
||||
int fd = transport->GetFileDescriptor();
|
||||
transport->ResetFileDescriptor(fd);
|
||||
|
||||
IToplevelProtocol* toplevel = child->GetFirstOpenedActors();
|
||||
while (toplevel != nullptr) {
|
||||
nsTArray<IToplevelProtocol*> actors;
|
||||
child->GetOpenedActors(actors);
|
||||
for (size_t i = 0; i < actors.Length(); i++) {
|
||||
IToplevelProtocol* toplevel = actors[i];
|
||||
transport = toplevel->GetTransport();
|
||||
fd = transport->GetFileDescriptor();
|
||||
transport->ResetFileDescriptor(fd);
|
||||
|
||||
toplevel = toplevel->getNext();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2677,9 +2678,10 @@ GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
|
|||
content->GetTransport()->GetFileDescriptor();
|
||||
i++;
|
||||
|
||||
for (IToplevelProtocol* actor = content->GetFirstOpenedActors();
|
||||
actor != nullptr;
|
||||
actor = actor->getNext()) {
|
||||
IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
|
||||
size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
IToplevelProtocol* actor = actors[j];
|
||||
if (i >= aInfoListSize) {
|
||||
NS_RUNTIMEABORT("Too many top level protocols!");
|
||||
}
|
||||
|
|
|
@ -528,7 +528,6 @@ child:
|
|||
HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
|
||||
HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid);
|
||||
HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
|
||||
HandleLongTapUp(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid);
|
||||
NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg);
|
||||
|
||||
|
||||
|
|
|
@ -2113,15 +2113,6 @@ TabChild::RecvHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (mGlobal && mTabChildGlobal) {
|
||||
mAPZEventState->ProcessLongTapUp(aPoint, aModifiers, aGuid, GetPresShellResolution());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId,
|
||||
const APZStateChange& aChange,
|
||||
|
|
|
@ -335,9 +335,6 @@ public:
|
|||
const Modifiers& aModifiers,
|
||||
const mozilla::layers::ScrollableLayerGuid& aGuid,
|
||||
const uint64_t& aInputBlockId) MOZ_OVERRIDE;
|
||||
virtual bool RecvHandleLongTapUp(const CSSPoint& aPoint,
|
||||
const Modifiers& aModifiers,
|
||||
const mozilla::layers::ScrollableLayerGuid& aGuid) MOZ_OVERRIDE;
|
||||
virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId,
|
||||
const APZStateChange& aChange,
|
||||
const int& aArg) MOZ_OVERRIDE;
|
||||
|
|
|
@ -964,15 +964,6 @@ void TabParent::HandleLongTap(const CSSPoint& aPoint,
|
|||
}
|
||||
}
|
||||
|
||||
void TabParent::HandleLongTapUp(const CSSPoint& aPoint,
|
||||
Modifiers aModifiers,
|
||||
const ScrollableLayerGuid &aGuid)
|
||||
{
|
||||
if (!mIsDestroyed) {
|
||||
unused << SendHandleLongTapUp(aPoint, aModifiers, aGuid);
|
||||
}
|
||||
}
|
||||
|
||||
void TabParent::NotifyAPZStateChange(ViewID aViewId,
|
||||
APZStateChange aChange,
|
||||
int aArg)
|
||||
|
@ -1198,15 +1189,6 @@ bool TabParent::SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModi
|
|||
return PBrowserParent::SendHandleLongTap(AdjustTapToChildWidget(aPoint), aModifiers, aGuid, aInputBlockId);
|
||||
}
|
||||
|
||||
bool TabParent::SendHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (mIsDestroyed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return PBrowserParent::SendHandleLongTapUp(AdjustTapToChildWidget(aPoint), aModifiers, aGuid);
|
||||
}
|
||||
|
||||
bool TabParent::SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
if (mIsDestroyed) {
|
||||
|
|
|
@ -239,9 +239,6 @@ public:
|
|||
Modifiers aModifiers,
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
uint64_t aInputBlockId);
|
||||
void HandleLongTapUp(const CSSPoint& aPoint,
|
||||
Modifiers aModifiers,
|
||||
const ScrollableLayerGuid& aGuid);
|
||||
void NotifyAPZStateChange(ViewID aViewId,
|
||||
APZStateChange aChange,
|
||||
int aArg);
|
||||
|
@ -268,7 +265,6 @@ public:
|
|||
bool SendRealTouchEvent(WidgetTouchEvent& event);
|
||||
bool SendHandleSingleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
|
||||
bool SendHandleLongTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId);
|
||||
bool SendHandleLongTapUp(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
|
||||
bool SendHandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, const ScrollableLayerGuid& aGuid);
|
||||
|
||||
virtual PDocumentRendererParent*
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
/* 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/. */
|
||||
|
||||
/*
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* ManifestProcessor
|
||||
* Implementation of processing algorithms from:
|
||||
* http://www.w3.org/2008/webapps/manifest/
|
||||
|
@ -11,347 +10,351 @@
|
|||
* or individual parts of a manifest object. A manifest is just a
|
||||
* standard JS object that has been cleaned up.
|
||||
*
|
||||
* .process(jsonText, manifestURL, docURL);
|
||||
* .process({jsonText,manifestURL,docURL});
|
||||
*
|
||||
* TODO: The constructor should accept the UA's supported orientations.
|
||||
* TODO: The constructor should accept the UA's supported display modes.
|
||||
* TODO: hook up developer tools to issueDeveloperWarning (1086997).
|
||||
* TODO: hook up developer tools to console. (1086997).
|
||||
*/
|
||||
/*globals Components*/
|
||||
/*exported EXPORTED_SYMBOLS */
|
||||
/*JSLint options in comment below: */
|
||||
/*globals Components, XPCOMUtils*/
|
||||
'use strict';
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['ManifestProcessor'];
|
||||
const imports = {};
|
||||
const {
|
||||
utils: Cu,
|
||||
classes: Cc,
|
||||
interfaces: Ci
|
||||
} = Components;
|
||||
const imports = {};
|
||||
Cu.import('resource://gre/modules/Services.jsm', imports);
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.importGlobalProperties(['URL']);
|
||||
const securityManager = imports.Services.scriptSecurityManager;
|
||||
const netutil = Cc['@mozilla.org/network/util;1'].getService(Ci.nsINetUtil);
|
||||
const defaultDisplayMode = 'browser';
|
||||
const displayModes = new Set([
|
||||
'fullscreen',
|
||||
'standalone',
|
||||
'minimal-ui',
|
||||
XPCOMUtils.defineLazyModuleGetter(imports, 'Services',
|
||||
'resource://gre/modules/Services.jsm');
|
||||
imports.netutil = Cc['@mozilla.org/network/util;1'].getService(Ci.nsINetUtil);
|
||||
// Helper function extracts values from manifest members
|
||||
// and reports conformance violations.
|
||||
function extractValue({
|
||||
objectName,
|
||||
object,
|
||||
property,
|
||||
expectedType,
|
||||
trim
|
||||
}, console) {
|
||||
const value = object[property];
|
||||
const isArray = Array.isArray(value);
|
||||
// We need to special-case "array", as it's not a JS primitive.
|
||||
const type = (isArray) ? 'array' : typeof value;
|
||||
if (type !== expectedType) {
|
||||
if (type !== 'undefined') {
|
||||
let msg = `Expected the ${objectName}'s ${property} `;
|
||||
msg += `member to a be a ${expectedType}.`;
|
||||
console.log(msg);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
// Trim string and returned undefined if the empty string.
|
||||
const shouldTrim = expectedType === 'string' && value && trim;
|
||||
if (shouldTrim) {
|
||||
return value.trim() || undefined;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
|
||||
'browser'
|
||||
]);
|
||||
const orientationTypes = new Set([
|
||||
'any',
|
||||
'natural',
|
||||
'landscape',
|
||||
'portrait',
|
||||
'portrait-primary',
|
||||
'portrait-secondary',
|
||||
'landscape-primary',
|
||||
const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
|
||||
'portrait-primary', 'portrait-secondary', 'landscape-primary',
|
||||
'landscape-secondary'
|
||||
]);
|
||||
const {
|
||||
ConsoleAPI
|
||||
} = Cu.import('resource://gre/modules/devtools/Console.jsm');
|
||||
|
||||
this.ManifestProcessor = function ManifestProcessor() {};
|
||||
/**
|
||||
* process method: processes json text into a clean manifest
|
||||
* that conforms with the W3C specification.
|
||||
* @param jsonText - the JSON string to be processd.
|
||||
* @param manifestURL - the URL of the manifest, to resolve URLs.
|
||||
* @param docURL - the URL of the owner doc, for security checks
|
||||
*/
|
||||
this.ManifestProcessor.prototype.process = function({
|
||||
jsonText: jsonText,
|
||||
manifestURL: manifestURL,
|
||||
docLocation: docURL
|
||||
}) {
|
||||
/*
|
||||
* This helper function is used to extract values from manifest members.
|
||||
* It also reports conformance violations.
|
||||
*/
|
||||
function extractValue(obj) {
|
||||
let value = obj.object[obj.property];
|
||||
//we need to special-case "array", as it's not a JS primitive
|
||||
const type = (Array.isArray(value)) ? 'array' : typeof value;
|
||||
class ManifestProcessor {
|
||||
|
||||
if (type !== obj.expectedType) {
|
||||
if (type !== 'undefined') {
|
||||
let msg = `Expected the ${obj.objectName}'s ${obj.property}`;
|
||||
msg += `member to a be a ${obj.expectedType}.`;
|
||||
issueDeveloperWarning(msg);
|
||||
}
|
||||
value = undefined;
|
||||
constructor() {}
|
||||
|
||||
static get defaultDisplayMode() {
|
||||
return 'browser';
|
||||
}
|
||||
|
||||
static get displayModes() {
|
||||
return displayModes;
|
||||
}
|
||||
|
||||
static get orientationTypes() {
|
||||
return orientationTypes;
|
||||
}
|
||||
|
||||
// process method: processes json text into a clean manifest
|
||||
// that conforms with the W3C specification. Takes an object
|
||||
// expecting the following dictionary items:
|
||||
// * jsonText: the JSON string to be processd.
|
||||
// * manifestURL: the URL of the manifest, to resolve URLs.
|
||||
// * docURL: the URL of the owner doc, for security checks.
|
||||
process({
|
||||
jsonText, manifestURL, docURL
|
||||
}) {
|
||||
const console = new ConsoleAPI({
|
||||
prefix: 'Web Manifest: '
|
||||
});
|
||||
let rawManifest = {};
|
||||
try {
|
||||
rawManifest = JSON.parse(jsonText);
|
||||
} catch (e) {}
|
||||
if (typeof rawManifest !== 'object' || rawManifest === null) {
|
||||
let msg = 'Manifest needs to be an object.';
|
||||
console.warn(msg);
|
||||
rawManifest = {};
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function issueDeveloperWarning(msg) {
|
||||
//https://bugzilla.mozilla.org/show_bug.cgi?id=1086997
|
||||
}
|
||||
|
||||
function processNameMember(manifest) {
|
||||
const obj = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
property: 'name',
|
||||
expectedType: 'string'
|
||||
const processedManifest = {
|
||||
start_url: processStartURLMember(rawManifest, manifestURL, docURL),
|
||||
display: processDisplayMember(rawManifest),
|
||||
orientation: processOrientationMember(rawManifest),
|
||||
name: processNameMember(rawManifest),
|
||||
icons: IconsProcessor.process(rawManifest, manifestURL, console),
|
||||
short_name: processShortNameMember(rawManifest),
|
||||
};
|
||||
let value = extractValue(obj);
|
||||
return (value) ? value.trim() : value;
|
||||
}
|
||||
processedManifest.scope = processScopeMember(rawManifest, manifestURL,
|
||||
docURL, processedManifest.start_url);
|
||||
return processedManifest;
|
||||
|
||||
function processShortNameMember(manifest) {
|
||||
const obj = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
property: 'short_name',
|
||||
expectedType: 'string'
|
||||
};
|
||||
let value = extractValue(obj);
|
||||
return (value) ? value.trim() : value;
|
||||
}
|
||||
|
||||
function processOrientationMember(manifest) {
|
||||
const obj = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
property: 'orientation',
|
||||
expectedType: 'string'
|
||||
};
|
||||
let value = extractValue(obj);
|
||||
value = (value) ? value.trim() : undefined;
|
||||
//The spec special-cases orientation to return the empty string
|
||||
return (orientationTypes.has(value)) ? value : '';
|
||||
}
|
||||
|
||||
function processDisplayMember(manifest) {
|
||||
const obj = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
property: 'display',
|
||||
expectedType: 'string'
|
||||
};
|
||||
|
||||
let value = extractValue(obj);
|
||||
value = (value) ? value.trim() : value;
|
||||
return (displayModes.has(value)) ? value : defaultDisplayMode;
|
||||
}
|
||||
|
||||
function processScopeMember(manifest, manifestURL, docURL, startURL) {
|
||||
const spec = {
|
||||
function processNameMember(aManifest) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
object: aManifest,
|
||||
property: 'name',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
return extractValue(spec, console);
|
||||
}
|
||||
|
||||
function processShortNameMember(aManifest) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: aManifest,
|
||||
property: 'short_name',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
return extractValue(spec, console);
|
||||
}
|
||||
|
||||
function processOrientationMember(aManifest) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: aManifest,
|
||||
property: 'orientation',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
const value = extractValue(spec, console);
|
||||
if (ManifestProcessor.orientationTypes.has(value)) {
|
||||
return value;
|
||||
}
|
||||
// The spec special-cases orientation to return the empty string.
|
||||
return '';
|
||||
}
|
||||
|
||||
function processDisplayMember(aManifest) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: aManifest,
|
||||
property: 'display',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
const value = extractValue(spec, console);
|
||||
if (ManifestProcessor.displayModes.has(value)) {
|
||||
return value;
|
||||
}
|
||||
return ManifestProcessor.defaultDisplayMode;
|
||||
}
|
||||
|
||||
function processScopeMember(aManifest, aManifestURL, aDocURL, aStartURL) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: aManifest,
|
||||
property: 'scope',
|
||||
expectedType: 'string',
|
||||
dontTrim: true
|
||||
},
|
||||
value = extractValue(spec);
|
||||
let scopeURL;
|
||||
try {
|
||||
scopeURL = new URL(value, manifestURL);
|
||||
} catch (e) {
|
||||
let msg = 'The URL of scope is invalid.';
|
||||
issueDeveloperWarning(msg);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (scopeURL.origin !== docURL.origin) {
|
||||
let msg = 'Scope needs to be same-origin as Document.';
|
||||
issueDeveloperWarning(msg);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
//If start URL is not within scope of scope URL:
|
||||
if (startURL && startURL.origin !== scopeURL.origin || !startURL.pathname.startsWith(scopeURL.pathname)) {
|
||||
let msg = 'The start URL is outside the scope, so scope is invalid.';
|
||||
issueDeveloperWarning(msg);
|
||||
return undefined;
|
||||
}
|
||||
return scopeURL;
|
||||
}
|
||||
|
||||
function processStartURLMember(manifest, manifestURL, docURL) {
|
||||
const obj = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
property: 'start_url',
|
||||
expectedType: 'string'
|
||||
};
|
||||
|
||||
let value = extractValue(obj),
|
||||
result = new URL(docURL),
|
||||
targetURI = makeURI(result),
|
||||
sameOrigin = false,
|
||||
potentialResult,
|
||||
referrerURI;
|
||||
|
||||
if (value === undefined || value === '') {
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
potentialResult = new URL(value, manifestURL);
|
||||
} catch (e) {
|
||||
issueDeveloperWarning('Invalid URL.');
|
||||
return result;
|
||||
}
|
||||
referrerURI = makeURI(potentialResult);
|
||||
try {
|
||||
securityManager.checkSameOriginURI(referrerURI, targetURI, false);
|
||||
sameOrigin = true;
|
||||
} catch (e) {}
|
||||
if (!sameOrigin) {
|
||||
let msg = 'start_url must be same origin as document.';
|
||||
issueDeveloperWarning(msg);
|
||||
} else {
|
||||
result = potentialResult;
|
||||
}
|
||||
return result;
|
||||
|
||||
//Converts a URL to a Gecko URI
|
||||
function makeURI(webURL) {
|
||||
return imports.Services.io.newURI(webURL.toString(), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
//Constants used by IconsProcessor
|
||||
const onlyDecimals = /^\d+$/,
|
||||
anyRegEx = new RegExp('any', 'i');
|
||||
|
||||
function IconsProcessor() {}
|
||||
IconsProcessor.prototype.processIcons = function(manifest, baseURL) {
|
||||
const obj = {
|
||||
objectName: 'manifest',
|
||||
object: manifest,
|
||||
property: 'icons',
|
||||
expectedType: 'array'
|
||||
},
|
||||
icons = [];
|
||||
let value = extractValue(obj);
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
//filter out icons with no "src" or src is empty string
|
||||
let processableIcons = value.filter(
|
||||
icon => icon && Object.prototype.hasOwnProperty.call(icon, 'src') && icon.src !== ''
|
||||
);
|
||||
for (let potentialIcon of processableIcons) {
|
||||
let src = processSrcMember(potentialIcon, baseURL)
|
||||
if(src !== undefined){
|
||||
let icon = {
|
||||
src: src,
|
||||
type: processTypeMember(potentialIcon),
|
||||
sizes: processSizesMember(potentialIcon),
|
||||
density: processDensityMember(potentialIcon)
|
||||
};
|
||||
icons.push(icon);
|
||||
}
|
||||
trim: false
|
||||
};
|
||||
const value = extractValue(spec, console);
|
||||
let scopeURL;
|
||||
try {
|
||||
scopeURL = new URL(value, aManifestURL);
|
||||
} catch (e) {
|
||||
let msg = 'The URL of scope is invalid.';
|
||||
console.warn(msg);
|
||||
return undefined;
|
||||
}
|
||||
if (scopeURL.origin !== aDocURL.origin) {
|
||||
let msg = 'Scope needs to be same-origin as Document.';
|
||||
console.warn(msg);
|
||||
return undefined;
|
||||
}
|
||||
// If start URL is not within scope of scope URL:
|
||||
let isSameOrigin = aStartURL && aStartURL.origin !== scopeURL.origin;
|
||||
if (isSameOrigin || !aStartURL.pathname.startsWith(scopeURL.pathname)) {
|
||||
let msg =
|
||||
'The start URL is outside the scope, so scope is invalid.';
|
||||
console.warn(msg);
|
||||
return undefined;
|
||||
}
|
||||
return scopeURL;
|
||||
}
|
||||
|
||||
function processStartURLMember(aManifest, aManifestURL, aDocURL) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: aManifest,
|
||||
property: 'start_url',
|
||||
expectedType: 'string',
|
||||
trim: false
|
||||
};
|
||||
let result = new URL(aDocURL);
|
||||
const value = extractValue(spec, console);
|
||||
if (value === undefined || value === '') {
|
||||
return result;
|
||||
}
|
||||
let potentialResult;
|
||||
try {
|
||||
potentialResult = new URL(value, aManifestURL);
|
||||
} catch (e) {
|
||||
console.warn('Invalid URL.');
|
||||
return result;
|
||||
}
|
||||
if (potentialResult.origin !== aDocURL.origin) {
|
||||
let msg = 'start_url must be same origin as document.';
|
||||
console.warn(msg);
|
||||
} else {
|
||||
result = potentialResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.ManifestProcessor = ManifestProcessor;
|
||||
|
||||
class IconsProcessor {
|
||||
|
||||
constructor() {
|
||||
throw new Error('Static use only.');
|
||||
}
|
||||
|
||||
static get onlyDecimals() {
|
||||
return /^\d+$/;
|
||||
}
|
||||
|
||||
static get anyRegEx() {
|
||||
return new RegExp('any', 'i');
|
||||
}
|
||||
|
||||
static process(aManifest, aBaseURL, console) {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: aManifest,
|
||||
property: 'icons',
|
||||
expectedType: 'array',
|
||||
trim: false
|
||||
};
|
||||
const icons = [];
|
||||
const value = extractValue(spec, console);
|
||||
if (Array.isArray(value)) {
|
||||
// Filter out icons whose "src" is not useful.
|
||||
value.filter(item => !!processSrcMember(item, aBaseURL))
|
||||
.map(toIconObject)
|
||||
.forEach(icon => icons.push(icon));
|
||||
}
|
||||
return icons;
|
||||
|
||||
function processTypeMember(icon) {
|
||||
const charset = {},
|
||||
hadCharset = {},
|
||||
obj = {
|
||||
objectName: 'icon',
|
||||
object: icon,
|
||||
property: 'type',
|
||||
expectedType: 'string'
|
||||
};
|
||||
let value = extractValue(obj),
|
||||
isParsable = (typeof value === 'string' && value.length > 0);
|
||||
value = (isParsable) ? netutil.parseContentType(value.trim(), charset, hadCharset) : undefined;
|
||||
return (value === '') ? undefined : value;
|
||||
function toIconObject(aIconData) {
|
||||
return {
|
||||
src: processSrcMember(aIconData, aBaseURL),
|
||||
type: processTypeMember(aIconData),
|
||||
sizes: processSizesMember(aIconData),
|
||||
density: processDensityMember(aIconData)
|
||||
};
|
||||
}
|
||||
|
||||
function processDensityMember(icon) {
|
||||
const hasDensity = Object.prototype.hasOwnProperty.call(icon, 'density'),
|
||||
rawValue = (hasDensity) ? icon.density : undefined,
|
||||
value = parseFloat(rawValue),
|
||||
result = (Number.isNaN(value) || value === +Infinity || value <= 0) ? 1.0 : value;
|
||||
return result;
|
||||
function processTypeMember(aIcon) {
|
||||
const charset = {};
|
||||
const hadCharset = {};
|
||||
const spec = {
|
||||
objectName: 'icon',
|
||||
object: aIcon,
|
||||
property: 'type',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
let value = extractValue(spec, console);
|
||||
if (value) {
|
||||
value = imports.netutil.parseContentType(value, charset, hadCharset);
|
||||
}
|
||||
return value || undefined;
|
||||
}
|
||||
|
||||
function processSrcMember(icon, baseURL) {
|
||||
const obj = {
|
||||
objectName: 'icon',
|
||||
object: icon,
|
||||
property: 'src',
|
||||
expectedType: 'string'
|
||||
},
|
||||
value = extractValue(obj);
|
||||
function processDensityMember(aIcon) {
|
||||
const value = parseFloat(aIcon.density);
|
||||
const validNum = Number.isNaN(value) || value === +Infinity || value <=
|
||||
0;
|
||||
return (validNum) ? 1.0 : value;
|
||||
}
|
||||
|
||||
function processSrcMember(aIcon, aBaseURL) {
|
||||
const spec = {
|
||||
objectName: 'icon',
|
||||
object: aIcon,
|
||||
property: 'src',
|
||||
expectedType: 'string',
|
||||
trim: false
|
||||
};
|
||||
const value = extractValue(spec, console);
|
||||
let url;
|
||||
if (typeof value === 'string' && value.trim() !== '') {
|
||||
if (value && value.length) {
|
||||
try {
|
||||
url = new URL(value, baseURL);
|
||||
url = new URL(value, aBaseURL);
|
||||
} catch (e) {}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function processSizesMember(icon) {
|
||||
function processSizesMember(aIcon) {
|
||||
const sizes = new Set(),
|
||||
obj = {
|
||||
spec = {
|
||||
objectName: 'icon',
|
||||
object: icon,
|
||||
object: aIcon,
|
||||
property: 'sizes',
|
||||
expectedType: 'string'
|
||||
};
|
||||
let value = extractValue(obj);
|
||||
value = (value) ? value.trim() : value;
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
},
|
||||
value = extractValue(spec, console);
|
||||
if (value) {
|
||||
//split on whitespace and filter out invalid values
|
||||
let validSizes = value.split(/\s+/).filter(isValidSizeValue);
|
||||
validSizes.forEach((size) => sizes.add(size));
|
||||
// Split on whitespace and filter out invalid values.
|
||||
value.split(/\s+/)
|
||||
.filter(isValidSizeValue)
|
||||
.forEach(size => sizes.add(size));
|
||||
}
|
||||
return sizes;
|
||||
|
||||
/*
|
||||
* Implementation of HTML's link@size attribute checker
|
||||
*/
|
||||
function isValidSizeValue(size) {
|
||||
if (anyRegEx.test(size)) {
|
||||
// Implementation of HTML's link@size attribute checker.
|
||||
function isValidSizeValue(aSize) {
|
||||
const size = aSize.toLowerCase();
|
||||
if (IconsProcessor.anyRegEx.test(aSize)) {
|
||||
return true;
|
||||
}
|
||||
size = size.toLowerCase();
|
||||
if (!size.contains('x') || size.indexOf('x') !== size.lastIndexOf('x')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//split left of x for width, after x for height
|
||||
const width = size.substring(0, size.indexOf('x'));
|
||||
const height = size.substring(size.indexOf('x') + 1, size.length);
|
||||
const isValid = !(height.startsWith('0') || width.startsWith('0') || !onlyDecimals.test(width + height));
|
||||
return isValid;
|
||||
// Split left of x for width, after x for height.
|
||||
const widthAndHeight = size.split('x');
|
||||
const w = widthAndHeight.shift();
|
||||
const h = widthAndHeight.join('x');
|
||||
const validStarts = !w.startsWith('0') && !h.startsWith('0');
|
||||
const validDecimals = IconsProcessor.onlyDecimals.test(w + h);
|
||||
return (validStarts && validDecimals);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function processIconsMember(manifest, manifestURL) {
|
||||
const iconsProcessor = new IconsProcessor();
|
||||
return iconsProcessor.processIcons(manifest, manifestURL);
|
||||
}
|
||||
|
||||
//Processing starts here!
|
||||
let manifest = {};
|
||||
|
||||
try {
|
||||
manifest = JSON.parse(jsonText);
|
||||
if (typeof manifest !== 'object' || manifest === null) {
|
||||
let msg = 'Manifest needs to be an object.';
|
||||
issueDeveloperWarning(msg);
|
||||
manifest = {};
|
||||
}
|
||||
} catch (e) {
|
||||
issueDeveloperWarning(e);
|
||||
}
|
||||
|
||||
const processedManifest = {
|
||||
start_url: processStartURLMember(manifest, manifestURL, docURL),
|
||||
display: processDisplayMember(manifest),
|
||||
orientation: processOrientationMember(manifest),
|
||||
name: processNameMember(manifest),
|
||||
icons: processIconsMember(manifest, manifestURL),
|
||||
short_name: processShortNameMember(manifest)
|
||||
};
|
||||
processedManifest.scope = processScopeMember(manifest, manifestURL, docURL, processedManifest.start_url);
|
||||
return processedManifest;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -16,5 +16,5 @@ const bsp = SpecialPowers.Cu.import('resource://gre/modules/ManifestProcessor.js
|
|||
data = {
|
||||
jsonText: '{}',
|
||||
manifestURL: manifestURL,
|
||||
docLocation: docLocation
|
||||
docURL: docLocation
|
||||
};
|
||||
|
|
|
@ -221,6 +221,7 @@ static const char* const gOmxTypes[] = {
|
|||
"audio/mp4",
|
||||
"audio/amr",
|
||||
"audio/3gpp",
|
||||
"audio/flac",
|
||||
"video/mp4",
|
||||
"video/3gpp",
|
||||
"video/3gpp2",
|
||||
|
|
|
@ -1418,7 +1418,11 @@ NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
|
|||
MediaManager::Get() {
|
||||
if (!sSingleton) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
|
||||
|
||||
#ifdef DEBUG
|
||||
static int timesCreated = 0;
|
||||
timesCreated++;
|
||||
MOZ_ASSERT(timesCreated == 1);
|
||||
#endif
|
||||
sSingleton = new MediaManager();
|
||||
|
||||
sSingleton->mMediaThread = new base::Thread("MediaManager");
|
||||
|
@ -1454,6 +1458,11 @@ MediaManager::Get() {
|
|||
return sSingleton;
|
||||
}
|
||||
|
||||
/* static */ MediaManager*
|
||||
MediaManager::GetIfExists() {
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<MediaManager>
|
||||
MediaManager::GetInstance()
|
||||
{
|
||||
|
|
|
@ -554,6 +554,7 @@ public:
|
|||
// thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
|
||||
// from MediaManager thread.
|
||||
static MediaManager* Get();
|
||||
static MediaManager* GetIfExists();
|
||||
static MessageLoop* GetMessageLoop();
|
||||
|
||||
static bool Exists()
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "GMPVideoDecoder.h"
|
||||
#include "GMPVideoHost.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -132,13 +133,26 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
|
|||
|
||||
memcpy(frame->Buffer(), aSample->data, frame->Size());
|
||||
|
||||
// Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
|
||||
// suit the GMP API.
|
||||
if (mConvertNALUnitLengths) {
|
||||
const int kNALLengthSize = 4;
|
||||
uint8_t* buf = frame->Buffer();
|
||||
while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
|
||||
uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
|
||||
*reinterpret_cast<uint32_t *>(buf) = length;
|
||||
buf += length;
|
||||
}
|
||||
}
|
||||
|
||||
frame->SetBufferType(GMP_BufferLength32);
|
||||
|
||||
frame->SetEncodedWidth(mConfig.display_width);
|
||||
frame->SetEncodedHeight(mConfig.display_height);
|
||||
frame->SetTimeStamp(aSample->composition_timestamp);
|
||||
frame->SetCompleteFrame(true);
|
||||
frame->SetDuration(aSample->duration);
|
||||
frame->SetFrameType(aSample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
|
||||
frame->SetBufferType(GMP_BufferLength32);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
@ -157,6 +171,16 @@ GMPVideoDecoder::Init()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(mHost && mGMP);
|
||||
|
||||
// GMP implementations have interpreted the meaning of GMP_BufferLength32
|
||||
// differently. The OpenH264 GMP expects GMP_BufferLength32 to behave as
|
||||
// specified in the GMP API, where each buffer is prefixed by a 32-bit
|
||||
// host-endian buffer length that includes the size of the buffer length
|
||||
// field. Other existing GMPs currently expect GMP_BufferLength32 (when
|
||||
// combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
|
||||
// 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
|
||||
// and do not include the length of the buffer length field.
|
||||
mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
|
||||
|
||||
GMPVideoCodec codec;
|
||||
memset(&codec, 0, sizeof(codec));
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ protected:
|
|||
, mGMP(nullptr)
|
||||
, mHost(nullptr)
|
||||
, mAdapter(aAdapter)
|
||||
, mConvertNALUnitLengths(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -79,6 +80,7 @@ public:
|
|||
VideoInfo(aConfig.display_width,
|
||||
aConfig.display_height),
|
||||
aImageContainer))
|
||||
, mConvertNALUnitLengths(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -100,9 +102,9 @@ private:
|
|||
GMPVideoDecoderProxy* mGMP;
|
||||
GMPVideoHost* mHost;
|
||||
nsAutoPtr<VideoCallbackAdapter> mAdapter;
|
||||
bool mConvertNALUnitLengths;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPVideoDecoder_h_
|
||||
|
|
|
@ -34,25 +34,18 @@ using CrashReporter::GetIDFromMinidump;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef LOG
|
||||
#undef LOG
|
||||
#endif
|
||||
#undef LOGD
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* GetGMPLog();
|
||||
|
||||
#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
|
||||
#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
|
||||
#define LOG(level, x, ...) PR_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
|
||||
#define LOGD(x, ...) LOG(PR_LOG_DEBUG, "GMPParent[%p|childPid=%d] " x, this, mChildPid, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOGD(msg)
|
||||
#define LOG(level, msg)
|
||||
#define LOG(level, x, ...)
|
||||
#define LOGD(x, ...)
|
||||
#endif
|
||||
|
||||
#ifdef __CLASS__
|
||||
#undef __CLASS__
|
||||
#endif
|
||||
#define __CLASS__ "GMPParent"
|
||||
|
||||
namespace gmp {
|
||||
|
||||
GMPParent::GMPParent()
|
||||
|
@ -62,13 +55,18 @@ GMPParent::GMPParent()
|
|||
, mAbnormalShutdownInProgress(false)
|
||||
, mAsyncShutdownRequired(false)
|
||||
, mAsyncShutdownInProgress(false)
|
||||
#ifdef PR_LOGGING
|
||||
, mChildPid(0)
|
||||
#endif
|
||||
{
|
||||
LOGD("GMPParent ctor");
|
||||
}
|
||||
|
||||
GMPParent::~GMPParent()
|
||||
{
|
||||
// Can't Close or Destroy the process here, since destruction is MainThread only
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOGD("GMPParent dtor");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -107,8 +105,7 @@ GMPParent::Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir)
|
|||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this,
|
||||
NS_LossyConvertUTF16toASCII(parentLeafName).get()));
|
||||
LOGD("%s: for %s", __FUNCTION__, NS_LossyConvertUTF16toASCII(parentLeafName).get());
|
||||
|
||||
MOZ_ASSERT(parentLeafName.Length() > 4);
|
||||
mName = Substring(parentLeafName, 4);
|
||||
|
@ -135,7 +132,7 @@ GMPParent::LoadProcess()
|
|||
if (NS_FAILED(mDirectory->GetPath(path))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, path.get()));
|
||||
LOGD("%s: for %s", __FUNCTION__, NS_ConvertUTF16toUTF8(path).get());
|
||||
|
||||
if (!mProcess) {
|
||||
mProcess = new GMPProcessParent(NS_ConvertUTF16toUTF8(path).get());
|
||||
|
@ -145,32 +142,36 @@ GMPParent::LoadProcess()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
mChildPid = base::GetProcId(mProcess->GetChildProcessHandle());
|
||||
#endif
|
||||
|
||||
bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle());
|
||||
if (!opened) {
|
||||
LOGD(("%s::%s: Failed to create new child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
|
||||
LOGD("%s: Failed to create new child process", __FUNCTION__);
|
||||
mProcess->Delete();
|
||||
mProcess = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOGD(("%s::%s: Created new child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
|
||||
LOGD("%s: Created new child process", __FUNCTION__);
|
||||
|
||||
bool ok = SendSetNodeId(mNodeId);
|
||||
if (!ok) {
|
||||
LOGD(("%s::%s: Failed to send node id to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
|
||||
LOGD("%s: Failed to send node id to child process", __FUNCTION__);
|
||||
mProcess->Delete();
|
||||
mProcess = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOGD(("%s::%s: Sent node id to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
|
||||
LOGD("%s: Sent node id to child process", __FUNCTION__);
|
||||
|
||||
ok = SendStartPlugin();
|
||||
if (!ok) {
|
||||
LOGD(("%s::%s: Failed to send start to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
|
||||
LOGD("%s: Failed to send start to child process", __FUNCTION__);
|
||||
mProcess->Delete();
|
||||
mProcess = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOGD(("%s::%s: Sent StartPlugin to child process %p", __CLASS__, __FUNCTION__, (void *)mProcess));
|
||||
LOGD("%s: Sent StartPlugin to child process", __FUNCTION__);
|
||||
}
|
||||
|
||||
mState = GMPStateLoaded;
|
||||
|
@ -225,8 +226,7 @@ void
|
|||
GMPParent::CloseIfUnused()
|
||||
{
|
||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
LOGD(("%s::%s: %p mAsyncShutdownRequired=%d", __CLASS__, __FUNCTION__, this,
|
||||
mAsyncShutdownRequired));
|
||||
LOGD("%s: mAsyncShutdownRequired=%d", __FUNCTION__, mAsyncShutdownRequired);
|
||||
|
||||
if ((mDeleteProcessOnlyOnUnload ||
|
||||
mState == GMPStateLoaded ||
|
||||
|
@ -243,8 +243,7 @@ GMPParent::CloseIfUnused()
|
|||
|
||||
if (mAsyncShutdownRequired) {
|
||||
if (!mAsyncShutdownInProgress) {
|
||||
LOGD(("%s::%s: %p sending async shutdown notification", __CLASS__,
|
||||
__FUNCTION__, this));
|
||||
LOGD("%s: sending async shutdown notification", __FUNCTION__);
|
||||
mAsyncShutdownInProgress = true;
|
||||
if (!SendBeginAsyncShutdown() ||
|
||||
NS_FAILED(EnsureAsyncShutdownTimeoutSet())) {
|
||||
|
@ -265,7 +264,7 @@ void
|
|||
GMPParent::AbortAsyncShutdown()
|
||||
{
|
||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
LOGD("%s", __FUNCTION__);
|
||||
|
||||
if (mAsyncShutdownTimeout) {
|
||||
mAsyncShutdownTimeout->Cancel();
|
||||
|
@ -299,7 +298,7 @@ GMPParent::AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder)
|
|||
void
|
||||
GMPParent::CloseActive(bool aDieWhenUnloaded)
|
||||
{
|
||||
LOGD(("%s::%s: %p state %d", __CLASS__, __FUNCTION__, this, mState));
|
||||
LOGD("%s: state %d", __FUNCTION__, mState);
|
||||
if (aDieWhenUnloaded) {
|
||||
mDeleteProcessOnlyOnUnload = true; // don't allow this to go back...
|
||||
}
|
||||
|
@ -339,7 +338,7 @@ GMPParent::CloseActive(bool aDieWhenUnloaded)
|
|||
void
|
||||
GMPParent::Shutdown()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
LOGD("%s", __FUNCTION__);
|
||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
|
||||
MOZ_ASSERT(!mAsyncShutdownTimeout, "Should have canceled shutdown timeout");
|
||||
|
@ -384,7 +383,7 @@ public:
|
|||
void
|
||||
GMPParent::DeleteProcess()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
LOGD("%s", __FUNCTION__);
|
||||
|
||||
if (mState != GMPStateClosing) {
|
||||
// Don't Close() twice!
|
||||
|
@ -393,7 +392,7 @@ GMPParent::DeleteProcess()
|
|||
Close();
|
||||
}
|
||||
mProcess->Delete();
|
||||
LOGD(("%s::%s: Shut down process %p", __CLASS__, __FUNCTION__, (void *) mProcess));
|
||||
LOGD("%s: Shut down process", __FUNCTION__);
|
||||
mProcess = nullptr;
|
||||
mState = GMPStateNotLoaded;
|
||||
|
||||
|
@ -654,7 +653,7 @@ GMPNotifyObservers(nsAString& aData)
|
|||
void
|
||||
GMPParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
LOGD(("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int) aWhy));
|
||||
LOGD("%s: (%d)", __FUNCTION__, (int)aWhy);
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
if (AbnormalShutdown == aWhy) {
|
||||
Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
|
||||
|
@ -1002,6 +1001,12 @@ GMPParent::SetNodeId(const nsACString& aNodeId)
|
|||
mNodeId = aNodeId;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GMPParent::GetDisplayName() const
|
||||
{
|
||||
return mDisplayName;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GMPParent::GetVersion() const
|
||||
{
|
||||
|
@ -1011,7 +1016,7 @@ GMPParent::GetVersion() const
|
|||
bool
|
||||
GMPParent::RecvAsyncShutdownRequired()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
LOGD("%s", __FUNCTION__);
|
||||
if (mAsyncShutdownRequired) {
|
||||
NS_WARNING("Received AsyncShutdownRequired message more than once!");
|
||||
return true;
|
||||
|
@ -1024,7 +1029,7 @@ GMPParent::RecvAsyncShutdownRequired()
|
|||
bool
|
||||
GMPParent::RecvAsyncShutdownComplete()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
LOGD("%s", __FUNCTION__);
|
||||
|
||||
MOZ_ASSERT(mAsyncShutdownRequired);
|
||||
AbortAsyncShutdown();
|
||||
|
@ -1033,3 +1038,6 @@ GMPParent::RecvAsyncShutdownComplete()
|
|||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#undef LOG
|
||||
#undef LOGD
|
||||
|
|
|
@ -115,6 +115,7 @@ public:
|
|||
void SetNodeId(const nsACString& aNodeId);
|
||||
const nsACString& GetNodeId() const { return mNodeId; }
|
||||
|
||||
const nsCString& GetDisplayName() const;
|
||||
const nsCString& GetVersion() const;
|
||||
|
||||
// Returns true if a plugin can be or is being used across multiple NodeIds.
|
||||
|
@ -198,6 +199,10 @@ private:
|
|||
|
||||
bool mAsyncShutdownRequired;
|
||||
bool mAsyncShutdownInProgress;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
int mChildPid;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -39,7 +39,7 @@ extern PRLogModuleInfo* GetGMPLog();
|
|||
#ifdef __CLASS__
|
||||
#undef __CLASS__
|
||||
#endif
|
||||
#define __CLASS__ "GMPParent"
|
||||
#define __CLASS__ "GMPStorageParent"
|
||||
|
||||
namespace gmp {
|
||||
|
||||
|
|
|
@ -182,6 +182,18 @@ GMPVideoDecoderParent::Drain()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
GMPVideoDecoderParent::GetDisplayName() const
|
||||
{
|
||||
if (!mIsOpen) {
|
||||
NS_WARNING("Trying to use an dead GMP video decoder");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
|
||||
|
||||
return mPlugin->GetDisplayName();
|
||||
}
|
||||
|
||||
// Note: Consider keeping ActorDestroy sync'd up when making changes here.
|
||||
nsresult
|
||||
GMPVideoDecoderParent::Shutdown()
|
||||
|
|
|
@ -45,6 +45,7 @@ public:
|
|||
virtual nsresult Reset() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast<uint64_t>(mPlugin.get()); }
|
||||
virtual const nsCString& GetDisplayName() const MOZ_OVERRIDE;
|
||||
|
||||
// GMPSharedMemManager
|
||||
virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) MOZ_OVERRIDE
|
||||
|
|
|
@ -49,6 +49,8 @@ public:
|
|||
// Call to tell GMP/plugin the consumer will no longer use this
|
||||
// interface/codec.
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual const nsCString& GetDisplayName() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,6 +9,7 @@ var gSmallTests = [
|
|||
{ name:"small-shot.m4a", type:"audio/mp4", duration:0.29 },
|
||||
{ name:"small-shot.mp3", type:"audio/mpeg", duration:0.27 },
|
||||
{ name:"small-shot-mp3.mp4", type:"audio/mp4; codecs=mp3", duration:0.34 },
|
||||
{ name:"small-shot.flac", type:"audio/flac", duration:0.197 },
|
||||
{ name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 },
|
||||
{ name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.266 },
|
||||
{ name:"seek.webm", type:"video/webm", width:320, height:240, duration:3.966 },
|
||||
|
|
|
@ -247,6 +247,7 @@ support-files =
|
|||
small-shot.mp3^headers^
|
||||
small-shot.ogg
|
||||
small-shot.ogg^headers^
|
||||
small-shot.flac
|
||||
sound.ogg
|
||||
sound.ogg^headers^
|
||||
spacestorm-1000Hz-100ms.ogg
|
||||
|
|
Двоичный файл не отображается.
|
@ -742,9 +742,9 @@ function PeerConnectionWrapper(label, configuration, h264) {
|
|||
this.remoteRequiresTrickleIce = false;
|
||||
this.localMediaElements = [];
|
||||
|
||||
this.expectedLocalTrackTypesById = {};
|
||||
this.expectedRemoteTrackTypesById = {};
|
||||
this.observedRemoteTrackTypesById = {};
|
||||
this.expectedLocalTrackInfoById = {};
|
||||
this.expectedRemoteTrackInfoById = {};
|
||||
this.observedRemoteTrackInfoById = {};
|
||||
|
||||
this.disableRtpCountChecking = false;
|
||||
|
||||
|
@ -875,7 +875,10 @@ PeerConnectionWrapper.prototype = {
|
|||
stream.getTracks().forEach(track => {
|
||||
ok(track.id, "track has id");
|
||||
ok(track.kind, "track has kind");
|
||||
this.expectedLocalTrackTypesById[track.id] = track.kind;
|
||||
this.expectedLocalTrackInfoById[track.id] = {
|
||||
type: track.kind,
|
||||
streamId: stream.id
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -893,14 +896,17 @@ PeerConnectionWrapper.prototype = {
|
|||
|
||||
removeSender : function(index) {
|
||||
var sender = this._pc.getSenders()[index];
|
||||
delete this.expectedLocalTrackTypesById[sender.track.id];
|
||||
delete this.expectedLocalTrackInfoById[sender.track.id];
|
||||
this._pc.removeTrack(sender);
|
||||
},
|
||||
|
||||
senderReplaceTrack : function(index, withTrack) {
|
||||
senderReplaceTrack : function(index, withTrack, withStreamId) {
|
||||
var sender = this._pc.getSenders()[index];
|
||||
delete this.expectedLocalTrackTypesById[sender.track.id];
|
||||
this.expectedLocalTrackTypesById[withTrack.id] = withTrack.kind;
|
||||
delete this.expectedLocalTrackInfoById[sender.track.id];
|
||||
this.expectedLocalTrackInfoById[withTrack.id] = {
|
||||
type: withTrack.kind,
|
||||
streamId: withStreamId
|
||||
};
|
||||
return sender.replaceTrack(withTrack);
|
||||
},
|
||||
|
||||
|
@ -1081,20 +1087,23 @@ PeerConnectionWrapper.prototype = {
|
|||
/**
|
||||
* Checks whether a given track is expected, has not been observed yet, and
|
||||
* is of the correct type. Then, moves the track from
|
||||
* |expectedTrackTypesById| to |observedTrackTypesById|.
|
||||
* |expectedTrackInfoById| to |observedTrackInfoById|.
|
||||
*/
|
||||
checkTrackIsExpected : function(track,
|
||||
expectedTrackTypesById,
|
||||
observedTrackTypesById) {
|
||||
ok(expectedTrackTypesById[track.id], "track id " + track.id + " was expected");
|
||||
ok(!observedTrackTypesById[track.id], "track id " + track.id + " was not yet observed");
|
||||
expectedTrackInfoById,
|
||||
observedTrackInfoById) {
|
||||
ok(expectedTrackInfoById[track.id], "track id " + track.id + " was expected");
|
||||
ok(!observedTrackInfoById[track.id], "track id " + track.id + " was not yet observed");
|
||||
var observedKind = track.kind;
|
||||
var expectedKind = expectedTrackTypesById[track.id];
|
||||
var expectedKind = expectedTrackInfoById[track.id].type;
|
||||
is(observedKind, expectedKind,
|
||||
"track id " + track.id + " was of kind " +
|
||||
observedKind + ", which matches " + expectedKind);
|
||||
observedTrackTypesById[track.id] = expectedTrackTypesById[track.id];
|
||||
delete expectedTrackTypesById[track.id];
|
||||
observedTrackInfoById[track.id] = expectedTrackInfoById[track.id];
|
||||
},
|
||||
|
||||
allExpectedTracksAreObserved: function(expected, observed) {
|
||||
return Object.keys(expected).every(trackId => observed[trackId]);
|
||||
},
|
||||
|
||||
setupAddStreamEventHandler: function() {
|
||||
|
@ -1112,11 +1121,12 @@ PeerConnectionWrapper.prototype = {
|
|||
|
||||
event.stream.getTracks().forEach(track => {
|
||||
this.checkTrackIsExpected(track,
|
||||
this.expectedRemoteTrackTypesById,
|
||||
this.observedRemoteTrackTypesById);
|
||||
this.expectedRemoteTrackInfoById,
|
||||
this.observedRemoteTrackInfoById);
|
||||
});
|
||||
|
||||
if (Object.keys(this.expectedRemoteTrackTypesById).length === 0) {
|
||||
if (this.allExpectedTracksAreObserved(this.expectedRemoteTrackInfoById,
|
||||
this.observedRemoteTrackInfoById)) {
|
||||
resolveAllAddStreamEventsDone();
|
||||
}
|
||||
|
||||
|
@ -1363,24 +1373,14 @@ PeerConnectionWrapper.prototype = {
|
|||
},
|
||||
|
||||
checkLocalMediaTracks : function() {
|
||||
var observedLocalTrackTypesById = {};
|
||||
// We do not want to empty out this.expectedLocalTrackTypesById, so make a
|
||||
// copy.
|
||||
var expectedLocalTrackTypesById =
|
||||
JSON.parse(JSON.stringify((this.expectedLocalTrackTypesById)));
|
||||
info(this + " Checking local tracks " +
|
||||
JSON.stringify(expectedLocalTrackTypesById));
|
||||
this._pc.getLocalStreams().forEach(stream => {
|
||||
stream.getTracks().forEach(track => {
|
||||
this.checkTrackIsExpected(track,
|
||||
expectedLocalTrackTypesById,
|
||||
observedLocalTrackTypesById);
|
||||
});
|
||||
var observed = {};
|
||||
info(this + " Checking local tracks " + JSON.stringify(this.expectedLocalTrackInfoById));
|
||||
this._pc.getSenders().forEach(sender => {
|
||||
this.checkTrackIsExpected(sender.track, this.expectedLocalTrackInfoById, observed);
|
||||
});
|
||||
|
||||
Object.keys(expectedLocalTrackTypesById).forEach(id => {
|
||||
ok(false, this + " local id " + id + " was observed");
|
||||
});
|
||||
Object.keys(this.expectedLocalTrackInfoById).forEach(
|
||||
id => ok(observed[id], this + " local id " + id + " was observed"));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1393,10 +1393,11 @@ PeerConnectionWrapper.prototype = {
|
|||
this.checkLocalMediaTracks();
|
||||
|
||||
info(this + " Checking remote tracks " +
|
||||
JSON.stringify(this.expectedRemoteTrackTypesById));
|
||||
JSON.stringify(this.expectedRemoteTrackInfoById));
|
||||
|
||||
// No tracks are expected
|
||||
if (Object.keys(this.expectedRemoteTrackTypesById).length === 0) {
|
||||
if (this.allExpectedTracksAreObserved(this.expectedRemoteTrackInfoById,
|
||||
this.observedRemoteTrackInfoById)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1404,19 +1405,18 @@ PeerConnectionWrapper.prototype = {
|
|||
},
|
||||
|
||||
checkMsids: function() {
|
||||
var checkSdpForMsids = (desc, streams, side) => {
|
||||
streams.forEach(stream => {
|
||||
stream.getTracks().forEach(track => {
|
||||
ok(desc.sdp.match(new RegExp("a=msid:" + stream.id + " " + track.id)),
|
||||
this + ": " + side + " SDP contains stream " + stream.id +
|
||||
" and track " + track.id );
|
||||
});
|
||||
var checkSdpForMsids = (desc, expectedTrackInfo, side) => {
|
||||
Object.keys(expectedTrackInfo).forEach(trackId => {
|
||||
var streamId = expectedTrackInfo[trackId].streamId;
|
||||
ok(desc.sdp.match(new RegExp("a=msid:" + streamId + " " + trackId)),
|
||||
this + ": " + side + " SDP contains stream " + streamId +
|
||||
" and track " + trackId );
|
||||
});
|
||||
};
|
||||
|
||||
checkSdpForMsids(this.localDescription, this._pc.getLocalStreams(),
|
||||
checkSdpForMsids(this.localDescription, this.expectedLocalTrackInfoById,
|
||||
"local");
|
||||
checkSdpForMsids(this.remoteDescription, this._pc.getRemoteStreams(),
|
||||
checkSdpForMsids(this.remoteDescription, this.expectedRemoteTrackInfoById,
|
||||
"remote");
|
||||
},
|
||||
|
||||
|
@ -1507,11 +1507,6 @@ PeerConnectionWrapper.prototype = {
|
|||
*/
|
||||
checkStats : function(stats, twoMachines) {
|
||||
var toNum = obj => obj? obj : 0;
|
||||
var numTracks = streams =>
|
||||
streams.reduce((count, stream) => count +
|
||||
stream.getAudioTracks().length +
|
||||
stream.getVideoTracks().length,
|
||||
0);
|
||||
|
||||
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
|
||||
|
||||
|
@ -1599,8 +1594,8 @@ PeerConnectionWrapper.prototype = {
|
|||
});
|
||||
is(JSON.stringify(counters), JSON.stringify(counters2),
|
||||
"Spec and MapClass variant of RTCStatsReport enumeration agree");
|
||||
var nin = numTracks(this._pc.getRemoteStreams());
|
||||
var nout = numTracks(this._pc.getLocalStreams());
|
||||
var nin = Object.keys(this.expectedRemoteTrackInfoById).length;
|
||||
var nout = Object.keys(this.expectedLocalTrackInfoById).length;
|
||||
var ndata = this.dataChannels.length;
|
||||
|
||||
// TODO(Bug 957145): Restore stronger inboundrtp test once Bug 948249 is fixed
|
||||
|
|
|
@ -106,36 +106,32 @@ function waitForAnIceCandidate(pc) {
|
|||
});
|
||||
}
|
||||
|
||||
function checkTrackStats(pc, audio, outbound) {
|
||||
var stream = outbound ? pc._pc.getLocalStreams()[0] : pc._pc.getRemoteStreams()[0];
|
||||
if (!stream) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
var track = audio ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];
|
||||
if (!track) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
function checkTrackStats(pc, rtpSenderOrReceiver, outbound) {
|
||||
var track = rtpSenderOrReceiver.track;
|
||||
var audio = (track.kind == "audio");
|
||||
var msg = pc + " stats " + (outbound ? "outbound " : "inbound ") +
|
||||
(audio ? "audio" : "video") + " rtp ";
|
||||
(audio ? "audio" : "video") + " rtp track id " + track.id;
|
||||
return pc.getStats(track).then(stats => {
|
||||
ok(pc.hasStat(stats, {
|
||||
type: outbound ? "outboundrtp" : "inboundrtp",
|
||||
isRemote: false,
|
||||
mediaType: audio ? "audio" : "video"
|
||||
}), msg + "1");
|
||||
}), msg + " - found expected stats");
|
||||
ok(!pc.hasStat(stats, {
|
||||
type: outbound ? "inboundrtp" : "outboundrtp",
|
||||
isRemote: false
|
||||
}), msg + "2");
|
||||
}), msg + " - did not find extra stats with wrong direction");
|
||||
ok(!pc.hasStat(stats, {
|
||||
mediaType: audio ? "video" : "audio"
|
||||
}), msg + "3");
|
||||
}), msg + " - did not find extra stats with wrong media type");
|
||||
});
|
||||
}
|
||||
|
||||
// checks all stats combinations inbound/outbound, audio/video
|
||||
var checkAllTrackStats = pc =>
|
||||
Promise.all([0, 1, 2, 3].map(i => checkTrackStats(pc, i & 1, i & 2)));
|
||||
var checkAllTrackStats = pc => {
|
||||
return Promise.all([].concat(
|
||||
pc._pc.getSenders().map(sender => checkTrackStats(pc, sender, true)),
|
||||
pc._pc.getReceivers().map(receiver => checkTrackStats(pc, receiver, false))));
|
||||
}
|
||||
|
||||
// Commands run once at the beginning of each test, even when performing a
|
||||
// renegotiation test.
|
||||
|
@ -246,14 +242,14 @@ var commandsPeerConnectionOfferAnswer = [
|
|||
function PC_LOCAL_STEEPLECHASE_SIGNAL_EXPECTED_LOCAL_TRACKS(test) {
|
||||
if (test.steeplechase) {
|
||||
send_message({"type": "local_expected_tracks",
|
||||
"expected_tracks": test.pcLocal.expectedLocalTrackTypesById});
|
||||
"expected_tracks": test.pcLocal.expectedLocalTrackInfoById});
|
||||
}
|
||||
},
|
||||
|
||||
function PC_REMOTE_STEEPLECHASE_SIGNAL_EXPECTED_LOCAL_TRACKS(test) {
|
||||
if (test.steeplechase) {
|
||||
send_message({"type": "remote_expected_tracks",
|
||||
"expected_tracks": test.pcRemote.expectedLocalTrackTypesById});
|
||||
"expected_tracks": test.pcRemote.expectedLocalTrackInfoById});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -261,36 +257,26 @@ var commandsPeerConnectionOfferAnswer = [
|
|||
if (test.steeplechase) {
|
||||
return test.getSignalingMessage("remote_expected_tracks").then(
|
||||
message => {
|
||||
test.pcLocal.expectedRemoteTrackTypesById = message.expected_tracks;
|
||||
test.pcLocal.expectedRemoteTrackInfoById = message.expected_tracks;
|
||||
});
|
||||
} else {
|
||||
// Deep copy, as similar to steeplechase as possible
|
||||
test.pcLocal.expectedRemoteTrackTypesById =
|
||||
JSON.parse(JSON.stringify((test.pcRemote.expectedLocalTrackTypesById)));
|
||||
}
|
||||
|
||||
// Remove what we've already observed
|
||||
Object.keys(test.pcLocal.observedRemoteTrackTypesById).forEach(id => {
|
||||
delete test.pcLocal.expectedRemoteTrackTypesById[id];
|
||||
});
|
||||
// Deep copy, as similar to steeplechase as possible
|
||||
test.pcLocal.expectedRemoteTrackInfoById =
|
||||
JSON.parse(JSON.stringify(test.pcRemote.expectedLocalTrackInfoById));
|
||||
},
|
||||
|
||||
function PC_REMOTE_GET_EXPECTED_REMOTE_TRACKS(test) {
|
||||
if (test.steeplechase) {
|
||||
return test.getSignalingMessage("local_expected_tracks").then(
|
||||
message => {
|
||||
test.pcRemote.expectedRemoteTrackTypesById = message.expected_tracks;
|
||||
test.pcRemote.expectedRemoteTrackInfoById = message.expected_tracks;
|
||||
});
|
||||
} else {
|
||||
// Deep copy, as similar to steeplechase as possible
|
||||
test.pcRemote.expectedRemoteTrackTypesById =
|
||||
JSON.parse(JSON.stringify((test.pcLocal.expectedLocalTrackTypesById)));
|
||||
}
|
||||
|
||||
// Remove what we've already observed
|
||||
Object.keys(test.pcRemote.observedRemoteTrackTypesById).forEach(id => {
|
||||
delete test.pcRemote.expectedRemoteTrackTypesById[id];
|
||||
});
|
||||
// Deep copy, as similar to steeplechase as possible
|
||||
test.pcRemote.expectedRemoteTrackInfoById =
|
||||
JSON.parse(JSON.stringify(test.pcLocal.expectedLocalTrackInfoById));
|
||||
},
|
||||
|
||||
function PC_LOCAL_CREATE_OFFER(test) {
|
||||
|
|
|
@ -31,9 +31,12 @@ runNetworkTest(function() {
|
|||
.then(() => {
|
||||
var stream = v1.mozCaptureStreamUntilEnded();
|
||||
is(stream.getTracks().length, 2, "Captured stream has 2 tracks");
|
||||
stream.getTracks().forEach(tr => {
|
||||
test.pcLocal._pc.addTrack(tr, stream);
|
||||
test.pcLocal.expectedLocalTrackTypesById[tr.id] = tr.kind;
|
||||
stream.getTracks().forEach(track => {
|
||||
test.pcLocal._pc.addTrack(track, stream);
|
||||
test.pcLocal.expectedLocalTrackInfoById[track.id] = {
|
||||
type: track.kind,
|
||||
streamId: stream.id
|
||||
};
|
||||
});
|
||||
test.pcLocal.constraints = [{ video: true, audio:true }]; // fool tests
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
return navigator.mediaDevices.getUserMedia({video:true, fake:true})
|
||||
.then(newstream => {
|
||||
var newtrack = newstream.getVideoTracks()[0];
|
||||
return test.pcLocal.senderReplaceTrack(0, newtrack);
|
||||
return test.pcLocal.senderReplaceTrack(0, newtrack, newstream.id);
|
||||
})
|
||||
.then(() => {
|
||||
test.setMediaConstraints([{video: true}, {video: true}],
|
||||
|
|
|
@ -111,7 +111,9 @@ skip-if = toolkit == 'android' # bug 1056706
|
|||
[test_mediaDecoding.html]
|
||||
[test_mediaElementAudioSourceNode.html]
|
||||
[test_mediaElementAudioSourceNodePassThrough.html]
|
||||
skip-if = toolkit == 'android' # bug 1145816
|
||||
[test_mediaElementAudioSourceNodeCrossOrigin.html]
|
||||
skip-if = toolkit == 'android' # bug 1145816
|
||||
[test_mediaStreamAudioDestinationNode.html]
|
||||
[test_mediaStreamAudioSourceNode.html]
|
||||
[test_mediaStreamAudioSourceNodeCrossOrigin.html]
|
||||
|
|
|
@ -941,8 +941,108 @@ bool WebMReader::DecodeAudioData()
|
|||
return DecodeAudioPacket(holder->mPacket, holder->mOffset);
|
||||
}
|
||||
|
||||
bool WebMReader::FilterPacketByTime(int64_t aEndTime, WebMPacketQueue& aOutput)
|
||||
{
|
||||
// Push the video frames to the aOutput which's timestamp is less
|
||||
// than aEndTime.
|
||||
while (true) {
|
||||
nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO));
|
||||
if (!holder) {
|
||||
break;
|
||||
}
|
||||
uint64_t tstamp = 0;
|
||||
int r = nestegg_packet_tstamp(holder->mPacket, &tstamp);
|
||||
if (r == -1) {
|
||||
break;
|
||||
}
|
||||
uint64_t tstamp_usecs = tstamp / NS_PER_USEC;
|
||||
if (tstamp_usecs >= (uint64_t)aEndTime) {
|
||||
PushVideoPacket(holder.disown());
|
||||
return true;
|
||||
} else {
|
||||
aOutput.PushFront(holder.disown());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t WebMReader::GetNextKeyframeTime(int64_t aTimeThreshold)
|
||||
{
|
||||
WebMPacketQueue skipPacketQueue;
|
||||
if (!FilterPacketByTime(aTimeThreshold, skipPacketQueue)) {
|
||||
// Restore the packets before we return -1.
|
||||
uint32_t size = skipPacketQueue.GetSize();
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
PushVideoPacket(skipPacketQueue.PopFront());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find keyframe.
|
||||
bool foundKeyframe = false;
|
||||
int64_t keyframeTime = -1;
|
||||
while (!foundKeyframe) {
|
||||
nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO));
|
||||
if (!holder) {
|
||||
break;
|
||||
}
|
||||
unsigned int count = 0;
|
||||
int r = nestegg_packet_count(holder->mPacket, &count);
|
||||
if (r == -1) {
|
||||
break;
|
||||
}
|
||||
uint64_t tstamp = 0;
|
||||
r = nestegg_packet_tstamp(holder->mPacket, &tstamp);
|
||||
if (r == -1) {
|
||||
break;
|
||||
}
|
||||
uint64_t tstamp_usecs = tstamp / NS_PER_USEC;
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
unsigned char* data;
|
||||
size_t length;
|
||||
r = nestegg_packet_data(holder->mPacket, i, &data, &length);
|
||||
if (r == -1) {
|
||||
foundKeyframe = true;
|
||||
break;
|
||||
}
|
||||
vpx_codec_stream_info_t si;
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.sz = sizeof(si);
|
||||
if (mVideoCodec == NESTEGG_CODEC_VP8) {
|
||||
vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
|
||||
} else if (mVideoCodec == NESTEGG_CODEC_VP9) {
|
||||
vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si);
|
||||
}
|
||||
if (si.is_kf) {
|
||||
foundKeyframe = true;
|
||||
keyframeTime = tstamp_usecs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
skipPacketQueue.PushFront(holder.disown());
|
||||
}
|
||||
|
||||
uint32_t size = skipPacketQueue.GetSize();
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
PushVideoPacket(skipPacketQueue.PopFront());
|
||||
}
|
||||
|
||||
return keyframeTime;
|
||||
}
|
||||
|
||||
bool WebMReader::ShouldSkipVideoFrame(int64_t aTimeThreshold)
|
||||
{
|
||||
return GetNextKeyframeTime(aTimeThreshold) != -1;
|
||||
}
|
||||
|
||||
bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold)
|
||||
{
|
||||
if (!(aKeyframeSkip && ShouldSkipVideoFrame(aTimeThreshold))) {
|
||||
LOG(PR_LOG_DEBUG, ("Reader [%p]: set the aKeyframeSkip to false.",this));
|
||||
aKeyframeSkip = false;
|
||||
}
|
||||
return mVideoDecoder->DecodeVideoFrame(aKeyframeSkip, aTimeThreshold);
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,15 @@ protected:
|
|||
// Initializes mLayersBackendType if possible.
|
||||
void InitLayersBackendType();
|
||||
|
||||
bool ShouldSkipVideoFrame(int64_t aTimeThreshold);
|
||||
|
||||
private:
|
||||
// Get the timestamp of keyframe greater than aTimeThreshold.
|
||||
int64_t GetNextKeyframeTime(int64_t aTimeThreshold);
|
||||
// Push the packets into aOutput which's timestamp is less than aEndTime.
|
||||
// Return false if we reach the end of stream or something wrong.
|
||||
bool FilterPacketByTime(int64_t aEndTime, WebMPacketQueue& aOutput);
|
||||
|
||||
// libnestegg context for webm container. Access on state machine thread
|
||||
// or decoder thread only.
|
||||
nestegg* mContext;
|
||||
|
|
|
@ -24,8 +24,7 @@ interface AnimationPlayer {
|
|||
[SetterThrows, BinaryName="currentTimeAsDouble"]
|
||||
attribute double? currentTime;
|
||||
|
||||
/* Not yet implemented
|
||||
attribute double playbackRate; */
|
||||
attribute double playbackRate;
|
||||
[BinaryName="playStateFromJS"]
|
||||
readonly attribute AnimationPlayState playState;
|
||||
[Throws]
|
||||
|
|
|
@ -18,6 +18,7 @@ interface FetchEvent : Event {
|
|||
readonly attribute boolean isReload;
|
||||
|
||||
[Throws] void respondWith(Promise<Response> r);
|
||||
[Throws] void respondWith(Response r);
|
||||
};
|
||||
|
||||
dictionary FetchEventInit : EventInit {
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
[Global=(WorkerDebugger), Exposed=WorkerDebugger]
|
||||
interface WorkerDebuggerGlobalScope : EventTarget {
|
||||
readonly attribute object global;
|
||||
|
||||
void postMessage(DOMString message);
|
||||
|
||||
attribute EventHandler onmessage;
|
||||
};
|
||||
|
||||
// So you can debug while you debug
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
@ -283,6 +285,26 @@ FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv)
|
|||
aPromise.AppendNativeHandler(handler);
|
||||
}
|
||||
|
||||
void
|
||||
FetchEvent::RespondWith(Response& aResponse, ErrorResult& aRv)
|
||||
{
|
||||
if (mWaitToRespond) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
nsRefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
promise->MaybeResolve(&aResponse);
|
||||
|
||||
RespondWith(*promise, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerClient>
|
||||
FetchEvent::GetClient()
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/dom/FetchEventBinding.h"
|
||||
#include "mozilla/dom/InstallEventBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Response.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
class nsIInterceptedChannel;
|
||||
|
@ -83,6 +84,9 @@ public:
|
|||
void
|
||||
RespondWith(Promise& aPromise, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
RespondWith(Response& aResponse, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ForwardTo(const nsAString& aUrl);
|
||||
|
||||
|
|
|
@ -1295,6 +1295,54 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable {
|
||||
nsString mMessage;
|
||||
|
||||
public:
|
||||
DebuggerMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aMessage)
|
||||
: WorkerDebuggerRunnable(aWorkerPrivate),
|
||||
mMessage(aMessage)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
|
||||
{
|
||||
WorkerDebuggerGlobalScope* globalScope = aWorkerPrivate->DebuggerGlobalScope();
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, mMessage.get(),
|
||||
mMessage.Length()));
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
JS::Rooted<JS::Value> data(aCx, JS::StringValue(message));
|
||||
|
||||
nsRefPtr<MessageEvent> event = new MessageEvent(globalScope, nullptr,
|
||||
nullptr);
|
||||
nsresult rv =
|
||||
event->InitMessageEvent(NS_LITERAL_STRING("message"),
|
||||
false, // canBubble
|
||||
true, // cancelable
|
||||
data,
|
||||
EmptyString(),
|
||||
EmptyString(),
|
||||
nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
xpc::Throw(aCx, rv);
|
||||
return false;
|
||||
}
|
||||
event->SetTrusted(true);
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
globalScope->DispatchDOMEvent(nullptr, domEvent, nullptr, &status);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NotifyRunnable MOZ_FINAL : public WorkerControlRunnable
|
||||
{
|
||||
Status mStatus;
|
||||
|
@ -4322,6 +4370,26 @@ WorkerDebugger::Initialize(const nsAString& aURL, JSContext* aCx)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::PostMessageMoz(const nsAString& aMessage, JSContext* aCx)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!mWorkerPrivate || !mIsInitialized) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsRefPtr<DebuggerMessageEventRunnable> runnable =
|
||||
new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
|
||||
if (!runnable->Dispatch(aCx)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener)
|
||||
{
|
||||
|
@ -4402,6 +4470,35 @@ WorkerDebugger::Disable()
|
|||
NotifyIsEnabled(false);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<nsString>(this,
|
||||
&WorkerDebugger::PostMessageToDebuggerOnMainThread, nsString(aMessage));
|
||||
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
listeners.AppendElements(mListeners);
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < listeners.Length(); ++index) {
|
||||
listeners[index]->OnMessage(aMessage);
|
||||
}
|
||||
}
|
||||
|
||||
WorkerPrivate::WorkerPrivate(JSContext* aCx,
|
||||
WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL,
|
||||
|
@ -6016,6 +6113,12 @@ WorkerPrivate::PostMessageToParentMessagePort(
|
|||
aMessagePortSerial, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
|
||||
{
|
||||
mDebugger->PostMessageToDebugger(aMessage);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
|
||||
{
|
||||
|
|
|
@ -738,18 +738,30 @@ public:
|
|||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIWORKERDEBUGGER
|
||||
|
||||
void AssertIsOnParentThread();
|
||||
void
|
||||
AssertIsOnParentThread();
|
||||
|
||||
void WaitIsEnabled(bool aIsEnabled);
|
||||
void
|
||||
WaitIsEnabled(bool aIsEnabled);
|
||||
|
||||
void Enable();
|
||||
void
|
||||
Enable();
|
||||
|
||||
void Disable();
|
||||
void
|
||||
Disable();
|
||||
|
||||
void
|
||||
PostMessageToDebugger(const nsAString& aMessage);
|
||||
|
||||
private:
|
||||
virtual ~WorkerDebugger();
|
||||
virtual
|
||||
~WorkerDebugger();
|
||||
|
||||
void NotifyIsEnabled(bool aIsEnabled);
|
||||
void
|
||||
NotifyIsEnabled(bool aIsEnabled);
|
||||
|
||||
void
|
||||
PostMessageToDebuggerOnMainThread(const nsAString& aMessage);
|
||||
};
|
||||
|
||||
class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
|
||||
|
@ -950,6 +962,9 @@ public:
|
|||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
PostMessageToDebugger(const nsAString& aMessage);
|
||||
|
||||
bool
|
||||
NotifyInternal(JSContext* aCx, Status aStatus);
|
||||
|
||||
|
|
|
@ -513,6 +513,12 @@ WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx,
|
|||
aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper());
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage)
|
||||
{
|
||||
mWorkerPrivate->PostMessageToDebugger(aMessage);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
|
||||
const Optional<nsAString>& aString) const
|
||||
|
|
|
@ -264,6 +264,11 @@ public:
|
|||
void
|
||||
GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal);
|
||||
|
||||
void
|
||||
PostMessage(const nsAString& aMessage);
|
||||
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
|
||||
void
|
||||
Dump(JSContext* aCx, const Optional<nsAString>& aString) const;
|
||||
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
interface nsIDOMWindow;
|
||||
|
||||
[scriptable, uuid(54fd2dd3-c01b-4f71-888f-462f37a54f57)]
|
||||
[scriptable, uuid(ead45621-22a7-48ef-b7db-c4252ae3e6cb)]
|
||||
interface nsIWorkerDebuggerListener : nsISupports
|
||||
{
|
||||
void onClose();
|
||||
|
||||
void onMessage(in DOMString message);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(b0ea6da8-8bd9-446a-94e2-2ee979903205)]
|
||||
[scriptable, builtinclass, uuid(28e0a60c-ff10-446c-8c2a-5fbdc01394ea)]
|
||||
interface nsIWorkerDebugger : nsISupports
|
||||
{
|
||||
const unsigned long TYPE_DEDICATED = 0;
|
||||
|
@ -30,6 +32,9 @@ interface nsIWorkerDebugger : nsISupports
|
|||
[implicit_jscontext]
|
||||
void initialize(in DOMString url);
|
||||
|
||||
[implicit_jscontext, binaryname(PostMessageMoz)]
|
||||
void postMessage(in DOMString message);
|
||||
|
||||
void addListener(in nsIWorkerDebuggerListener listener);
|
||||
|
||||
void removeListener(in nsIWorkerDebuggerListener listener);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
|
@ -0,0 +1,9 @@
|
|||
"use strict"
|
||||
|
||||
onmessage = function (event) {
|
||||
switch (event.data) {
|
||||
case "ping":
|
||||
postMessage("pong");
|
||||
break;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
var worker = new Worker("WorkerDebugger.postMessage_childWorker.js");
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче