зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
d8044bfc25
|
@ -104,8 +104,8 @@ AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccHideEvent::
|
||||
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode),
|
||||
AccHideEvent(Accessible* aTarget, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget),
|
||||
mNeedsShutdown(aNeedsShutdown)
|
||||
{
|
||||
mNextSibling = mAccessible->NextSibling();
|
||||
|
@ -118,8 +118,8 @@ AccHideEvent::
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccShowEvent::
|
||||
AccShowEvent(Accessible* aTarget, nsINode* aTargetNode) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode)
|
||||
AccShowEvent(Accessible* aTarget) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -212,8 +212,7 @@ private:
|
|||
class AccMutationEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget,
|
||||
nsINode* aTargetNode) :
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceMutationTextChange)
|
||||
{
|
||||
// Don't coalesce these since they are coalesced by reorder event. Coalesce
|
||||
|
@ -250,8 +249,7 @@ protected:
|
|||
class AccHideEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccHideEvent(Accessible* aTarget, nsINode* aTargetNode,
|
||||
bool aNeedsShutdown = true);
|
||||
explicit AccHideEvent(Accessible* aTarget, bool aNeedsShutdown = true);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eHideEvent;
|
||||
|
@ -281,7 +279,7 @@ protected:
|
|||
class AccShowEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
AccShowEvent(Accessible* aTarget, nsINode* aTargetNode);
|
||||
explicit AccShowEvent(Accessible* aTarget);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eShowEvent;
|
||||
|
|
|
@ -1511,7 +1511,7 @@ DocAccessible::DoInitialUpdate()
|
|||
uint32_t childCount = ChildCount();
|
||||
for (uint32_t i = 0; i < childCount; i++) {
|
||||
Accessible* child = GetChildAt(i);
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(child);
|
||||
FireDelayedEvent(event);
|
||||
}
|
||||
}
|
||||
|
@ -1892,7 +1892,6 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
|||
// this node already then it will be suppressed by this one.
|
||||
Accessible* focusedAcc = nullptr;
|
||||
|
||||
nsINode* node = aChild->GetNode();
|
||||
if (aIsInsert) {
|
||||
// Create accessible tree for shown accessible.
|
||||
CacheChildrenInSubtree(aChild, &focusedAcc);
|
||||
|
@ -1914,9 +1913,9 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
|||
// Fire show/hide event.
|
||||
RefPtr<AccMutationEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(aChild, node);
|
||||
event = new AccShowEvent(aChild);
|
||||
else
|
||||
event = new AccHideEvent(aChild, node);
|
||||
event = new AccHideEvent(aChild);
|
||||
|
||||
FireDelayedEvent(event);
|
||||
aReorderEvent->AddSubMutationEvent(event);
|
||||
|
@ -2089,8 +2088,7 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
|||
int32_t oldIdxInParent = aChild->IndexInParent();
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
|
@ -2121,8 +2119,7 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
|||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
reorderEvent = new AccReorderEvent(aNewParent);
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
|
||||
FireDelayedEvent(showEvent);
|
||||
|
@ -2140,8 +2137,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
|||
|
||||
Accessible* parent = aChild->Parent();
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(aChild, aChild->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
AutoTreeMutation mut(parent);
|
||||
|
@ -2152,8 +2148,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
|||
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
RefPtr<AccMutationEvent> showEvent =
|
||||
new AccShowEvent(aChild, aChild->GetContent());
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
FireDelayedEvent(showEvent);
|
||||
|
||||
|
@ -2177,8 +2172,7 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
|||
continue;
|
||||
}
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
||||
RefPtr<AccMutationEvent> hideEvent =
|
||||
new AccHideEvent(child, child->GetContent(), false);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(child, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
|
||||
{
|
||||
|
|
|
@ -121,7 +121,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
|||
}
|
||||
|
||||
if (aDoFireEvents) {
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area, areaContent);
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ HTMLLIAccessible::UpdateBullet(bool aHasBullet)
|
|||
document->BindToDocument(mBullet, nullptr);
|
||||
InsertChildAt(0, mBullet);
|
||||
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet, mBullet->GetContent());
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "16ebbfb6ce62c14573982b0aa87537ef8f857047",
|
||||
"git_revision": "81c021654c657f6995e5e70ef02ee067d4bfedbd",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "8668ab0c480334a48e1935cce440e33dff23b33a",
|
||||
"revision": "e651f5b571034436b7bc8eb8099033dff5623a20",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!--
|
||||
B2G repositories for all targets
|
||||
-->
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="16ebbfb6ce62c14573982b0aa87537ef8f857047"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="81c021654c657f6995e5e70ef02ee067d4bfedbd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
|
||||
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
|
||||
|
|
|
@ -1014,8 +1014,6 @@ pref("browser.sessionstore.interval", 15000);
|
|||
// on which sites to save text data, POSTDATA and cookies
|
||||
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
|
||||
pref("browser.sessionstore.privacy_level", 0);
|
||||
// the same as browser.sessionstore.privacy_level, but for saving deferred session data
|
||||
pref("browser.sessionstore.privacy_level_deferred", 1);
|
||||
// how many tabs can be reopened (per window)
|
||||
pref("browser.sessionstore.max_tabs_undo", 10);
|
||||
// how many windows can be reopened (per session) - on non-OS X platforms this
|
||||
|
@ -1405,8 +1403,8 @@ pref("security.insecure_password.ui.enabled", false);
|
|||
// 1 = allow MITM for certificate pinning checks.
|
||||
pref("security.cert_pinning.enforcement_level", 1);
|
||||
|
||||
// 2 = allow SHA-1 only before 2016-01-01
|
||||
pref("security.pki.sha1_enforcement_level", 2);
|
||||
// 0 = allow SHA-1
|
||||
pref("security.pki.sha1_enforcement_level", 0);
|
||||
|
||||
// Required blocklist freshness for OneCRL OCSP bypass
|
||||
// (default is 1.25x extensions.blocklist.interval, or 30 hours)
|
||||
|
@ -1502,7 +1500,8 @@ pref("media.eme.apiVisible", true);
|
|||
|
||||
// Decode using Gecko Media Plugins in <video>, if a system decoder is not
|
||||
// availble and the preferred GMP is available.
|
||||
pref("media.gmp.decoder.enabled", true);
|
||||
// NOTE: Disabled until Bug 1236756 is fixed by Adobe.
|
||||
pref("media.gmp.decoder.enabled", false);
|
||||
|
||||
// If decoding-via-GMP is turned on for <video>, use Adobe's GMP for decoding,
|
||||
// if it's available. Note: We won't fallback to another GMP if Adobe's is not
|
||||
|
|
|
@ -6132,6 +6132,16 @@
|
|||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="setUserContextId">
|
||||
<parameter name="aUserContextId"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.linkedBrowser.setAttribute("usercontextid", aUserContextId);
|
||||
this.setAttribute("usercontextid", aUserContextId);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
|
|
|
@ -80,8 +80,7 @@ add_task(function* test() {
|
|||
// check each item in the title and validate it meets expectatations
|
||||
for (let part of title) {
|
||||
let [storageMethodName, value] = part.split("=");
|
||||
let is_f = storageMethodName == "cookie" ? is : todo_is;
|
||||
is_f(value, expectedContext,
|
||||
is(value, expectedContext,
|
||||
"the title reflects the expected contextual identity of " +
|
||||
expectedContext + " for method " + storageMethodName + ": " + value);
|
||||
}
|
||||
|
|
|
@ -18,12 +18,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
|
|||
* |url|.
|
||||
*
|
||||
* @param url The URL we want to save data for.
|
||||
* @param isPinned Whether the given |url| is contained in a pinned tab.
|
||||
* @return bool
|
||||
*/
|
||||
function checkPrivacyLevel(url, isPinned) {
|
||||
function checkPrivacyLevel(url) {
|
||||
let isHttps = url.startsWith("https:");
|
||||
return PrivacyLevel.canSave({isHttps: isHttps, isPinned: isPinned});
|
||||
return PrivacyLevel.canSave({isHttps});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,14 +36,13 @@ this.PrivacyFilter = Object.freeze({
|
|||
* we're allowed to store.
|
||||
*
|
||||
* @param data The session storage data as collected from a tab.
|
||||
* @param isPinned Whether the tab we collected from is pinned.
|
||||
* @return object
|
||||
*/
|
||||
filterSessionStorageData: function (data, isPinned) {
|
||||
filterSessionStorageData: function (data) {
|
||||
let retval = {};
|
||||
|
||||
for (let host of Object.keys(data)) {
|
||||
if (checkPrivacyLevel(host, isPinned)) {
|
||||
if (checkPrivacyLevel(host)) {
|
||||
retval[host] = data[host];
|
||||
}
|
||||
}
|
||||
|
@ -58,14 +56,13 @@ this.PrivacyFilter = Object.freeze({
|
|||
* allowed to store.
|
||||
*
|
||||
* @param data The form data as collected from a tab.
|
||||
* @param isPinned Whether the tab we collected from is pinned.
|
||||
* @return object
|
||||
*/
|
||||
filterFormData: function (data, isPinned) {
|
||||
filterFormData: function (data) {
|
||||
// If the given form data object has an associated URL that we are not
|
||||
// allowed to store data for, bail out. We explicitly discard data for any
|
||||
// children as well even if storing data for those frames would be allowed.
|
||||
if (data.url && !checkPrivacyLevel(data.url, isPinned)) {
|
||||
if (data.url && !checkPrivacyLevel(data.url)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -73,7 +70,7 @@ this.PrivacyFilter = Object.freeze({
|
|||
|
||||
for (let key of Object.keys(data)) {
|
||||
if (key === "children") {
|
||||
let recurse = child => this.filterFormData(child, isPinned);
|
||||
let recurse = child => this.filterFormData(child);
|
||||
let children = data.children.map(recurse).filter(child => child);
|
||||
|
||||
if (children.length) {
|
||||
|
|
|
@ -14,8 +14,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
||||
"@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
|
||||
|
||||
const PREF_NORMAL = "browser.sessionstore.privacy_level";
|
||||
const PREF_DEFERRED = "browser.sessionstore.privacy_level_deferred";
|
||||
const PREF = "browser.sessionstore.privacy_level";
|
||||
|
||||
// The following constants represent the different possible privacy levels that
|
||||
// can be set by the user and that we need to consider when collecting text
|
||||
|
@ -28,25 +27,6 @@ const PRIVACY_ENCRYPTED = 1;
|
|||
// Collect no data.
|
||||
const PRIVACY_FULL = 2;
|
||||
|
||||
/**
|
||||
* Determines the current privacy level as set by the user.
|
||||
*
|
||||
* @param isPinned
|
||||
* Whether to return the privacy level for pinned tabs.
|
||||
* @return {int} The privacy level as read from the user's preferences.
|
||||
*/
|
||||
function getCurrentLevel(isPinned) {
|
||||
let pref = PREF_NORMAL;
|
||||
|
||||
// If we're in the process of quitting and we're not autoresuming the session
|
||||
// then we will use the deferred privacy level for non-pinned tabs.
|
||||
if (!isPinned && Services.startup.shuttingDown && !gSessionStartup.isAutomaticRestoreEnabled()) {
|
||||
pref = PREF_DEFERRED;
|
||||
}
|
||||
|
||||
return Services.prefs.getIntPref(pref);
|
||||
}
|
||||
|
||||
/**
|
||||
* The external API as exposed by this module.
|
||||
*/
|
||||
|
@ -54,14 +34,13 @@ var PrivacyLevel = Object.freeze({
|
|||
/**
|
||||
* Checks whether we're allowed to save data for a specific site.
|
||||
*
|
||||
* @param {isHttps: boolean, isPinned: boolean}
|
||||
* An object that must have two properties: 'isHttps' and 'isPinned'.
|
||||
* @param {isHttps: boolean}
|
||||
* An object that must have one property: 'isHttps'.
|
||||
* 'isHttps' tells whether the site us secure communication (HTTPS).
|
||||
* 'isPinned' tells whether the site is loaded in a pinned tab.
|
||||
* @return {bool} Whether we can save data for the specified site.
|
||||
*/
|
||||
canSave: function ({isHttps, isPinned}) {
|
||||
let level = getCurrentLevel(isPinned);
|
||||
canSave: function ({isHttps}) {
|
||||
let level = Services.prefs.getIntPref(PREF);
|
||||
|
||||
// Never save any data when full privacy is requested.
|
||||
if (level == PRIVACY_FULL) {
|
||||
|
|
|
@ -64,7 +64,7 @@ var SessionHistoryInternal = {
|
|||
* The docShell that owns the session history.
|
||||
*/
|
||||
collect: function (docShell) {
|
||||
let data = {entries: []};
|
||||
let data = {entries: [], userContextId: docShell.userContextId };
|
||||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
|
||||
|
||||
|
@ -253,6 +253,10 @@ var SessionHistoryInternal = {
|
|||
let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let history = webNavigation.sessionHistory;
|
||||
|
||||
if ("userContextId" in tabData) {
|
||||
docShell.userContextId = tabData.userContextId;
|
||||
}
|
||||
|
||||
if (history.count > 0) {
|
||||
history.PurgeHistory(history.count);
|
||||
}
|
||||
|
|
|
@ -3189,6 +3189,10 @@ var SessionStoreInternal = {
|
|||
tabbrowser.showTab(tab);
|
||||
}
|
||||
|
||||
if (tabData.userContextId) {
|
||||
tab.setUserContextId(tabData.userContextId);
|
||||
}
|
||||
|
||||
if (!!tabData.muted != browser.audioMuted) {
|
||||
tab.toggleMuteAudio();
|
||||
}
|
||||
|
|
|
@ -224,15 +224,19 @@ var TabStateInternal = {
|
|||
// Filter sensitive data according to the current privacy level.
|
||||
if (!includePrivateData) {
|
||||
if (key === "storage") {
|
||||
value = PrivacyFilter.filterSessionStorageData(value, isPinned);
|
||||
value = PrivacyFilter.filterSessionStorageData(value);
|
||||
} else if (key === "formdata") {
|
||||
value = PrivacyFilter.filterFormData(value, isPinned);
|
||||
value = PrivacyFilter.filterFormData(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (key === "history") {
|
||||
tabData.entries = value.entries;
|
||||
|
||||
if (value.hasOwnProperty("userContextId")) {
|
||||
tabData.userContextId = value.userContextId;
|
||||
}
|
||||
|
||||
if (value.hasOwnProperty("index")) {
|
||||
tabData.index = value.index;
|
||||
}
|
||||
|
|
|
@ -219,3 +219,4 @@ skip-if = os == "mac"
|
|||
run-if = e10s
|
||||
[browser_async_window_flushing.js]
|
||||
[browser_forget_async_closings.js]
|
||||
[browser_sessionStoreContainer.js]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* 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/. */
|
||||
|
||||
function retrieveUserContextId(browser) {
|
||||
return ContentTask.spawn(browser, null, function* () {
|
||||
return docShell.userContextId;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function() {
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
let tab = gBrowser.addTab("about:blank");
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
yield promiseBrowserLoaded(browser);
|
||||
yield promiseTabState(tab, { userContextId: i, entries: [{ url: "http://example.com/" }] });
|
||||
|
||||
let userContextId = yield retrieveUserContextId(browser);
|
||||
is(userContextId, i, "The docShell has the correct userContextId");
|
||||
|
||||
yield promiseRemoveTab(tab);
|
||||
}
|
||||
});
|
|
@ -132,6 +132,7 @@ OriginAttributes::CreateSuffix(nsACString& aStr) const
|
|||
}
|
||||
|
||||
if (!mSignedPkg.IsEmpty()) {
|
||||
MOZ_RELEASE_ASSERT(mSignedPkg.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) == kNotFound);
|
||||
params->Set(NS_LITERAL_STRING("signedPkg"), mSignedPkg);
|
||||
}
|
||||
|
||||
|
|
|
@ -10072,6 +10072,11 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
}
|
||||
}
|
||||
|
||||
// If we're doing a history load, use its scroll restoration state.
|
||||
if (aSHEntry) {
|
||||
aSHEntry->GetScrollRestorationIsManual(&scrollRestorationIsManual);
|
||||
}
|
||||
|
||||
/* Assign mOSHE to mLSHE. This will either be a new entry created
|
||||
* by OnNewURI() for normal loads or aSHEntry for history loads.
|
||||
*/
|
||||
|
@ -13831,6 +13836,17 @@ nsDocShell::SetIsSignedPackage(const nsAString& aSignedPkg)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetUserContextId(uint32_t* aUserContextId)
|
||||
{
|
||||
if (!aUserContextId) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*aUserContextId = mUserContextId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetUserContextId(uint32_t aUserContextId)
|
||||
{
|
||||
|
@ -14181,55 +14197,8 @@ nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceReques
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class FetchEventDispatcher final : public nsIFetchEventDispatcher
|
||||
{
|
||||
public:
|
||||
FetchEventDispatcher(nsIInterceptedChannel* aChannel,
|
||||
nsIRunnable* aContinueRunnable)
|
||||
: mChannel(aChannel)
|
||||
, mContinueRunnable(aContinueRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIFETCHEVENTDISPATCHER
|
||||
|
||||
private:
|
||||
~FetchEventDispatcher()
|
||||
{
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInterceptedChannel> mChannel;
|
||||
nsCOMPtr<nsIRunnable> mContinueRunnable;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(FetchEventDispatcher, nsIFetchEventDispatcher)
|
||||
|
||||
NS_IMETHODIMP
|
||||
FetchEventDispatcher::Dispatch()
|
||||
{
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ErrorResult error;
|
||||
swm->DispatchPreparedFetchEvent(mChannel, mContinueRunnable, error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel,
|
||||
nsIFetchEventDispatcher** aFetchDispatcher)
|
||||
nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
|
||||
{
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
|
@ -14266,18 +14235,12 @@ nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel,
|
|||
attrs.InheritFromDocShellToDoc(GetOriginAttributes(), uri);
|
||||
|
||||
ErrorResult error;
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
swm->PrepareFetchEvent(attrs, doc, mInterceptedDocumentId, aChannel,
|
||||
isReload, isSubresourceLoad, error);
|
||||
swm->DispatchFetchEvent(attrs, doc, mInterceptedDocumentId, aChannel,
|
||||
isReload, isSubresourceLoad, error);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return error.StealNSResult();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(runnable);
|
||||
RefPtr<FetchEventDispatcher> dispatcher =
|
||||
new FetchEventDispatcher(aChannel, runnable);
|
||||
dispatcher.forget(aFetchDispatcher);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,7 +232,6 @@ public:
|
|||
NS_IMETHOD GetUseRemoteTabs(bool*) override;
|
||||
NS_IMETHOD SetRemoteTabs(bool) override;
|
||||
NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
|
||||
NS_IMETHOD SetUserContextId(uint32_t);
|
||||
|
||||
// Restores a cached presentation from history (mLSHE).
|
||||
// This method swaps out the content viewer and simulates loads for
|
||||
|
|
|
@ -43,7 +43,7 @@ interface nsITabParent;
|
|||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(811aa3e1-7c4d-45ae-89da-ea1b107c60ed)]
|
||||
[scriptable, builtinclass, uuid(258a8a33-219f-42f8-8fa8-f8f2dcd2358b)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
|
@ -1096,4 +1096,9 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
* @see https://html.spec.whatwg.org/#dom-history-scroll-restoration
|
||||
*/
|
||||
attribute boolean currentScrollRestorationIsManual;
|
||||
|
||||
/**
|
||||
* Sets/gets the user context ID for this docshell.
|
||||
*/
|
||||
attribute unsigned long userContextId;
|
||||
};
|
||||
|
|
|
@ -82,6 +82,15 @@
|
|||
opener.is(window.scrollY, 0, "Shouldn't have scrolled back to the state3's position");
|
||||
opener.is(history.state.state, "state3", "Unexpected state.");
|
||||
|
||||
history.pushState({ state: "state5" }, "state5");
|
||||
history.scrollRestoration = "auto";
|
||||
document.getElementById("bottom").scrollIntoView();
|
||||
opener.isnot(window.scrollY, 0, "Should have scrolled to 'bottom'.");
|
||||
history.back();
|
||||
window.scrollTo(0, 0);
|
||||
history.forward();
|
||||
opener.isnot(window.scrollY, 0, "Should have scrolled back to the state5's position");
|
||||
|
||||
var ifr = document.createElement("iframe");
|
||||
ifr.src = "data:text/html,";
|
||||
document.body.appendChild(ifr);
|
||||
|
|
|
@ -47,7 +47,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
|
|||
[test_popup-navigates-children.html]
|
||||
skip-if = buildapp == 'b2g' # b2g(Needs multiple window.open support, also uses docshelltreenode) b2g-debug(Needs multiple window.open support, also uses docshelltreenode) b2g-desktop(Needs multiple window.open support, also uses docshelltreenode)
|
||||
[test_reserved.html]
|
||||
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || android_version == '10' || android_version == '18' #too slow on Android 2.3 and 4.3 aws only; bug 1030403
|
||||
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || android_version == '10' || android_version == '18' || (e10s && debug && os == 'win') #too slow on Android 2.3 and 4.3 aws only; bug 1030403
|
||||
[test_sessionhistory.html]
|
||||
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || toolkit == 'android' #RANDOM # b2g-debug(Perma-orange on debug emulator builds) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
|
||||
[test_sibling-matching-parent.html]
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/* -*- 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 "AnimValuesStyleRule.h"
|
||||
#include "nsRuleData.h"
|
||||
#include "nsStyleContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(AnimValuesStyleRule, nsIStyleRule)
|
||||
|
||||
void
|
||||
AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
|
||||
{
|
||||
nsStyleContext *contextParent = aRuleData->mStyleContext->GetParent();
|
||||
if (contextParent && contextParent->HasPseudoElementData()) {
|
||||
// Don't apply transitions or animations to things inside of
|
||||
// pseudo-elements.
|
||||
// FIXME (Bug 522599): Add tests for this.
|
||||
|
||||
// Prevent structs from being cached on the rule node since we're inside
|
||||
// a pseudo-element, as we could determine cacheability differently
|
||||
// when walking the rule tree for a style context that is not inside
|
||||
// a pseudo-element. Note that nsRuleNode::GetStyle##name_ and GetStyleData
|
||||
// will never look at cached structs when we're animating things inside
|
||||
// a pseduo-element, so that we don't incorrectly return a struct that
|
||||
// is only appropriate for non-pseudo-elements.
|
||||
aRuleData->mConditions.SetUncacheable();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
|
||||
PropertyValuePair &cv = mPropertyValuePairs[i];
|
||||
if (aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(
|
||||
nsCSSProps::kSIDTable[cv.mProperty]))
|
||||
{
|
||||
nsCSSValue *prop = aRuleData->ValueFor(cv.mProperty);
|
||||
if (prop->GetUnit() == eCSSUnit_Null) {
|
||||
#ifdef DEBUG
|
||||
bool ok =
|
||||
#endif
|
||||
StyleAnimationValue::UncomputeValue(cv.mProperty, cv.mValue, *prop);
|
||||
MOZ_ASSERT(ok, "could not store computed value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AnimValuesStyleRule::MightMapInheritedStyleData()
|
||||
{
|
||||
return mStyleBits & NS_STYLE_INHERITED_STRUCT_MASK;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
AnimValuesStyleRule::List(FILE* out, int32_t aIndent) const
|
||||
{
|
||||
nsAutoCString str;
|
||||
for (int32_t index = aIndent; --index >= 0; ) {
|
||||
str.AppendLiteral(" ");
|
||||
}
|
||||
str.AppendLiteral("[anim values] { ");
|
||||
for (uint32_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
|
||||
const PropertyValuePair &pair = mPropertyValuePairs[i];
|
||||
str.Append(nsCSSProps::GetStringValue(pair.mProperty));
|
||||
str.AppendLiteral(": ");
|
||||
nsAutoString value;
|
||||
StyleAnimationValue::UncomputeValue(pair.mProperty, pair.mValue, value);
|
||||
AppendUTF16toUTF8(value, str);
|
||||
str.AppendLiteral("; ");
|
||||
}
|
||||
str.AppendLiteral("}\n");
|
||||
fprintf_stderr(out, "%s", str.get());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,79 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_AnimValuesStyleRule_h
|
||||
#define mozilla_AnimValuesStyleRule_h
|
||||
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "nsCSSProperty.h"
|
||||
#include "nsCSSPropertySet.h"
|
||||
#include "nsIStyleRule.h"
|
||||
#include "nsISupportsImpl.h" // For NS_DECL_ISUPPORTS
|
||||
#include "nsRuleNode.h" // For nsCachedStyleData
|
||||
#include "nsTArray.h" // For nsTArray
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* A style rule that maps property-StyleAnimationValue pairs.
|
||||
*/
|
||||
class AnimValuesStyleRule final : public nsIStyleRule
|
||||
{
|
||||
public:
|
||||
AnimValuesStyleRule()
|
||||
: mStyleBits(0) {}
|
||||
|
||||
// nsISupports implementation
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsIStyleRule implementation
|
||||
void MapRuleInfoInto(nsRuleData* aRuleData) override;
|
||||
bool MightMapInheritedStyleData() override;
|
||||
#ifdef DEBUG
|
||||
void List(FILE* out = stdout, int32_t aIndent = 0) const override;
|
||||
#endif
|
||||
|
||||
void AddValue(nsCSSProperty aProperty, StyleAnimationValue &aStartValue)
|
||||
{
|
||||
PropertyValuePair v = { aProperty, aStartValue };
|
||||
mPropertyValuePairs.AppendElement(v);
|
||||
mStyleBits |=
|
||||
nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]);
|
||||
}
|
||||
|
||||
// Caller must fill in returned value.
|
||||
StyleAnimationValue* AddEmptyValue(nsCSSProperty aProperty)
|
||||
{
|
||||
PropertyValuePair *p = mPropertyValuePairs.AppendElement();
|
||||
p->mProperty = aProperty;
|
||||
mStyleBits |=
|
||||
nsCachedStyleData::GetBitForSID(nsCSSProps::kSIDTable[aProperty]);
|
||||
return &p->mValue;
|
||||
}
|
||||
|
||||
struct PropertyValuePair {
|
||||
nsCSSProperty mProperty;
|
||||
StyleAnimationValue mValue;
|
||||
};
|
||||
|
||||
void AddPropertiesToSet(nsCSSPropertySet& aSet) const
|
||||
{
|
||||
for (size_t i = 0, i_end = mPropertyValuePairs.Length(); i < i_end; ++i) {
|
||||
const PropertyValuePair &cv = mPropertyValuePairs[i];
|
||||
aSet.AddProperty(cv.mProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
~AnimValuesStyleRule() {}
|
||||
|
||||
InfallibleTArray<PropertyValuePair> mPropertyValuePairs;
|
||||
uint32_t mStyleBits;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_AnimValuesStyleRule_h
|
|
@ -477,14 +477,12 @@ Animation::Tick()
|
|||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
|
||||
// FIXME: Detect the no-change case and don't request a restyle at all
|
||||
// FIXME: Detect changes to IsPlaying() state and request RestyleType::Layer
|
||||
// so that layers get updated immediately
|
||||
AnimationCollection* collection = GetCollection();
|
||||
if (collection) {
|
||||
collection->RequestRestyle(CanThrottle() ?
|
||||
AnimationCollection::RestyleType::Throttled :
|
||||
AnimationCollection::RestyleType::Standard);
|
||||
// Update layers if we are newly finished.
|
||||
if (mEffect &&
|
||||
!mEffect->Properties().IsEmpty() &&
|
||||
!mFinishedAtLastComposeStyle &&
|
||||
PlayState() == AnimationPlayState::Finished) {
|
||||
PostUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -673,41 +671,6 @@ Animation::HasLowerCompositeOrderThan(const Animation& aOther) const
|
|||
return mAnimationIndex < aOther.mAnimationIndex;
|
||||
}
|
||||
|
||||
bool
|
||||
Animation::CanThrottle() const
|
||||
{
|
||||
// This method answers the question, "Can we get away with NOT updating
|
||||
// style on the main thread for this animation on this tick?"
|
||||
|
||||
// Ignore animations that were never going to have any effect anyway.
|
||||
if (!mEffect || mEffect->Properties().IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finished animations can be throttled unless this is the first
|
||||
// sample since finishing. In that case we need an unthrottled sample
|
||||
// so we can apply the correct end-of-animation behavior on the main
|
||||
// thread (either removing the animation style or applying the fill mode).
|
||||
if (PlayState() == AnimationPlayState::Finished) {
|
||||
return mFinishedAtLastComposeStyle;
|
||||
}
|
||||
|
||||
// We should also ignore animations which are not "in effect"--i.e. not
|
||||
// producing an output. This includes animations that are idle or in their
|
||||
// delay phase but with no backwards fill.
|
||||
//
|
||||
// Note that unlike newly-finished animations, we don't need to worry about
|
||||
// special handling for newly-idle animations or animations that are newly
|
||||
// yet-to-start since any operation that would cause that change (e.g. a call
|
||||
// to cancel() on the animation, or seeking its current time) will trigger an
|
||||
// unthrottled sample.
|
||||
if (!IsInEffect()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mEffect->CanThrottle();
|
||||
}
|
||||
|
||||
void
|
||||
Animation::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
nsCSSPropertySet& aSetProperties,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nsWrapperCache.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
|
||||
#include "mozilla/dom/AnimationBinding.h" // for AnimationPlayState
|
||||
|
@ -285,6 +286,15 @@ public:
|
|||
*/
|
||||
virtual bool HasLowerCompositeOrderThan(const Animation& aOther) const;
|
||||
|
||||
/**
|
||||
* Returns the level at which the effect(s) associated with this Animation
|
||||
* are applied to the CSS cascade.
|
||||
*/
|
||||
virtual EffectCompositor::CascadeLevel CascadeLevel() const
|
||||
{
|
||||
return EffectCompositor::CascadeLevel::Animations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this animation does not currently need to update
|
||||
* style on the main thread (e.g. because it is empty, or is
|
||||
|
|
|
@ -7,12 +7,24 @@
|
|||
#include "EffectCompositor.h"
|
||||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/KeyframeEffect.h" // For KeyframeEffectReadOnly
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/EffectSet.h"
|
||||
#include "mozilla/LayerAnimationInfo.h"
|
||||
#include "AnimationCommon.h" // For AnimationCollection
|
||||
#include "nsAnimationManager.h"
|
||||
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
|
||||
#include "nsCSSPropertySet.h"
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation
|
||||
#include "nsTArray.h"
|
||||
#include "nsTransitionManager.h"
|
||||
|
||||
using mozilla::dom::Animation;
|
||||
using mozilla::dom::Element;
|
||||
using mozilla::dom::KeyframeEffectReadOnly;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -40,6 +52,21 @@ FindAnimationsForCompositor(const nsIFrame* aFrame,
|
|||
return false;
|
||||
}
|
||||
|
||||
// The animation cascade will almost always be up-to-date by this point
|
||||
// but there are some cases such as when we are restoring the refresh driver
|
||||
// from test control after seeking where it might not be the case.
|
||||
//
|
||||
// Those cases are probably not important but just to be safe, let's make
|
||||
// sure the cascade is up to date since if it *is* up to date, this is
|
||||
// basically a no-op.
|
||||
Maybe<Pair<dom::Element*, nsCSSPseudoElements::Type>> pseudoElement =
|
||||
EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
|
||||
if (pseudoElement) {
|
||||
EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->first(),
|
||||
pseudoElement->second(),
|
||||
aFrame->StyleContext());
|
||||
}
|
||||
|
||||
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
nsCString message;
|
||||
|
@ -104,4 +131,288 @@ EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
|||
return result;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::MaybeUpdateCascadeResults(Element* aElement,
|
||||
nsCSSPseudoElements::Type
|
||||
aPseudoType,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
||||
if (!effects || !effects->CascadeNeedsUpdate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
|
||||
|
||||
MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
|
||||
}
|
||||
|
||||
namespace {
|
||||
class EffectCompositeOrderComparator {
|
||||
public:
|
||||
bool Equals(const KeyframeEffectReadOnly* a,
|
||||
const KeyframeEffectReadOnly* b) const
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
bool LessThan(const KeyframeEffectReadOnly* a,
|
||||
const KeyframeEffectReadOnly* b) const
|
||||
{
|
||||
MOZ_ASSERT(a->GetAnimation() && b->GetAnimation());
|
||||
MOZ_ASSERT(
|
||||
Equals(a, b) ||
|
||||
a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation()) !=
|
||||
b->GetAnimation()->HasLowerCompositeOrderThan(*a->GetAnimation()));
|
||||
return a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::UpdateCascadeResults(Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
||||
if (!effects) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
|
||||
}
|
||||
|
||||
/* static */ Maybe<Pair<Element*, nsCSSPseudoElements::Type>>
|
||||
EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
|
||||
{
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
Maybe<Pair<Element*, nsCSSPseudoElements::Type>> result;
|
||||
|
||||
nsIContent* content = aFrame->GetContent();
|
||||
if (!content) {
|
||||
return result;
|
||||
}
|
||||
|
||||
nsCSSPseudoElements::Type pseudoType =
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement;
|
||||
|
||||
if (aFrame->IsGeneratedContentFrame()) {
|
||||
nsIFrame* parent = aFrame->GetParent();
|
||||
if (parent->IsGeneratedContentFrame()) {
|
||||
return result;
|
||||
}
|
||||
nsIAtom* name = content->NodeInfo()->NameAtom();
|
||||
if (name == nsGkAtoms::mozgeneratedcontentbefore) {
|
||||
pseudoType = nsCSSPseudoElements::ePseudo_before;
|
||||
} else if (name == nsGkAtoms::mozgeneratedcontentafter) {
|
||||
pseudoType = nsCSSPseudoElements::ePseudo_after;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
content = content->GetParent();
|
||||
if (!content) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!content->IsElement()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = Some(MakePair(content->AsElement(), pseudoType));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::ComposeAnimationRule(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
CascadeLevel aCascadeLevel,
|
||||
bool& aStyleChanging)
|
||||
{
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
||||
if (!effects) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The caller is responsible for calling MaybeUpdateCascadeResults first.
|
||||
MOZ_ASSERT(!effects->CascadeNeedsUpdate(),
|
||||
"Animation cascade out of date when composing animation rule");
|
||||
|
||||
// Get a list of effects for the current level sorted by composite order.
|
||||
nsTArray<KeyframeEffectReadOnly*> sortedEffectList;
|
||||
for (KeyframeEffectReadOnly* effect : *effects) {
|
||||
MOZ_ASSERT(effect->GetAnimation());
|
||||
if (effect->GetAnimation()->CascadeLevel() == aCascadeLevel) {
|
||||
sortedEffectList.AppendElement(effect);
|
||||
}
|
||||
}
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
RefPtr<AnimValuesStyleRule>& animationRule =
|
||||
effects->AnimationRule(aCascadeLevel);
|
||||
animationRule = nullptr;
|
||||
|
||||
// We'll set aStyleChanging to true below if necessary.
|
||||
aStyleChanging = false;
|
||||
|
||||
// If multiple animations specify behavior for the same property the
|
||||
// animation with the *highest* composite order wins.
|
||||
// As a result, we iterate from last animation to first and, if a
|
||||
// property has already been set, we don't change it.
|
||||
nsCSSPropertySet properties;
|
||||
|
||||
for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) {
|
||||
effect->GetAnimation()->ComposeStyle(animationRule, properties,
|
||||
aStyleChanging);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::GetOverriddenProperties(nsStyleContext* aStyleContext,
|
||||
EffectSet& aEffectSet,
|
||||
nsCSSPropertySet&
|
||||
aPropertiesOverridden)
|
||||
{
|
||||
nsAutoTArray<nsCSSProperty, LayerAnimationInfo::kRecords> propertiesToTrack;
|
||||
{
|
||||
nsCSSPropertySet propertiesToTrackAsSet;
|
||||
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
||||
for (const AnimationProperty& property : effect->Properties()) {
|
||||
if (nsCSSProps::PropHasFlags(property.mProperty,
|
||||
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
|
||||
!propertiesToTrackAsSet.HasProperty(property.mProperty)) {
|
||||
propertiesToTrackAsSet.AddProperty(property.mProperty);
|
||||
propertiesToTrack.AppendElement(property.mProperty);
|
||||
}
|
||||
}
|
||||
// Skip iterating over the rest of the effects if we've already
|
||||
// found all the compositor-animatable properties.
|
||||
if (propertiesToTrack.Length() == LayerAnimationInfo::kRecords) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesToTrack.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
|
||||
aStyleContext,
|
||||
aPropertiesOverridden);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
|
||||
Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext)
|
||||
{
|
||||
MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet,
|
||||
"Effect set should correspond to the specified (pseudo-)element");
|
||||
if (aEffectSet.IsEmpty()) {
|
||||
aEffectSet.MarkCascadeUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a list of effects sorted by composite order.
|
||||
nsTArray<KeyframeEffectReadOnly*> sortedEffectList;
|
||||
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
||||
sortedEffectList.AppendElement(effect);
|
||||
}
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
// Get properties that override the *animations* level of the cascade.
|
||||
//
|
||||
// We only do this for properties that we can animate on the compositor
|
||||
// since we will apply other properties on the main thread where the usual
|
||||
// cascade applies.
|
||||
nsCSSPropertySet overriddenProperties;
|
||||
if (aStyleContext) {
|
||||
GetOverriddenProperties(aStyleContext, aEffectSet, overriddenProperties);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
nsCSSPropertySet animatedProperties;
|
||||
|
||||
// Iterate from highest to lowest composite order.
|
||||
for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) {
|
||||
MOZ_ASSERT(effect->GetAnimation(),
|
||||
"Effects on a target element should have an Animation");
|
||||
bool inEffect = effect->IsInEffect();
|
||||
for (AnimationProperty& prop : effect->Properties()) {
|
||||
|
||||
bool winsInCascade = !animatedProperties.HasProperty(prop.mProperty) &&
|
||||
inEffect;
|
||||
|
||||
// If this property wins in the cascade, add it to the set of animated
|
||||
// properties. We need to do this even if the property is overridden
|
||||
// (in which case we set winsInCascade to false below) since we don't
|
||||
// want to fire transitions on these properties.
|
||||
if (winsInCascade) {
|
||||
animatedProperties.AddProperty(prop.mProperty);
|
||||
}
|
||||
|
||||
// For effects that will be applied to the animations level of the
|
||||
// cascade, we need to check that the property isn't being set by
|
||||
// something with higher priority in the cascade.
|
||||
//
|
||||
// We only do this, however, for properties that can be animated on
|
||||
// the compositor. For properties animated on the main thread the usual
|
||||
// cascade ensures these animations will be correctly overridden.
|
||||
if (winsInCascade &&
|
||||
effect->GetAnimation()->CascadeLevel() == CascadeLevel::Animations &&
|
||||
overriddenProperties.HasProperty(prop.mProperty)) {
|
||||
winsInCascade = false;
|
||||
}
|
||||
|
||||
if (winsInCascade != prop.mWinsInCascade) {
|
||||
changed = true;
|
||||
}
|
||||
prop.mWinsInCascade = winsInCascade;
|
||||
}
|
||||
}
|
||||
|
||||
aEffectSet.MarkCascadeUpdated();
|
||||
|
||||
// If there is any change in the cascade result, update animations on
|
||||
// layers with the winning animations.
|
||||
nsPresContext* presContext = GetPresContext(aElement);
|
||||
if (changed && presContext) {
|
||||
// We currently unconditionally update both animations and transitions
|
||||
// even if we could, for example, get away with only updating animations.
|
||||
// This is a temporary measure until we unify all animation style updating
|
||||
// under EffectCompositor.
|
||||
AnimationCollection* animations =
|
||||
presContext->AnimationManager()->GetAnimationCollection(aElement,
|
||||
aPseudoType,
|
||||
false);
|
||||
/* don't create */
|
||||
if (animations) {
|
||||
animations->RequestRestyle(AnimationCollection::RestyleType::Layer);
|
||||
}
|
||||
|
||||
AnimationCollection* transitions =
|
||||
presContext->TransitionManager()->GetAnimationCollection(aElement,
|
||||
aPseudoType,
|
||||
false);
|
||||
/* don't create */
|
||||
if (transitions) {
|
||||
transitions->RequestRestyle(AnimationCollection::RestyleType::Layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ nsPresContext*
|
||||
EffectCompositor::GetPresContext(Element* aElement)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
nsIPresShell* shell = nsComputedDOMStyle::GetPresShellForContent(aElement);
|
||||
if (!shell) {
|
||||
return nullptr;
|
||||
}
|
||||
return shell->GetPresContext();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,24 +7,109 @@
|
|||
#ifndef mozilla_EffectCompositor_h
|
||||
#define mozilla_EffectCompositor_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Pair.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCSSProperty.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsCSSPropertySet;
|
||||
class nsIFrame;
|
||||
class nsPresContext;
|
||||
class nsStyleContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class EffectSet;
|
||||
|
||||
namespace dom {
|
||||
class Animation;
|
||||
class Element;
|
||||
}
|
||||
|
||||
class EffectCompositor
|
||||
{
|
||||
public:
|
||||
// Animations can be applied at two different levels in the CSS cascade:
|
||||
enum class CascadeLevel {
|
||||
// The animations sheet (CSS animations, script-generated animations,
|
||||
// and CSS transitions that are no longer tied to CSS markup)
|
||||
Animations,
|
||||
// The transitions sheet (CSS transitions that are tied to CSS markup)
|
||||
Transitions
|
||||
};
|
||||
// We don't define this as part of CascadeLevel as then we'd have to add
|
||||
// explicit checks for the Count enum value everywhere CascadeLevel is used.
|
||||
static const size_t kCascadeLevelCount =
|
||||
static_cast<size_t>(CascadeLevel::Transitions) + 1;
|
||||
|
||||
static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty);
|
||||
|
||||
static nsTArray<RefPtr<dom::Animation>>
|
||||
GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty);
|
||||
|
||||
|
||||
// Update animation cascade results for the specified (pseudo-)element
|
||||
// but only if we have marked the cascade as needing an update due a
|
||||
// the change in the set of effects or a change in one of the effects'
|
||||
// "in effect" state.
|
||||
//
|
||||
// This method does NOT detect if other styles that apply above the
|
||||
// animation level of the cascade have changed.
|
||||
static void
|
||||
MaybeUpdateCascadeResults(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
// Update the mWinsInCascade member for each property in effects targetting
|
||||
// the specified (pseudo-)element.
|
||||
//
|
||||
// This can be expensive so we should only call it if styles that apply
|
||||
// above the animation level of the cascade might have changed. For all
|
||||
// other cases we should call MaybeUpdateCascadeResults.
|
||||
static void
|
||||
UpdateCascadeResults(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
// Helper to fetch the corresponding element and pseudo-type from a frame.
|
||||
//
|
||||
// For frames corresponding to pseudo-elements, the returned element is the
|
||||
// element on which we store the animations (i.e. the EffectSet and/or
|
||||
// AnimationCollection), *not* the generated content.
|
||||
//
|
||||
// Returns an empty result when a suitable element cannot be found including
|
||||
// when the frame represents a pseudo-element on which we do not support
|
||||
// animations.
|
||||
static Maybe<Pair<dom::Element*, nsCSSPseudoElements::Type>>
|
||||
GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame);
|
||||
|
||||
// Rebuilds the animation rule corresponding to |aCascadeLevel| on the
|
||||
// EffectSet associated with the specified (pseudo-)element.
|
||||
static void ComposeAnimationRule(dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
CascadeLevel aCascadeLevel,
|
||||
bool& aStyleChanging);
|
||||
|
||||
private:
|
||||
// Get the properties in |aEffectSet| that we are able to animate on the
|
||||
// compositor but which are also specified at a higher level in the cascade
|
||||
// than the animations level in |aStyleContext|.
|
||||
static void
|
||||
GetOverriddenProperties(nsStyleContext* aStyleContext,
|
||||
EffectSet& aEffectSet,
|
||||
nsCSSPropertySet& aPropertiesOverridden);
|
||||
|
||||
static void
|
||||
UpdateCascadeResults(EffectSet& aEffectSet,
|
||||
dom::Element* aElement,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsStyleContext* aStyleContext);
|
||||
|
||||
static nsPresContext* GetPresContext(dom::Element* aElement);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
#include "EffectSet.h"
|
||||
#include "mozilla/dom/Element.h" // For Element
|
||||
#include "RestyleManager.h"
|
||||
#include "nsCycleCollectionNoteChild.h" // For CycleCollectionNoteChild
|
||||
#include "nsPresContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -106,6 +108,13 @@ EffectSet::GetOrCreateEffectSet(dom::Element* aElement,
|
|||
return effectSet;
|
||||
}
|
||||
|
||||
void
|
||||
EffectSet::UpdateAnimationGeneration(nsPresContext* aPresContext)
|
||||
{
|
||||
mAnimationGeneration =
|
||||
aPresContext->RestyleManager()->GetAnimationGeneration();
|
||||
}
|
||||
|
||||
/* static */ nsIAtom**
|
||||
EffectSet::GetEffectSetPropertyAtoms()
|
||||
{
|
||||
|
@ -143,13 +152,23 @@ EffectSet::GetEffectSetPropertyAtom(nsCSSPseudoElements::Type aPseudoType)
|
|||
void
|
||||
EffectSet::AddEffect(dom::KeyframeEffectReadOnly& aEffect)
|
||||
{
|
||||
if (mEffects.Contains(&aEffect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEffects.PutEntry(&aEffect);
|
||||
MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
void
|
||||
EffectSet::RemoveEffect(dom::KeyframeEffectReadOnly& aEffect)
|
||||
{
|
||||
if (!mEffects.Contains(&aEffect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEffects.RemoveEntry(&aEffect);
|
||||
MarkCascadeNeedsUpdate();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
#ifndef mozilla_EffectSet_h
|
||||
#define mozilla_EffectSet_h
|
||||
|
||||
#include "mozilla/AnimValuesStyleRule.h"
|
||||
#include "mozilla/EffectCompositor.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "nsCSSPseudoElements.h" // For nsCSSPseudoElements::Type
|
||||
#include "nsHashKeys.h" // For nsPtrHashKey
|
||||
#include "nsTHashtable.h" // For nsTHashtable
|
||||
|
||||
class nsPresContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
@ -24,8 +29,10 @@ class EffectSet
|
|||
{
|
||||
public:
|
||||
EffectSet()
|
||||
: mCascadeNeedsUpdate(false)
|
||||
, mAnimationGeneration(0)
|
||||
#ifdef DEBUG
|
||||
: mCalledPropertyDtor(false)
|
||||
, mCalledPropertyDtor(false)
|
||||
#endif
|
||||
{
|
||||
MOZ_COUNT_CTOR(EffectSet);
|
||||
|
@ -120,6 +127,19 @@ public:
|
|||
}
|
||||
bool IsEmpty() const { return mEffects.IsEmpty(); }
|
||||
|
||||
RefPtr<AnimValuesStyleRule>& AnimationRule(EffectCompositor::CascadeLevel
|
||||
aCascadeLevel)
|
||||
{
|
||||
return mAnimationRule[aCascadeLevel];
|
||||
}
|
||||
|
||||
bool CascadeNeedsUpdate() const { return mCascadeNeedsUpdate; }
|
||||
void MarkCascadeNeedsUpdate() { mCascadeNeedsUpdate = true; }
|
||||
void MarkCascadeUpdated() { mCascadeNeedsUpdate = false; }
|
||||
|
||||
void UpdateAnimationGeneration(nsPresContext* aPresContext);
|
||||
uint64_t GetAnimationGeneration() const { return mAnimationGeneration; }
|
||||
|
||||
static nsIAtom** GetEffectSetPropertyAtoms();
|
||||
|
||||
private:
|
||||
|
@ -128,6 +148,31 @@ private:
|
|||
|
||||
OwningEffectSet mEffects;
|
||||
|
||||
// These style rules contain the style data for currently animating
|
||||
// values. They only match when styling with animation. When we
|
||||
// style without animation, we need to not use them so that we can
|
||||
// detect any new changes; if necessary we restyle immediately
|
||||
// afterwards with animation.
|
||||
EnumeratedArray<EffectCompositor::CascadeLevel,
|
||||
EffectCompositor::CascadeLevel(
|
||||
EffectCompositor::kCascadeLevelCount),
|
||||
RefPtr<AnimValuesStyleRule>> mAnimationRule;
|
||||
|
||||
// Dirty flag to represent when the mWinsInCascade flag on effects in
|
||||
// this set might need to be updated.
|
||||
//
|
||||
// Set to true any time the set of effects is changed or when
|
||||
// one the effects goes in or out of the "in effect" state.
|
||||
bool mCascadeNeedsUpdate;
|
||||
|
||||
// RestyleManager keeps track of the number of animation restyles.
|
||||
// 'mini-flushes' (see nsTransitionManager::UpdateAllThrottledStyles()).
|
||||
// mAnimationGeneration is the sequence number of the last flush where a
|
||||
// transition/animation changed. We keep a similar count on the
|
||||
// corresponding layer so we can check that the layer is up to date with
|
||||
// the animation manager.
|
||||
uint64_t mAnimationGeneration;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mCalledPropertyDtor;
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
|
||||
#include "nsCSSValue.h"
|
||||
#include "nsStyleUtil.h"
|
||||
#include <algorithm> // std::max
|
||||
#include <algorithm> // For std::max
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -94,6 +94,7 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
|||
, mTarget(aTarget)
|
||||
, mTiming(aTiming)
|
||||
, mPseudoType(aPseudoType)
|
||||
, mInEffectOnLastAnimationTimingUpdate(false)
|
||||
{
|
||||
MOZ_ASSERT(aTarget, "null animation target is not yet supported");
|
||||
}
|
||||
|
@ -132,6 +133,56 @@ KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming)
|
|||
// update our registration with the target element.
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
||||
{
|
||||
UpdateTargetRegistration();
|
||||
|
||||
// If the effect is not relevant it will be removed from the target
|
||||
// element's effect set. However, effects not in the effect set
|
||||
// will not be included in the set of candidate effects for running on
|
||||
// the compositor and hence they won't have their compositor status
|
||||
// updated. As a result, we need to make sure we clear their compositor
|
||||
// status here.
|
||||
bool isRelevant = mAnimation && mAnimation->IsRelevant();
|
||||
if (!isRelevant) {
|
||||
ResetIsRunningOnCompositor();
|
||||
}
|
||||
|
||||
// Detect changes to "in effect" status since we need to recalculate the
|
||||
// animation cascade for this element whenever that changes.
|
||||
bool inEffect = IsInEffect();
|
||||
if (inEffect != mInEffectOnLastAnimationTimingUpdate) {
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
|
||||
if (effectSet) {
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
}
|
||||
mInEffectOnLastAnimationTimingUpdate = inEffect;
|
||||
}
|
||||
|
||||
// Request restyle if necessary.
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
AnimationCollection* collection = GetCollection();
|
||||
// Bug 1235002: We should skip requesting a restyle when mProperties is empty.
|
||||
// However, currently we don't properly encapsulate mProperties so we can't
|
||||
// detect when it changes. As a result, if we skip requesting restyles when
|
||||
// mProperties is empty and we play an animation and *then* add properties to
|
||||
// it (as we currently do when building CSS animations), we will fail to
|
||||
// request a restyle at all. Since animations without properties are rare, we
|
||||
// currently just request the restyle regardless of whether mProperties is
|
||||
// empty or not.
|
||||
if (collection &&
|
||||
// Bug 1216843: When we implement iteration composite modes, we need to
|
||||
// also detect if the current iteration has changed.
|
||||
computedTiming.mProgress != mProgressOnLastCompose) {
|
||||
collection->RequestRestyle(CanThrottle() ?
|
||||
AnimationCollection::RestyleType::Throttled :
|
||||
AnimationCollection::RestyleType::Standard);
|
||||
}
|
||||
}
|
||||
|
||||
Nullable<TimeDuration>
|
||||
KeyframeEffectReadOnly::GetLocalTime() const
|
||||
{
|
||||
|
@ -376,6 +427,7 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
|||
nsCSSPropertySet& aSetProperties)
|
||||
{
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
mProgressOnLastCompose = computedTiming.mProgress;
|
||||
|
||||
// If the progress is null, we don't have fill data for the current
|
||||
// time so we shouldn't animate.
|
||||
|
@ -533,10 +585,6 @@ KeyframeEffectReadOnly::UpdateTargetRegistration()
|
|||
if (effectSet) {
|
||||
effectSet->RemoveEffect(*this);
|
||||
}
|
||||
// Any effects not in the effect set will not be included in the set of
|
||||
// candidate effects for running on the compositor and hence they won't
|
||||
// have their compositor status updated so we should do that now.
|
||||
ResetIsRunningOnCompositor();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1776,15 +1824,14 @@ KeyframeEffectReadOnly::OverflowRegionRefreshInterval()
|
|||
bool
|
||||
KeyframeEffectReadOnly::CanThrottle() const
|
||||
{
|
||||
// Animation::CanThrottle checks for not in effect animations
|
||||
// before calling this.
|
||||
MOZ_ASSERT(IsInEffect(), "Effect should be in effect");
|
||||
|
||||
// Unthrottle if this animation is not current (i.e. it has passed the end).
|
||||
// In future we may be able to throttle this case too, but we should only get
|
||||
// occasional ticks while the animation is in this state so it doesn't matter
|
||||
// too much.
|
||||
if (!IsCurrent()) {
|
||||
// Unthrottle if we are not in effect or current. This will be the case when
|
||||
// our owning animation has finished, is idle, or when we are in the delay
|
||||
// phase (but without a backwards fill). In each case the computed progress
|
||||
// value produced on each tick will be the same so we will skip requesting
|
||||
// unnecessary restyles in NotifyAnimationTimingUpdated. Any calls we *do* get
|
||||
// here will be because of a change in state (e.g. we are newly finished or
|
||||
// newly no longer in effect) in which case we shouldn't throttle the sample.
|
||||
if (!IsInEffect() || !IsCurrent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1812,14 +1859,15 @@ KeyframeEffectReadOnly::CanThrottle() const
|
|||
continue;
|
||||
}
|
||||
|
||||
AnimationCollection* collection = GetCollection();
|
||||
MOZ_ASSERT(collection,
|
||||
"CanThrottle should be called on an effect associated with an animation");
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget, mPseudoType);
|
||||
MOZ_ASSERT(effectSet, "CanThrottle should be called on an effect "
|
||||
"associated with a target element");
|
||||
layers::Layer* layer =
|
||||
FrameLayerBuilder::GetDedicatedLayer(frame, record.mLayerType);
|
||||
// Unthrottle if the layer needs to be brought up to date with the animation.
|
||||
// Unthrottle if the layer needs to be brought up to date
|
||||
if (!layer ||
|
||||
collection->mAnimationGeneration > layer->GetAnimationGeneration()) {
|
||||
effectSet->GetAnimationGeneration() !=
|
||||
layer->GetAnimationGeneration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,10 +131,7 @@ struct AnimationProperty
|
|||
// 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.
|
||||
// **NOTE**: For CSS animations, we only bother setting mWinsInCascade
|
||||
// accurately for properties that we can animate on the compositor.
|
||||
// For other properties, we make it always be true.
|
||||
// **NOTE 2**: This member is not included when comparing AnimationProperty
|
||||
// **NOTE**: This member is not included when comparing AnimationProperty
|
||||
// objects for equality.
|
||||
bool mWinsInCascade = true;
|
||||
|
||||
|
@ -229,7 +226,7 @@ public:
|
|||
const AnimationTiming& Timing() const { return mTiming; }
|
||||
AnimationTiming& Timing() { return mTiming; }
|
||||
void SetTiming(const AnimationTiming& aTiming);
|
||||
void NotifyAnimationTimingUpdated() { UpdateTargetRegistration(); }
|
||||
void NotifyAnimationTimingUpdated();
|
||||
|
||||
Nullable<TimeDuration> GetLocalTime() const;
|
||||
|
||||
|
@ -293,8 +290,6 @@ public:
|
|||
bool IsRunningOnCompositor() const;
|
||||
void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning);
|
||||
|
||||
bool CanThrottle() const;
|
||||
|
||||
// Returns true if this effect, applied to |aFrame|, contains
|
||||
// properties that mean we shouldn't run *any* compositor animations on this
|
||||
// element.
|
||||
|
@ -349,9 +344,19 @@ protected:
|
|||
|
||||
InfallibleTArray<AnimationProperty> mProperties;
|
||||
|
||||
// The computed progress last time we composed the style rule. This is
|
||||
// used to detect when the progress is not changing (e.g. due to a step
|
||||
// timing function) so we can avoid unnecessary style updates.
|
||||
Nullable<double> mProgressOnLastCompose;
|
||||
|
||||
// We need to track when we go to or from being "in effect" since
|
||||
// we need to re-evaluate the cascade of animations when that changes.
|
||||
bool mInEffectOnLastAnimationTimingUpdate;
|
||||
|
||||
private:
|
||||
nsIFrame* GetAnimationFrame() const;
|
||||
|
||||
bool CanThrottle() const;
|
||||
bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
|
||||
|
||||
// Returns true unless Gecko limitations prevent performing transform
|
||||
|
|
|
@ -18,6 +18,7 @@ EXPORTS.mozilla.dom += [
|
|||
EXPORTS.mozilla += [
|
||||
'AnimationComparator.h',
|
||||
'AnimationUtils.h',
|
||||
'AnimValuesStyleRule.h',
|
||||
'ComputedTimingFunction.h',
|
||||
'EffectCompositor.h',
|
||||
'EffectSet.h',
|
||||
|
@ -29,6 +30,7 @@ UNIFIED_SOURCES += [
|
|||
'AnimationEffectReadOnly.cpp',
|
||||
'AnimationTimeline.cpp',
|
||||
'AnimationUtils.cpp',
|
||||
'AnimValuesStyleRule.cpp',
|
||||
'ComputedTimingFunction.cpp',
|
||||
'DocumentTimeline.cpp',
|
||||
'EffectCompositor.cpp',
|
||||
|
@ -39,6 +41,8 @@ UNIFIED_SOURCES += [
|
|||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/layout/base',
|
||||
'/layout/style',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
|
|
@ -80,6 +80,12 @@ this.UserCustomizations = {
|
|||
},
|
||||
|
||||
isFromExtension: function(aURI) {
|
||||
if (!aURI && Services.prefs.getBoolPref("webextensions.tests")) {
|
||||
// That's the case in mochitests because of the packaging setup:
|
||||
// aURI is expected to be the appURI from the jarChannel but there is
|
||||
// no real app associated to mochitest's jar:remoteopenfile:/// uris.
|
||||
return true;
|
||||
}
|
||||
return this.appId.has(aURI.host);
|
||||
},
|
||||
|
||||
|
|
|
@ -700,9 +700,9 @@ public:
|
|||
// (on element that have status pointer capture override
|
||||
// or on element that have status pending pointer capture)
|
||||
if (pointerCaptureInfo->mOverrideContent == this) {
|
||||
nsIPresShell::ReleasePointerCapturingContent(aPointerId, this);
|
||||
nsIPresShell::ReleasePointerCapturingContent(aPointerId);
|
||||
} else if (pointerCaptureInfo->mPendingContent == this) {
|
||||
nsIPresShell::ReleasePointerCapturingContent(aPointerId, this);
|
||||
nsIPresShell::ReleasePointerCapturingContent(aPointerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -742,8 +742,7 @@ nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
|
|||
}
|
||||
|
||||
void
|
||||
nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
|
||||
CSSStyleSheet* aStyleSheet,
|
||||
nsDOMStyleSheetList::StyleSheetAdded(CSSStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet)
|
||||
{
|
||||
if (aDocumentSheet && -1 != mLength) {
|
||||
|
@ -752,8 +751,7 @@ nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
|
|||
}
|
||||
|
||||
void
|
||||
nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
|
||||
CSSStyleSheet* aStyleSheet,
|
||||
nsDOMStyleSheetList::StyleSheetRemoved(CSSStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet)
|
||||
{
|
||||
if (aDocumentSheet && -1 != mLength) {
|
||||
|
@ -3666,14 +3664,12 @@ nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
|
|||
// Don't add anything here. Add it to |doCreateShell| instead.
|
||||
// This exists so that subclasses can pass other values for the 4th
|
||||
// parameter some of the time.
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet,
|
||||
eCompatibility_FullStandards);
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet);
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPresShell>
|
||||
nsDocument::doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager, nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode)
|
||||
nsViewManager* aViewManager, nsStyleSet* aStyleSet)
|
||||
{
|
||||
NS_ASSERTION(!mPresShell, "We have a presshell already!");
|
||||
|
||||
|
@ -3682,7 +3678,7 @@ nsDocument::doCreateShell(nsPresContext* aContext,
|
|||
FillStyleSet(aStyleSet);
|
||||
|
||||
RefPtr<PresShell> shell = new PresShell;
|
||||
shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
|
||||
shell->Init(this, aContext, aViewManager, aStyleSet);
|
||||
|
||||
// Note: we don't hold a ref to the shell (it holds a ref to us)
|
||||
mPresShell = shell;
|
||||
|
@ -4075,7 +4071,7 @@ nsDocument::AddStyleSheetToStyleSets(CSSStyleSheet* aSheet)
|
|||
void
|
||||
nsDocument::NotifyStyleSheetAdded(CSSStyleSheet* aSheet, bool aDocumentSheet)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (aSheet, aDocumentSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
|
||||
|
@ -4088,7 +4084,7 @@ nsDocument::NotifyStyleSheetAdded(CSSStyleSheet* aSheet, bool aDocumentSheet)
|
|||
void
|
||||
nsDocument::NotifyStyleSheetRemoved(CSSStyleSheet* aSheet, bool aDocumentSheet)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (aSheet, aDocumentSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
|
||||
|
@ -4216,8 +4212,7 @@ nsDocument::SetStyleSheetApplicableState(CSSStyleSheet* aSheet,
|
|||
// that are children of sheets in our style set, as well as some
|
||||
// sheets for nsHTMLEditor.
|
||||
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
|
||||
(this, aSheet, aApplicable));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
|
||||
|
@ -5140,9 +5135,7 @@ void
|
|||
nsDocument::StyleRuleChanged(CSSStyleSheet* aSheet,
|
||||
css::Rule* aStyleRule)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
|
||||
(this, aSheet,
|
||||
aStyleRule));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
|
||||
|
@ -5156,8 +5149,7 @@ void
|
|||
nsDocument::StyleRuleAdded(CSSStyleSheet* aSheet,
|
||||
css::Rule* aStyleRule)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
|
||||
(this, aSheet, aStyleRule));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
|
||||
|
@ -5172,8 +5164,7 @@ void
|
|||
nsDocument::StyleRuleRemoved(CSSStyleSheet* aSheet,
|
||||
css::Rule* aStyleRule)
|
||||
{
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
|
||||
(this, aSheet, aStyleRule));
|
||||
NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (aSheet));
|
||||
|
||||
if (StyleSheetChangeEventsEnabled()) {
|
||||
DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
|
||||
|
@ -8758,6 +8749,13 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_WEBSPEECH
|
||||
nsGlobalWindow* globalWindow = static_cast<nsGlobalWindow*>(win);
|
||||
if (globalWindow->HasActiveSpeechSynthesis()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1494,8 +1494,7 @@ public:
|
|||
protected:
|
||||
already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
|
||||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet,
|
||||
nsCompatibility aCompatMode);
|
||||
nsStyleSet* aStyleSet);
|
||||
|
||||
void RemoveDocStyleSheetsFromStyleSets();
|
||||
void RemoveStyleSheetsFromStyleSets(
|
||||
|
|
|
@ -3760,6 +3760,19 @@ nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError)
|
|||
|
||||
return mSpeechSynthesis;
|
||||
}
|
||||
|
||||
bool
|
||||
nsGlobalWindow::HasActiveSpeechSynthesis()
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
|
||||
if (mSpeechSynthesis) {
|
||||
return !mSpeechSynthesis->HasEmptyQueue();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
already_AddRefed<nsIDOMWindow>
|
||||
|
|
|
@ -1045,6 +1045,7 @@ public:
|
|||
#ifdef MOZ_WEBSPEECH
|
||||
mozilla::dom::SpeechSynthesis*
|
||||
GetSpeechSynthesis(mozilla::ErrorResult& aError);
|
||||
bool HasActiveSpeechSynthesis();
|
||||
#endif
|
||||
already_AddRefed<nsICSSDeclaration>
|
||||
GetDefaultComputedStyle(mozilla::dom::Element& aElt,
|
||||
|
|
|
@ -94,15 +94,13 @@ public:
|
|||
* A StyleSheet has just been added to the document. This method is
|
||||
* called automatically when a StyleSheet gets added to the
|
||||
* document, even if the stylesheet is not applicable. The
|
||||
* notification is passed on to all of the document observers.
|
||||
* notification is passed on to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been added
|
||||
* @param aDocumentSheet True if sheet is in document's style sheet list,
|
||||
* false if sheet is not (i.e., UA or user sheet)
|
||||
*/
|
||||
virtual void StyleSheetAdded(nsIDocument *aDocument,
|
||||
mozilla::CSSStyleSheet* aStyleSheet,
|
||||
virtual void StyleSheetAdded(mozilla::CSSStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet) = 0;
|
||||
|
||||
/**
|
||||
|
@ -111,13 +109,11 @@ public:
|
|||
* from the document, even if the stylesheet is not applicable. The
|
||||
* notification is passed on to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been removed
|
||||
* @param aDocumentSheet True if sheet is in document's style sheet list,
|
||||
* false if sheet is not (i.e., UA or user sheet)
|
||||
*/
|
||||
virtual void StyleSheetRemoved(nsIDocument *aDocument,
|
||||
mozilla::CSSStyleSheet* aStyleSheet,
|
||||
virtual void StyleSheetRemoved(mozilla::CSSStyleSheet* aStyleSheet,
|
||||
bool aDocumentSheet) = 0;
|
||||
|
||||
/**
|
||||
|
@ -127,14 +123,9 @@ public:
|
|||
* notification to the document. The notification is passed on
|
||||
* to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has changed state
|
||||
* @param aApplicable true if the sheet is applicable, false if
|
||||
* it is not applicable
|
||||
*/
|
||||
virtual void StyleSheetApplicableStateChanged(nsIDocument *aDocument,
|
||||
mozilla::CSSStyleSheet* aStyleSheet,
|
||||
bool aApplicable) = 0;
|
||||
virtual void StyleSheetApplicableStateChanged(mozilla::CSSStyleSheet* aStyleSheet) = 0;
|
||||
|
||||
/**
|
||||
* A StyleRule has just been modified within a style sheet.
|
||||
|
@ -143,13 +134,9 @@ public:
|
|||
* the document. The notification is passed on to all of
|
||||
* the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that contians the rule
|
||||
* @param aStyleRule The rule being changed.
|
||||
*/
|
||||
virtual void StyleRuleChanged(nsIDocument *aDocument,
|
||||
mozilla::CSSStyleSheet* aStyleSheet,
|
||||
mozilla::css::Rule* aStyleRule) = 0;
|
||||
virtual void StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet) = 0;
|
||||
|
||||
/**
|
||||
* A StyleRule has just been added to a style sheet.
|
||||
|
@ -158,13 +145,9 @@ public:
|
|||
* notification to the document. The notification is passed on
|
||||
* to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been modified
|
||||
* @param aStyleRule the rule that was added
|
||||
*/
|
||||
virtual void StyleRuleAdded(nsIDocument *aDocument,
|
||||
mozilla::CSSStyleSheet* aStyleSheet,
|
||||
mozilla::css::Rule* aStyleRule) = 0;
|
||||
virtual void StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet) = 0;
|
||||
|
||||
/**
|
||||
* A StyleRule has just been removed from a style sheet.
|
||||
|
@ -173,13 +156,9 @@ public:
|
|||
* notification to the document. The notification is passed on
|
||||
* to all of the document observers.
|
||||
*
|
||||
* @param aDocument The document being observed
|
||||
* @param aStyleSheet the StyleSheet that has been modified
|
||||
* @param aStyleRule the rule that was removed
|
||||
*/
|
||||
virtual void StyleRuleRemoved(nsIDocument *aDocument,
|
||||
mozilla::CSSStyleSheet* aStyleSheet,
|
||||
mozilla::css::Rule* aStyleRule) = 0;
|
||||
virtual void StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet) = 0;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
|
||||
|
@ -207,35 +186,25 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
|
|||
mozilla::EventStates aStateMask) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED \
|
||||
virtual void StyleSheetAdded(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
virtual void StyleSheetAdded(mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED \
|
||||
virtual void StyleSheetRemoved(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
virtual void StyleSheetRemoved(mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED \
|
||||
virtual void StyleSheetApplicableStateChanged( \
|
||||
nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
bool aApplicable) override;
|
||||
mozilla::CSSStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED \
|
||||
virtual void StyleRuleChanged(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) override;
|
||||
virtual void StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED \
|
||||
virtual void StyleRuleAdded(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) override;
|
||||
virtual void StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED \
|
||||
virtual void StyleRuleRemoved(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) override;
|
||||
virtual void StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet) override;
|
||||
|
||||
#define NS_DECL_NSIDOCUMENTOBSERVER \
|
||||
NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE \
|
||||
|
@ -293,39 +262,29 @@ NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class)
|
|||
|
||||
#define NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(_class) \
|
||||
void \
|
||||
_class::StyleSheetAdded(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
_class::StyleSheetAdded(mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleSheetRemoved(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
_class::StyleSheetRemoved(mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
bool aDocumentSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleSheetApplicableStateChanged(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet,\
|
||||
bool aApplicable) \
|
||||
_class::StyleSheetApplicableStateChanged(mozilla::CSSStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleRuleChanged(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) \
|
||||
_class::StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleRuleAdded(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) \
|
||||
_class::StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
} \
|
||||
void \
|
||||
_class::StyleRuleRemoved(nsIDocument* aDocument, \
|
||||
mozilla::CSSStyleSheet* aStyleSheet, \
|
||||
mozilla::css::Rule* aStyleRule) \
|
||||
_class::StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet) \
|
||||
{ \
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
|
||||
var fileURL = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html';
|
||||
var generator = runTests();
|
||||
var testFrame;
|
||||
var ac;
|
||||
|
||||
function assert(aVal, aMessage) {
|
||||
return (!aVal) ? error(aMessage) : 0;
|
||||
}
|
||||
|
||||
function error(aMessage) {
|
||||
ok(false, "Error : " + aMessage);
|
||||
finish();
|
||||
}
|
||||
|
||||
function continueTest() {
|
||||
try {
|
||||
generator.next();
|
||||
} catch (e if e instanceof StopIteration) {
|
||||
error("Stop test because of exception!");
|
||||
}
|
||||
}
|
||||
|
||||
function finish() {
|
||||
document.body.removeChild(testFrame);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function setCommand(aArg) {
|
||||
assert(!!ac, "Audio channel doesn't exist!");
|
||||
info("# Command = " + aArg);
|
||||
|
||||
testFrame.src = fileURL + '#' + aArg;
|
||||
var expectedActive = false;
|
||||
switch (aArg) {
|
||||
case 'play':
|
||||
case 'unmute':
|
||||
case 'volume-1':
|
||||
expectedActive = true;
|
||||
break;
|
||||
case 'pause':
|
||||
case 'mute':
|
||||
case 'volume-0':
|
||||
expectedActive = false;
|
||||
break;
|
||||
default :
|
||||
error("Undefined command!");
|
||||
}
|
||||
|
||||
ac.onactivestatechanged = () => {
|
||||
ac.onactivestatechanged = null;
|
||||
ac.isActive().onsuccess = (e) => {
|
||||
is(expectedActive, e.target.result,
|
||||
"Correct active state = " + expectedActive);
|
||||
continueTest();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
setCommand('play');
|
||||
yield undefined;
|
||||
|
||||
setCommand('mute');
|
||||
yield undefined;
|
||||
|
||||
setCommand('unmute');
|
||||
yield undefined;
|
||||
|
||||
setCommand('volume-0');
|
||||
yield undefined;
|
||||
|
||||
setCommand('volume-1');
|
||||
yield undefined;
|
||||
|
||||
setCommand('pause');
|
||||
yield undefined;
|
||||
|
||||
finish();
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
function setupTestFrame() {
|
||||
testFrame = document.createElement('iframe');
|
||||
testFrame.setAttribute('mozbrowser', 'true');
|
||||
testFrame.setAttribute('mozapp', 'http://example.org/manifest.webapp');
|
||||
testFrame.src = fileURL;
|
||||
|
||||
function loadend() {
|
||||
testFrame.removeEventListener('mozbrowserloadend', loadend);
|
||||
ok("allowedAudioChannels" in testFrame, "allowedAudioChannels exist");
|
||||
var channels = testFrame.allowedAudioChannels;
|
||||
is(channels.length, 1, "1 audio channel by default");
|
||||
|
||||
ac = channels[0];
|
||||
|
||||
ok(ac instanceof BrowserElementAudioChannel, "Correct class");
|
||||
ok("isActive" in ac, "isActive exists");
|
||||
ok("onactivestatechanged" in ac, "onactivestatechanged exists");
|
||||
|
||||
generator.next();
|
||||
}
|
||||
|
||||
function alertError(e) {
|
||||
testFrame.removeEventListener('mozbrowsershowmodalprompt', alertError);
|
||||
var message = e.detail.message
|
||||
error(message);
|
||||
}
|
||||
|
||||
testFrame.addEventListener('mozbrowserloadend', loadend);
|
||||
testFrame.addEventListener('mozbrowsershowmodalprompt', alertError);
|
||||
document.body.appendChild(testFrame);
|
||||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
|
||||
function() {
|
||||
SimpleTest.executeSoon(setupTestFrame);
|
||||
});
|
||||
});
|
||||
|
|
@ -182,60 +182,7 @@ function audio() {
|
|||
document.body.appendChild(iframe);
|
||||
}
|
||||
|
||||
function audioMutedByDefault() {
|
||||
info("Test : audio-muted-by-default");
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{'set': [["dom.audiochannel.mutedByDefault", true]]}, function () {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('mozbrowser', 'true');
|
||||
iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
|
||||
iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_processingAudioSample.html';
|
||||
|
||||
function audio_loadend_MutedByDefault() {
|
||||
ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
|
||||
var channels = iframe.allowedAudioChannels;
|
||||
is(channels.length, 1, "1 audio channel by default");
|
||||
|
||||
var ac = channels[0];
|
||||
|
||||
ok(ac instanceof BrowserElementAudioChannel, "Correct class");
|
||||
ok("getMuted" in ac, "ac.getMuted exists");
|
||||
ok("setMuted" in ac, "ac.setMuted exists");
|
||||
|
||||
ac.onactivestatechanged = function() {
|
||||
ok(true, "activestatechanged event received.");
|
||||
ac.onactivestatechanged = null;
|
||||
|
||||
new Promise(function(r, rr) {
|
||||
ac.getMuted().onsuccess = function(e) {
|
||||
is(e.target.result, true, "Muted channel by default");
|
||||
r();
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
ac.setMuted(false).onsuccess = function(e) {
|
||||
ok(true, "Unmuted the channel.");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var complete = false;
|
||||
iframe.addEventListener("mozbrowsershowmodalprompt", function (e) {
|
||||
is(e.detail.message, "playback-success", "Audio playback success!");
|
||||
if (!complete) {
|
||||
document.body.removeChild(iframe);
|
||||
SpecialPowers.popPrefEnv(runTests);
|
||||
complete = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
iframe.addEventListener('mozbrowserloadend', audio_loadend_MutedByDefault);
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
var tests = [ noaudio, audio, audioMutedByDefault ];
|
||||
var tests = [ noaudio, audio ];
|
||||
|
||||
function runTests() {
|
||||
if (tests.length == 0) {
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
browserElementTestHelpers.setEnabledPref(true);
|
||||
browserElementTestHelpers.addPermission();
|
||||
|
||||
var fileURL = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AudioChannelMutedByDefault.html';
|
||||
var testFrame;
|
||||
var ac;
|
||||
|
||||
function alertListener(e) {
|
||||
var message = e.detail.message
|
||||
if (/^OK/.exec(message)) {
|
||||
ok(true, "Message from file : " + message);
|
||||
} else if (/^KO/.exec(message)) {
|
||||
error(message);
|
||||
} else if (/DONE/.exec(message)) {
|
||||
ok(true, "Audio playback success!");
|
||||
finish();
|
||||
} else {
|
||||
error("Undefined event.");
|
||||
}
|
||||
}
|
||||
|
||||
function assert(aVal, aMessage) {
|
||||
return (!aVal) ? error(aMessage) : 0;
|
||||
}
|
||||
|
||||
function error(aMessage) {
|
||||
ok(false, "Error : " + aMessage);
|
||||
finish();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
testFrame.removeEventListener('mozbrowsershowmodalprompt', alertListener);
|
||||
document.body.removeChild(testFrame);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function setCommand(aArg) {
|
||||
assert(!!ac, "Audio channel doesn't exist!");
|
||||
info("# Command = " + aArg);
|
||||
testFrame.src = fileURL + '#' + aArg;
|
||||
|
||||
switch (aArg) {
|
||||
case 'play':
|
||||
ac.onactivestatechanged = () => {
|
||||
ac.onactivestatechanged = null;
|
||||
ok(true, "activestatechanged event received.");
|
||||
|
||||
new Promise(function(r, rr) {
|
||||
ac.getMuted().onsuccess = function(e) {
|
||||
is(e.target.result, true, "Muted channel by default");
|
||||
r();
|
||||
}
|
||||
}).then(function() {
|
||||
ac.setMuted(false).onsuccess = function(e) {
|
||||
ok(true, "Unmuted the channel.");
|
||||
}
|
||||
});
|
||||
};
|
||||
break;
|
||||
default :
|
||||
error("Undefined command!");
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
setCommand('play');
|
||||
}
|
||||
|
||||
function setupTestFrame() {
|
||||
testFrame = document.createElement('iframe');
|
||||
testFrame.setAttribute('mozbrowser', 'true');
|
||||
testFrame.setAttribute('mozapp', 'http://example.org/manifest.webapp');
|
||||
testFrame.src = fileURL;
|
||||
|
||||
function loadend() {
|
||||
testFrame.removeEventListener('mozbrowserloadend', loadend);
|
||||
ok("allowedAudioChannels" in testFrame, "allowedAudioChannels exist");
|
||||
var channels = testFrame.allowedAudioChannels;
|
||||
is(channels.length, 1, "1 audio channel by default");
|
||||
|
||||
ac = channels[0];
|
||||
ok(ac instanceof BrowserElementAudioChannel, "Correct class");
|
||||
ok("getMuted" in ac, "ac.getMuted exists");
|
||||
ok("setMuted" in ac, "ac.setMuted exists");
|
||||
ok("onactivestatechanged" in ac, "onactivestatechanged exists");
|
||||
|
||||
runTests();
|
||||
}
|
||||
|
||||
info("Set EventListeners.");
|
||||
testFrame.addEventListener('mozbrowsershowmodalprompt', alertListener);
|
||||
testFrame.addEventListener('mozbrowserloadend', loadend);
|
||||
document.body.appendChild(testFrame);
|
||||
}
|
||||
|
||||
addEventListener('testready', function() {
|
||||
SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"],
|
||||
["dom.audiochannel.mutedByDefault", true]]},
|
||||
function() {
|
||||
SimpleTest.executeSoon(setupTestFrame);
|
||||
});
|
||||
});
|
|
@ -47,6 +47,20 @@ function registerPage(aEvent) {
|
|||
ioService.newURI(manifestURI, null, null));
|
||||
}
|
||||
|
||||
function uninstallApp() {
|
||||
if (app) {
|
||||
var request = navigator.mozApps.mgmt.uninstall(app);
|
||||
app = null;
|
||||
request.onerror = () => {
|
||||
error("Uninstall app failed!");
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
is(request.result, manifestURI, "App uninstalled.");
|
||||
runNextTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runTest(aEnable) {
|
||||
var request = navigator.mozApps.install(manifestURI, {});
|
||||
request.onerror = () => {
|
||||
|
@ -63,9 +77,10 @@ function runTest(aEnable) {
|
|||
iframe.setAttribute('remote', aEnable);
|
||||
iframe.setAttribute('mozapp', manifestURI);
|
||||
iframe.src = srcURI;
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
iframe.addEventListener('mozbrowserloadend', () => {
|
||||
function loadend() {
|
||||
iframe.removeEventListener('mozbrowserloadend', loadend);
|
||||
iframe.addEventListener("mozbrowsershowmodalprompt", getInterruption);
|
||||
var channels = iframe.allowedAudioChannels;
|
||||
is(channels.length, 1, "1 audio channel by default");
|
||||
|
||||
|
@ -76,23 +91,19 @@ function runTest(aEnable) {
|
|||
var message = "audiochannel-interruption-begin";
|
||||
registerPage(message);
|
||||
ac.notifyChannel(message);
|
||||
iframe.addEventListener("mozbrowsershowmodalprompt", function (e) {
|
||||
|
||||
function getInterruption(e) {
|
||||
e.target.removeEventListener("mozbrowsershowmodalprompt", getInterruption);
|
||||
is(e.detail.message, message,
|
||||
"App got audiochannel-interruption-begin.");
|
||||
|
||||
if (app) {
|
||||
request = navigator.mozApps.mgmt.uninstall(app);
|
||||
app = null;
|
||||
request.onerror = () => {
|
||||
error("Uninstall app failed!");
|
||||
};
|
||||
request.onsuccess = () => {
|
||||
is(request.result, manifestURI, "App uninstalled.");
|
||||
runNextTest();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
document.body.removeChild(iframe);
|
||||
uninstallApp();
|
||||
}
|
||||
};
|
||||
|
||||
iframe.addEventListener("mozbrowserloadend", loadend);
|
||||
document.body.appendChild(iframe);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7">
|
||||
var audio = new Audio();
|
||||
audio.src = "audio.ogg";
|
||||
audio.loop = true;
|
||||
|
||||
function runCommands()
|
||||
{
|
||||
switch(location.hash) {
|
||||
case '#play':
|
||||
audio.play();
|
||||
break;
|
||||
case '#mute':
|
||||
audio.muted = true;
|
||||
break;
|
||||
case '#unmute':
|
||||
audio.muted = false;
|
||||
break;
|
||||
case '#volume-0':
|
||||
audio.volume = 0.0;
|
||||
break;
|
||||
case '#volume-1':
|
||||
audio.volume = 1.0;
|
||||
break;
|
||||
case '#pause':
|
||||
audio.pause();
|
||||
break;
|
||||
default :
|
||||
alert("Undefined command!");
|
||||
}
|
||||
}
|
||||
window.addEventListener('hashchange', runCommands);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,65 @@
|
|||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var audio = new Audio("audio.ogg");
|
||||
var context = new AudioContext();
|
||||
var node = context.createMediaElementSource(audio);
|
||||
var sp = context.createScriptProcessor(2048, 1);
|
||||
node.connect(sp);
|
||||
var expectedSamplesCount;
|
||||
var nonzeroSamplesCount = 0;
|
||||
var isStarted = false;
|
||||
|
||||
function ok(aVal, aMsg) {
|
||||
alert((!!aVal ? "OK" : "KO") + ", " + aMsg);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
audio.onended = null;
|
||||
audio.pause();
|
||||
alert("DONE");
|
||||
}
|
||||
|
||||
function processSamples(e) {
|
||||
var buf = e.inputBuffer.getChannelData(0);
|
||||
for (var i = 0; i < buf.length; ++i) {
|
||||
if (buf[i] != 0) {
|
||||
if (!isStarted) {
|
||||
isStarted = true;
|
||||
ok(true, "Start process audio sample.");
|
||||
}
|
||||
nonzeroSamplesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nonzeroSamplesCount >= expectedSamplesCount) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
audio.oncanplaythrough = function() {
|
||||
var testDuration = audio.duration > 1.0 ? 1.0 : audio.duration * 0.5;
|
||||
expectedSamplesCount = Math.floor(testDuration * context.sampleRate);
|
||||
sp.onaudioprocess = processSamples;
|
||||
};
|
||||
|
||||
function runCommands()
|
||||
{
|
||||
switch(location.hash) {
|
||||
case '#play':
|
||||
ok(true, "Audio starts playing.")
|
||||
audio.play();
|
||||
audio.onended = () => {
|
||||
audio.onended = null;
|
||||
ok(false, "Audio shouldn't go ended in this test!")
|
||||
};
|
||||
break;
|
||||
default :
|
||||
ok(false, "Undefined command!");
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', runCommands);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,45 +0,0 @@
|
|||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var audio = new Audio("audio.ogg");
|
||||
var context = new AudioContext();
|
||||
var node = context.createMediaElementSource(audio);
|
||||
var sp = context.createScriptProcessor(2048, 1);
|
||||
node.connect(sp);
|
||||
var expectedSamplesCount;
|
||||
var nonzeroSamplesCount = 0;
|
||||
var complete = false;
|
||||
var iterationCount = 0;
|
||||
|
||||
function processSamples(e) {
|
||||
if (complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start playing the audio until the AudioContext is connected and running.
|
||||
if (iterationCount++ == 0) {
|
||||
audio.play();
|
||||
}
|
||||
|
||||
var buf = e.inputBuffer.getChannelData(0);
|
||||
for (var i = 0; i < buf.length; ++i) {
|
||||
if (buf[i] != 0) {
|
||||
nonzeroSamplesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nonzeroSamplesCount >= expectedSamplesCount && !complete) {
|
||||
alert("playback-success");
|
||||
complete = true;
|
||||
}
|
||||
}
|
||||
|
||||
audio.oncanplaythrough = function() {
|
||||
var testDuration = audio.duration > 2.0 ? 2.0 : audio.duration;
|
||||
expectedSamplesCount = Math.floor(testDuration * context.sampleRate) ;
|
||||
sp.onaudioprocess = processSamples;
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -27,6 +27,7 @@ skip-if = toolkit=='gonk'
|
|||
skip-if = (toolkit == 'gonk' && !debug)
|
||||
[test_browserElement_oop_AppWindowNamespace.html]
|
||||
skip-if = (toolkit == 'gonk' && !debug)
|
||||
[test_browserElement_oop_AudioChannelMutedByDefault.html]
|
||||
[test_browserElement_oop_Auth.html]
|
||||
skip-if = (toolkit == 'gonk' && !debug)
|
||||
[test_browserElement_oop_BackForward.html]
|
||||
|
@ -124,3 +125,4 @@ disabled = bug 924771
|
|||
[test_browserElement_oop_getWebManifest.html]
|
||||
[test_browserElement_oop_OpenWindowEmpty.html]
|
||||
skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
|
||||
[test_browserElement_oop_ActiveStateChangeOnChangingMutedOrVolume.html]
|
||||
|
|
|
@ -5,11 +5,13 @@ support-files =
|
|||
../../../dom/media/test/short-video.ogv
|
||||
async.js
|
||||
browserElementTestHelpers.js
|
||||
browserElement_ActiveStateChangeOnChangingMutedOrVolume.js
|
||||
browserElement_Alert.js
|
||||
browserElement_AlertInFrame.js
|
||||
browserElement_AllowEmbedAppsInNestedOOIframe.js
|
||||
browserElement_AppFramePermission.js
|
||||
browserElement_AppWindowNamespace.js
|
||||
browserElement_AudioChannelMutedByDefault.js
|
||||
browserElement_AudioPlayback.js
|
||||
browserElement_Auth.js
|
||||
browserElement_BackForward.js
|
||||
|
@ -84,12 +86,14 @@ support-files =
|
|||
browserElement_GetContentDimensions.js
|
||||
browserElement_AudioChannel.js
|
||||
browserElement_AudioChannel_nested.js
|
||||
file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html
|
||||
file_browserElement_AlertInFrame.html
|
||||
file_browserElement_AlertInFrame_Inner.html
|
||||
file_browserElement_AllowEmbedAppsInNestedOOIframe.html
|
||||
file_browserElement_AppFramePermission.html
|
||||
file_browserElement_AppWindowNamespace.html
|
||||
file_browserElement_AudioChannel_nested.html
|
||||
file_browserElement_AudioChannelMutedByDefault.html
|
||||
file_browserElement_Viewmode.html
|
||||
file_browserElement_ThemeColor.html
|
||||
file_browserElement_BrowserWindowNamespace.html
|
||||
|
@ -143,7 +147,6 @@ support-files =
|
|||
file_web_manifest.html
|
||||
file_web_manifest.json
|
||||
file_illegal_web_manifest.html
|
||||
file_processingAudioSample.html
|
||||
noaudio.webm
|
||||
|
||||
# Note: browserElementTestHelpers.js looks at the test's filename to determine
|
||||
|
@ -162,6 +165,8 @@ skip-if = buildapp == 'b2g'
|
|||
skip-if = toolkit == 'android' || buildapp == 'b2g'
|
||||
[test_browserElement_inproc_AppWindowNamespace.html]
|
||||
skip-if = toolkit == 'android' || buildapp == 'b2g' # android(TIMED_OUT, bug 783509) androidx86(TIMED_OUT, bug 783509)
|
||||
[test_browserElement_inproc_AudioChannelMutedByDefault.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_browserElement_inproc_AudioPlayback.html]
|
||||
[test_browserElement_inproc_Auth.html]
|
||||
skip-if = buildapp == 'b2g'
|
||||
|
@ -258,3 +263,4 @@ disabled = bug 774100
|
|||
[test_browserElement_inproc_getStructuredData.html]
|
||||
[test_browserElement_inproc_OpenWindowEmpty.html]
|
||||
skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
|
||||
[test_browserElement_inproc_ActiveStateChangeOnChangingMutedOrVolume.html]
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test ActiveStateChangeOnChangingMutedOrVolume</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7" src="browserElement_ActiveStateChangeOnChangingMutedOrVolume.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1235535 - Audio Channel Muted-By-Default.</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7" src="browserElement_AudioChannelMutedByDefault.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test ActiveStateChangeOnChangingMutedOrVolume</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7" src="browserElement_ActiveStateChangeOnChangingMutedOrVolume.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1235535 - Audio Channel Muted-By-Default.</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/javascript;version=1.7" src="browserElement_AudioChannelMutedByDefault.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -32,6 +32,7 @@ support-files =
|
|||
[test_cache_overwrite.html]
|
||||
[test_cache_match_vary.html]
|
||||
[test_caches.html]
|
||||
skip-if = e10s && debug && os == 'win'
|
||||
[test_cache_keys.html]
|
||||
[test_cache_put.html]
|
||||
[test_cache_requestCache.html]
|
||||
|
|
|
@ -599,6 +599,7 @@ WebGLProgram::GetProgramParameter(GLenum pname) const
|
|||
if (mContext->IsWebGL2()) {
|
||||
switch (pname) {
|
||||
case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
|
||||
return JS::Int32Value(GetProgramiv(gl, mGLName, pname));
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
|
||||
|
|
|
@ -259,20 +259,28 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
|
|||
[test_mozDashOffset.html]
|
||||
[test_mozGetAsFile.html]
|
||||
[test_strokeText_throw.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
[test_toBlob.html]
|
||||
skip-if = (e10s && debug && os == 'win') # bug 1236257
|
||||
[test_toDataURL_alpha.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
[test_toDataURL_lowercase_ascii.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
[test_toDataURL_parameters.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
[test_windingRuleUndefined.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
[test_2d.fillText.gradient.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
|
||||
[test_2d_composite_canvaspattern_setTransform.html]
|
||||
[test_createPattern_broken.html]
|
||||
[test_setlinedash.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
[test_filter.html]
|
||||
[test_offscreencanvas_toblob.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_toimagebitmap.html]
|
||||
skip-if = (e10s && debug && os == 'win')
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_basic_webgl.html]
|
||||
tags = offscreencanvas
|
||||
|
|
|
@ -87,7 +87,7 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(1 failure out of 615,
|
|||
[test_bug605242.html]
|
||||
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
[test_bug607464.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || (e10s && os == 'win') #CRASH_DUMP, RANDOM
|
||||
[test_bug613634.html]
|
||||
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
[test_bug615597.html]
|
||||
|
|
|
@ -170,7 +170,7 @@ addLoadEvent(doTest);
|
|||
</script>
|
||||
</pre>
|
||||
<div id="parent">
|
||||
<span id="testTarget" style="padding: 5px; border: 1px solid black;">testTarget</span>
|
||||
<span id="testTarget" style="margin-left: 200px; padding: 5px; border: 1px solid black;">testTarget</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -4842,20 +4842,8 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
|||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (mMediaKeys == aMediaKeys) {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
}
|
||||
if (aMediaKeys && aMediaKeys->IsBoundToMediaElement()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeys object is already bound to another HTMLMediaElement"));
|
||||
return promise.forget();
|
||||
}
|
||||
if (mMediaKeys) {
|
||||
// Existing MediaKeys object. Shut it down.
|
||||
mMediaKeys->Shutdown();
|
||||
mMediaKeys = nullptr;
|
||||
}
|
||||
|
||||
// We only support EME for MSE content by default.
|
||||
if (mDecoder &&
|
||||
!mMediaSource &&
|
||||
Preferences::GetBool("media.eme.mse-only", true)) {
|
||||
|
@ -4865,19 +4853,92 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
|
|||
return promise.forget();
|
||||
}
|
||||
|
||||
mMediaKeys = aMediaKeys;
|
||||
// 1. If mediaKeys and the mediaKeys attribute are the same object,
|
||||
// return a resolved promise.
|
||||
if (mMediaKeys == aMediaKeys) {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Note: Our attaching code is synchronous, so we can skip the following steps.
|
||||
|
||||
// 2. If this object's attaching media keys value is true, return a
|
||||
// promise rejected with a new DOMException whose name is InvalidStateError.
|
||||
// 3. Let this object's attaching media keys value be true.
|
||||
// 4. Let promise be a new promise.
|
||||
// 5. Run the following steps in parallel:
|
||||
|
||||
// 5.1 If mediaKeys is not null, CDM instance represented by mediaKeys is
|
||||
// already in use by another media element, and the user agent is unable
|
||||
// to use it with this element, let this object's attaching media keys
|
||||
// value be false and reject promise with a new DOMException whose name
|
||||
// is QuotaExceededError.
|
||||
if (aMediaKeys && aMediaKeys->IsBoundToMediaElement()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR,
|
||||
NS_LITERAL_CSTRING("MediaKeys object is already bound to another HTMLMediaElement"));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// 5.2 If the mediaKeys attribute is not null, run the following steps:
|
||||
if (mMediaKeys) {
|
||||
if (NS_FAILED(mMediaKeys->Bind(this))) {
|
||||
// 5.2.1 If the user agent or CDM do not support removing the association,
|
||||
// let this object's attaching media keys value be false and reject promise
|
||||
// with a new DOMException whose name is NotSupportedError.
|
||||
|
||||
// 5.2.2 If the association cannot currently be removed, let this object's
|
||||
// attaching media keys value be false and reject promise with a new
|
||||
// DOMException whose name is InvalidStateError.
|
||||
if (mDecoder) {
|
||||
// We don't support swapping out the MediaKeys once we've started to
|
||||
// setup the playback pipeline. Note this also means we don't need to worry
|
||||
// about handling disassociating the MediaKeys from the MediaDecoder.
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to bind MediaKeys object to HTMLMediaElement"));
|
||||
mMediaKeys = nullptr;
|
||||
NS_LITERAL_CSTRING("Can't change MediaKeys on HTMLMediaElement after load has started"));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// 5.2.3 Stop using the CDM instance represented by the mediaKeys attribute
|
||||
// to decrypt media data and remove the association with the media element.
|
||||
mMediaKeys->Unbind();
|
||||
mMediaKeys = nullptr;
|
||||
|
||||
// 5.2.4 If the preceding step failed, let this object's attaching media
|
||||
// keys value be false and reject promise with a new DOMException whose
|
||||
// name is the appropriate error name.
|
||||
}
|
||||
|
||||
// 5.3. If mediaKeys is not null, run the following steps:
|
||||
if (aMediaKeys) {
|
||||
// 5.3.1 Associate the CDM instance represented by mediaKeys with the
|
||||
// media element for decrypting media data.
|
||||
if (NS_FAILED(aMediaKeys->Bind(this))) {
|
||||
// 5.3.2 If the preceding step failed, run the following steps:
|
||||
// 5.3.2.1 Set the mediaKeys attribute to null.
|
||||
mMediaKeys = nullptr;
|
||||
// 5.3.2.2 Let this object's attaching media keys value be false.
|
||||
// 5.3.2.3 Reject promise with a new DOMException whose name is
|
||||
// the appropriate error name.
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to bind MediaKeys object to HTMLMediaElement"));
|
||||
return promise.forget();
|
||||
}
|
||||
// 5.3.3 Queue a task to run the "Attempt to Resume Playback If Necessary"
|
||||
// algorithm on the media element.
|
||||
// Note: Setting the CDMProxy on the MediaDecoder will unblock playback.
|
||||
if (mDecoder) {
|
||||
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
|
||||
mDecoder->SetCDMProxy(aMediaKeys->GetCDMProxy());
|
||||
}
|
||||
}
|
||||
|
||||
// 5.4 Set the mediaKeys attribute to mediaKeys.
|
||||
mMediaKeys = aMediaKeys;
|
||||
|
||||
// 5.5 Let this object's attaching media keys value be false.
|
||||
|
||||
// 5.6 Resolve promise.
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
|
||||
// 6. Return promise.
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ nsHTMLDocument::CreateShell(nsPresContext* aContext,
|
|||
nsViewManager* aViewManager,
|
||||
nsStyleSet* aStyleSet)
|
||||
{
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode);
|
||||
return doCreateShell(aContext, aViewManager, aStyleSet);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -593,13 +593,14 @@ support-files = file_bug871161-1.html file_bug871161-2.html
|
|||
[test_hash_encoded.html]
|
||||
[test_bug1081037.html]
|
||||
[test_window_open_close.html]
|
||||
skip-if = buildapp == 'b2g' # bug 1129014
|
||||
skip-if = buildapp == 'b2g' || (e10s && debug && os == 'win') # bug 1129014
|
||||
[test_img_complete.html]
|
||||
[test_viewport_resize.html]
|
||||
[test_extapp.html]
|
||||
[test_image_clone_load.html]
|
||||
[test_bug1203668.html]
|
||||
[test_bug1166138.html]
|
||||
[test_bug1230665.html]
|
||||
[test_filepicker_default_directory.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
|
||||
[test_bug1233598.html]
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 1230665</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
document.getElementById("flexbutton1").focus();
|
||||
synthesizeKey("VK_TAB", { });
|
||||
var e = document.getElementById("flexbutton2");
|
||||
is(document.activeElement, e, "focus in flexbutton2 after TAB");
|
||||
|
||||
document.getElementById("gridbutton1").focus();
|
||||
synthesizeKey("VK_TAB", { });
|
||||
e = document.getElementById("gridbutton2");
|
||||
is(document.activeElement, e, "focus in gridbutton2 after TAB");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div tabindex="0" style="display:flex">
|
||||
<button id="flexbutton1"></button>
|
||||
text <!-- this text will force a :-moz-anonymous-flex-item frame -->
|
||||
<div style="">
|
||||
<button id="flexbutton2"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div tabindex="0" style="display:grid">
|
||||
<button id="gridbutton1"></button>
|
||||
text <!-- this text will force a :-moz-anonymous-grid-item frame -->
|
||||
<div style="">
|
||||
<button id="gridbutton2"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -172,6 +172,6 @@ PEExpectedVariableName=Expected identifier for variable name but found '%1$S'.
|
|||
PEExpectedVariableFallback=Expected variable reference fallback after ','.
|
||||
PEExpectedVariableCommaOrCloseParen=Expected ',' or ')' after variable name in variable reference but found '%1$S'.
|
||||
PESubgridNotSupported=Support for the 'subgrid' keyword of CSS Grid is not enabled.
|
||||
PEMoreThanOneGridRepeatAutoFillInNameList=Only one repeat(auto-fill, ...) is allowed in a name list for a subgrid.
|
||||
PEMoreThanOneGridRepeatAutoFillFitInTrackList=Only one repeat(auto-fill, ...) or repeat(auto-fit, ...) is allowed in a track list.
|
||||
PEMoreThanOneGridRepeatTrackSize=Only one track size is allowed inside repeat(auto-fit/auto-fill, ...).
|
||||
PEMoreThanOneGridRepeatAutoFillInNameList=Only one repeat(auto-fill, …) is allowed in a name list for a subgrid.
|
||||
PEMoreThanOneGridRepeatAutoFillFitInTrackList=Only one repeat(auto-fill, …) or repeat(auto-fit, …) is allowed in a track list.
|
||||
PEMoreThanOneGridRepeatTrackSize=Only one track size is allowed inside repeat(auto-fit/auto-fill, …).
|
||||
|
|
|
@ -504,5 +504,12 @@ MediaKeys::Bind(HTMLMediaElement* aElement)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeys::Unbind()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mElement = nullptr;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsresult Bind(HTMLMediaElement* aElement);
|
||||
void Unbind();
|
||||
|
||||
// Javascript: readonly attribute DOMString keySystem;
|
||||
void GetKeySystem(nsString& retval) const;
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
|
||||
using namespace mozilla;
|
||||
|
||||
// "test.webm" contains 8 SimpleBlocks in a single Cluster with the following attributes
|
||||
static const uint64_t gTimecodes[] = { 66000000, 160000000, 100000000, 133000000,
|
||||
166000000, 200000000, 233000000, 320000000 };
|
||||
static const int64_t gEndOffsets[] = { 501, 772, 930, 1085, 1244, 1380, 1543, 2015 };
|
||||
// "test.webm" contains 8 SimpleBlocks in a single Cluster. The blocks with
|
||||
// timecodes 100000000 and are 133000000 skipped by WebMBufferedParser
|
||||
// because they occur after a block with timecode 160000000 and the parser
|
||||
// expects in-order timecodes per the WebM spec. The remaining 6
|
||||
// SimpleBlocks have the following attributes:
|
||||
static const uint64_t gTimecodes[] = { 66000000, 160000000, 166000000, 200000000, 233000000, 320000000 };
|
||||
static const int64_t gEndOffsets[] = { 501, 772, 1244, 1380, 1543, 2015 };
|
||||
|
||||
TEST(WebMBuffered, BasicTests)
|
||||
{
|
||||
|
@ -66,7 +69,7 @@ TEST(WebMBuffered, RealData)
|
|||
|
||||
nsTArray<WebMTimeDataOffset> mapping;
|
||||
parser.Append(webmData.Elements(), webmData.Length(), mapping, dummy);
|
||||
EXPECT_EQ(mapping.Length(), 8u);
|
||||
EXPECT_EQ(mapping.Length(), 6u);
|
||||
EXPECT_EQ(parser.mStartOffset, 0);
|
||||
EXPECT_EQ(parser.mCurrentOffset, int64_t(webmData.Length()));
|
||||
EXPECT_EQ(parser.GetTimecodeScale(), 500000u);
|
||||
|
@ -95,7 +98,7 @@ TEST(WebMBuffered, RealDataAppend)
|
|||
EXPECT_EQ(parser.mCurrentOffset, int64_t(offset));
|
||||
if (mapping.Length() != arrayEntries) {
|
||||
arrayEntries = mapping.Length();
|
||||
ASSERT_LE(arrayEntries, 8u);
|
||||
ASSERT_LE(arrayEntries, 6u);
|
||||
uint32_t i = arrayEntries - 1;
|
||||
EXPECT_EQ(mapping[i].mEndOffset, gEndOffsets[i]);
|
||||
EXPECT_EQ(mapping[i].mSyncOffset, 361);
|
||||
|
@ -103,7 +106,7 @@ TEST(WebMBuffered, RealDataAppend)
|
|||
EXPECT_EQ(parser.GetTimecodeScale(), 500000u);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(mapping.Length(), 8u);
|
||||
EXPECT_EQ(mapping.Length(), 6u);
|
||||
EXPECT_EQ(parser.mStartOffset, 0);
|
||||
EXPECT_EQ(parser.mCurrentOffset, int64_t(webmData.Length()));
|
||||
EXPECT_EQ(parser.GetTimecodeScale(), 500000u);
|
||||
|
|
|
@ -623,6 +623,8 @@ skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'go
|
|||
[test_eme_session_callable_value.html]
|
||||
[test_eme_canvas_blocked.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_detach_media_keys.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_initDataTypes.html]
|
||||
skip-if = toolkit == 'android' # bug 1149374
|
||||
[test_eme_non_mse_fails.html]
|
||||
|
@ -846,7 +848,9 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
|||
[test_VideoPlaybackQuality_disabled.html]
|
||||
[test_volume.html]
|
||||
[test_vttparser.html]
|
||||
skip-if = e10s && debug && os == 'win'
|
||||
[test_webvtt_disabled.html]
|
||||
skip-if = e10s && debug && os == 'win'
|
||||
|
||||
# The tests below contain backend-specific tests. Write backend independent
|
||||
# tests rather than adding to this list.
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test Encrypted Media Extensions</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script type="text/javascript" src="manifest.js"></script>
|
||||
<script type="text/javascript" src="eme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<video id="v" controls></video>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const keysystem = 'org.w3.clearkey';
|
||||
|
||||
function createAndSet() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var m;
|
||||
navigator.requestMediaKeySystemAccess(keysystem, [{initDataType: 'cenc'}])
|
||||
.then(function (access) {
|
||||
return access.createMediaKeys();
|
||||
}).then(function (mediaKeys) {
|
||||
m = mediaKeys;
|
||||
return document.getElementById("v").setMediaKeys(mediaKeys);
|
||||
}).then(function() {
|
||||
resolve(m);
|
||||
});
|
||||
}
|
||||
)}
|
||||
|
||||
var m1,m2;
|
||||
|
||||
// Test that if we create and set two MediaKeys on one video element,
|
||||
// that if the first MediaKeys we set on the media elemnt is still usable
|
||||
// after the second MediaKeys has been set on the media element.
|
||||
SetupEMEPref(() => {
|
||||
createAndSet().then((m) => {
|
||||
m1 = m; // Stash MediaKeys.
|
||||
return createAndSet();
|
||||
})
|
||||
.then((m) => {
|
||||
m2 = m;
|
||||
is(document.getElementById("v").mediaKeys, m2, "Should have set MediaKeys on media element");
|
||||
ok(document.getElementById("v").mediaKeys != m1, "First MediaKeys should no longer be set on media element");
|
||||
var s = m1.createSession("temporary");
|
||||
return s.generateRequest("webm", StringToArrayBuffer(atob('YAYeAX5Hfod+V9ANHtANHg==')));
|
||||
})
|
||||
.then(() => {
|
||||
ok(true, "Was able to generateRequest using second CDM");
|
||||
SimpleTest.finish();
|
||||
}, () => {
|
||||
ok(false, "Was *NOT* able to generateRequest using second CDM");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -9,8 +9,12 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
#define WEBM_DEBUG(arg, ...) MOZ_LOG(gWebMDemuxerLog, mozilla::LogLevel::Debug, ("WebMBufferedParser(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern LazyLogModule gWebMDemuxerLog;
|
||||
|
||||
static uint32_t
|
||||
VIntLength(unsigned char aFirstByte, uint32_t* aMask)
|
||||
{
|
||||
|
@ -183,9 +187,20 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
|
|||
MOZ_ASSERT(mGotTimecodeScale);
|
||||
uint64_t absTimecode = mClusterTimecode + mBlockTimecode;
|
||||
absTimecode *= mTimecodeScale;
|
||||
WebMTimeDataOffset entry(endOffset, absTimecode, mLastInitStartOffset,
|
||||
mClusterOffset, mClusterEndOffset);
|
||||
aMapping.InsertElementAt(idx, entry);
|
||||
// Avoid creating an entry if the timecode is out of order
|
||||
// (invalid according to the WebM specification) so that
|
||||
// ordering invariants of aMapping are not violated.
|
||||
if (idx == 0 ||
|
||||
aMapping[idx - 1].mTimecode <= absTimecode ||
|
||||
(idx + 1 < aMapping.Length() &&
|
||||
aMapping[idx + 1].mTimecode >= absTimecode)) {
|
||||
WebMTimeDataOffset entry(endOffset, absTimecode, mLastInitStartOffset,
|
||||
mClusterOffset, mClusterEndOffset);
|
||||
aMapping.InsertElementAt(idx, entry);
|
||||
} else {
|
||||
WEBM_DEBUG("Out of order timecode %llu in Cluster at %lld ignored",
|
||||
absTimecode, mClusterOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +317,7 @@ bool WebMBufferedState::CalculateBufferedForRange(int64_t aStartOffset, int64_t
|
|||
"Must have found greatest WebMTimeDataOffset for end");
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mTimeMapping[end].mTimecode >= mTimeMapping[end - 1].mTimecode);
|
||||
uint64_t frameDuration = mTimeMapping[end].mTimecode - mTimeMapping[end - 1].mTimecode;
|
||||
*aStartTime = mTimeMapping[start].mTimecode;
|
||||
*aEndTime = mTimeMapping[end].mTimecode + frameDuration;
|
||||
|
@ -478,3 +494,6 @@ WebMBufferedState::GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime)
|
|||
return true;
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
||||
#undef WEBM_DEBUG
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* 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 "nsISupportsPrimitives.h"
|
||||
#include "nsSpeechTask.h"
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
|
@ -55,7 +56,9 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechSynthesis)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(SpeechSynthesis)
|
||||
|
@ -64,8 +67,16 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(SpeechSynthesis)
|
|||
SpeechSynthesis::SpeechSynthesis(nsPIDOMWindow* aParent)
|
||||
: mParent(aParent)
|
||||
, mHoldQueue(false)
|
||||
, mInnerID(aParent->WindowID())
|
||||
{
|
||||
MOZ_ASSERT(aParent->IsInnerWindow());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, "inner-window-destroyed", true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SpeechSynthesis::~SpeechSynthesis()
|
||||
|
@ -118,6 +129,12 @@ SpeechSynthesis::Paused() const
|
|||
(!mSpeechQueue.IsEmpty() && mSpeechQueue.ElementAt(0)->IsPaused());
|
||||
}
|
||||
|
||||
bool
|
||||
SpeechSynthesis::HasEmptyQueue() const
|
||||
{
|
||||
return mSpeechQueue.Length() == 0;
|
||||
}
|
||||
|
||||
void
|
||||
SpeechSynthesis::Speak(SpeechSynthesisUtterance& aUtterance)
|
||||
{
|
||||
|
@ -239,6 +256,8 @@ SpeechSynthesis::GetVoices(nsTArray< RefPtr<SpeechSynthesisVoice> >& aResult)
|
|||
return;
|
||||
}
|
||||
|
||||
nsISupports* voiceParent = NS_ISUPPORTS_CAST(nsIObserver*, this);
|
||||
|
||||
for (uint32_t i = 0; i < voiceCount; i++) {
|
||||
nsAutoString uri;
|
||||
rv = nsSynthVoiceRegistry::GetInstance()->GetVoice(i, uri);
|
||||
|
@ -251,7 +270,7 @@ SpeechSynthesis::GetVoices(nsTArray< RefPtr<SpeechSynthesisVoice> >& aResult)
|
|||
SpeechSynthesisVoice* voice = mVoiceCache.GetWeak(uri);
|
||||
|
||||
if (!voice) {
|
||||
voice = new SpeechSynthesisVoice(this, uri);
|
||||
voice = new SpeechSynthesisVoice(voiceParent, uri);
|
||||
}
|
||||
|
||||
aResult.AppendElement(voice);
|
||||
|
@ -275,5 +294,36 @@ SpeechSynthesis::ForceEnd()
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SpeechSynthesis::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (strcmp(aTopic, "inner-window-destroyed")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
|
||||
NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
|
||||
|
||||
uint64_t innerID;
|
||||
nsresult rv = wrapper->GetData(&innerID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (innerID == mInnerID) {
|
||||
if (mCurrentTask) {
|
||||
mCurrentTask->Cancel();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, "inner-window-destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
#define mozilla_dom_SpeechSynthesis_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
#include "SpeechSynthesisUtterance.h"
|
||||
|
@ -23,14 +25,17 @@ namespace dom {
|
|||
|
||||
class nsSpeechTask;
|
||||
|
||||
class SpeechSynthesis final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
class SpeechSynthesis final : public nsIObserver
|
||||
, public nsWrapperCache
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
explicit SpeechSynthesis(nsPIDOMWindow* aParent);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(SpeechSynthesis)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(SpeechSynthesis,
|
||||
nsIObserver)
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
nsIDOMWindow* GetParentObject() const;
|
||||
|
||||
|
@ -42,6 +47,8 @@ public:
|
|||
|
||||
bool Paused() const;
|
||||
|
||||
bool HasEmptyQueue() const;
|
||||
|
||||
void Speak(SpeechSynthesisUtterance& aUtterance);
|
||||
|
||||
void Cancel();
|
||||
|
@ -70,6 +77,8 @@ private:
|
|||
nsRefPtrHashtable<nsStringHashKey, SpeechSynthesisVoice> mVoiceCache;
|
||||
|
||||
bool mHoldQueue;
|
||||
|
||||
uint64_t mInnerID;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -82,7 +82,11 @@ SpeechSynthesisRequestParent::SpeechSynthesisRequestParent(SpeechTaskParent* aTa
|
|||
|
||||
SpeechSynthesisRequestParent::~SpeechSynthesisRequestParent()
|
||||
{
|
||||
|
||||
if (mTask) {
|
||||
mTask->mActor = nullptr;
|
||||
// If we still have a task, cancel it.
|
||||
mTask->Cancel();
|
||||
}
|
||||
MOZ_COUNT_DTOR(SpeechSynthesisRequestParent);
|
||||
}
|
||||
|
||||
|
@ -157,7 +161,11 @@ SpeechTaskParent::DispatchStartImpl(const nsAString& aUri)
|
|||
nsresult
|
||||
SpeechTaskParent::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
|
||||
{
|
||||
MOZ_ASSERT(mActor);
|
||||
if (!mActor) {
|
||||
// Child is already gone.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if(NS_WARN_IF(!(mActor->SendOnEnd(false, aElapsedTime, aCharIndex)))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -498,11 +498,17 @@ nsSpeechTask::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
|
|||
NS_IMETHODIMP
|
||||
nsSpeechTask::DispatchError(float aElapsedTime, uint32_t aCharIndex)
|
||||
{
|
||||
LOG(LogLevel::Debug, ("nsSpeechTask::DispatchError"));
|
||||
|
||||
if (!mIndirectAudio) {
|
||||
NS_WARNING("Can't call DispatchError() from a direct audio speech service");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!mPreCanceled) {
|
||||
nsSynthVoiceRegistry::GetInstance()->SpeakNext();
|
||||
}
|
||||
|
||||
return DispatchErrorImpl(aElapsedTime, aCharIndex);
|
||||
}
|
||||
|
||||
|
@ -514,6 +520,10 @@ nsSpeechTask::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (mSpeechSynthesis) {
|
||||
mSpeechSynthesis->OnEnd(this);
|
||||
}
|
||||
|
||||
mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
|
||||
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
|
||||
aCharIndex, aElapsedTime,
|
||||
|
|
|
@ -14,8 +14,6 @@ function synthTestQueue(aTestArgs, aEndFunc) {
|
|||
is(e.target, utterances.shift(), "Target matches utterances");
|
||||
ok(!speechSynthesis.speaking, "speechSynthesis is not speaking.");
|
||||
|
||||
isnot(e.eventType, 'error', "Error in utterance");
|
||||
|
||||
if (utterances.length) {
|
||||
ok(speechSynthesis.pending, "other utterances queued");
|
||||
} else {
|
||||
|
@ -38,10 +36,12 @@ function synthTestQueue(aTestArgs, aEndFunc) {
|
|||
u.addEventListener('end', onend_handler);
|
||||
u.addEventListener('error', onend_handler);
|
||||
|
||||
u.addEventListener(
|
||||
'error', function onerror_handler(e) {
|
||||
ok(false, "Error in speech utterance '" + e.target.text + "'");
|
||||
});
|
||||
u.addEventListener('error',
|
||||
(function (expectedError) {
|
||||
return function onerror_handler(e) {
|
||||
ok(expectedError, "Error in speech utterance '" + e.target.text + "'");
|
||||
};
|
||||
})(aTestArgs[i][1] ? aTestArgs[i][1].err : false));
|
||||
|
||||
utterances.push(u);
|
||||
win.speechSynthesis.speak(u);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="application/javascript">
|
||||
var frameUnloaded = function() {
|
||||
var u = new SpeechSynthesisUtterance('hi');
|
||||
u.addEventListener('end', function () {
|
||||
parent.ok(true, 'Successfully spoke utterance from new frame.');
|
||||
parent.onDone();
|
||||
});
|
||||
speechSynthesis.speak(u);
|
||||
}
|
||||
addEventListener('pageshow', function onshow(evt) {
|
||||
var u = new SpeechSynthesisUtterance('hello');
|
||||
u.lang = 'it-IT-noend';
|
||||
u.addEventListener('start', function() {
|
||||
location =
|
||||
'data:text/html,<html><body onpageshow="' +
|
||||
frameUnloaded.toSource() + '()"></body></html>';
|
||||
});
|
||||
speechSynthesis.speak(u);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -28,7 +28,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1226015
|
|||
|
||||
function testFunc(done_cb) {
|
||||
var utterance = new SpeechSynthesisUtterance();
|
||||
utterance.lang = 'it-IT-error';
|
||||
utterance.lang = 'it-IT-failatstart';
|
||||
|
||||
speechSynthesis.speak(utterance);
|
||||
speechSynthesis.cancel();
|
||||
|
|
|
@ -30,9 +30,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=525444
|
|||
var langUriMap = {};
|
||||
|
||||
for (var voice of speechSynthesis.getVoices()) {
|
||||
if (voice.voiceURI.indexOf('urn:moz-tts:fake-direct') < 0) {
|
||||
continue;
|
||||
}
|
||||
langUriMap[voice.lang] = voice.voiceURI;
|
||||
ok(true, voice.lang + ' ' + voice.voiceURI + ' ' + voice.default);
|
||||
is(voice.default, voice.lang == 'en-JM', 'Only Jamaican voice should be default');
|
||||
|
@ -43,6 +40,7 @@ ok(langUriMap['en-GB'], 'No English-British voice');
|
|||
ok(langUriMap['en-CA'], 'No English-Canadian voice');
|
||||
ok(langUriMap['fr-CA'], 'No French-Canadian voice');
|
||||
ok(langUriMap['es-MX'], 'No Spanish-Mexican voice');
|
||||
ok(langUriMap['it-IT-fail'], 'No Failing Italian voice');
|
||||
|
||||
function testFunc(done_cb) {
|
||||
synthTestQueue(
|
||||
|
@ -53,6 +51,8 @@ function testFunc(done_cb) {
|
|||
{ uri: langUriMap['fr-CA'], rate: 0.5, pitch: 0.75}],
|
||||
[{text: "How are you doing?", args: { lang: "en-GB" } },
|
||||
{ rate: 1, pitch: 1, uri: langUriMap['en-GB']}],
|
||||
[{text: "Come stai?", args: { lang: "it-IT-fail" } },
|
||||
{ rate: 1, pitch: 1, uri: langUriMap['it-IT-fail'], err: true }],
|
||||
[{text: "¡hasta mañana!", args: { lang: "es-MX" } },
|
||||
{ uri: langUriMap['es-MX'] }]],
|
||||
function () {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
tags=msg
|
||||
support-files =
|
||||
common.js
|
||||
file_bfcache_frame.html
|
||||
file_setup.html
|
||||
file_speech_queue.html
|
||||
file_speech_simple.html
|
||||
|
@ -21,3 +22,4 @@ support-files =
|
|||
[test_global_queue.html]
|
||||
[test_global_queue_cancel.html]
|
||||
[test_global_queue_pause.html]
|
||||
[test_bfcache.html]
|
|
@ -31,7 +31,8 @@ enum VoiceFlags
|
|||
{
|
||||
eSuppressEvents = 1,
|
||||
eSuppressEnd = 2,
|
||||
eFailAtStart = 4
|
||||
eFailAtStart = 4,
|
||||
eFail = 8
|
||||
};
|
||||
|
||||
struct VoiceDetails
|
||||
|
@ -55,7 +56,8 @@ static const VoiceDetails sIndirectVoices[] = {
|
|||
{"urn:moz-tts:fake-indirect:zanetta", "Zanetta Farussi", "it-IT", false, 0},
|
||||
{"urn:moz-tts:fake-indirect:margherita", "Margherita Durastanti", "it-IT-noevents-noend", false, eSuppressEvents | eSuppressEnd},
|
||||
{"urn:moz-tts:fake-indirect:teresa", "Teresa Cornelys", "it-IT-noend", false, eSuppressEnd},
|
||||
{"urn:moz-tts:fake-indirect:cecilia", "Cecilia Bartoli", "it-IT-error", false, eFailAtStart},
|
||||
{"urn:moz-tts:fake-indirect:cecilia", "Cecilia Bartoli", "it-IT-failatstart", false, eFailAtStart},
|
||||
{"urn:moz-tts:fake-indirect:gottardo", "Gottardo Aldighieri", "it-IT-fail", false, eFail},
|
||||
};
|
||||
|
||||
// FakeSynthCallback
|
||||
|
@ -238,6 +240,26 @@ FakeIndirectAudioSynth::Speak(const nsAString& aText, const nsAString& aUri,
|
|||
nsString mText;
|
||||
};
|
||||
|
||||
class DispatchError final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DispatchError(nsISpeechTask* aTask, const nsAString& aText) :
|
||||
mTask(aTask), mText(aText)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mTask->DispatchError(mText.Length()/2, mText.Length());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISpeechTask> mTask;
|
||||
nsString mText;
|
||||
};
|
||||
|
||||
uint32_t flags = 0;
|
||||
for (uint32_t i = 0; i < ArrayLength(sIndirectVoices); i++) {
|
||||
if (aUri.EqualsASCII(sIndirectVoices[i].uri)) {
|
||||
|
@ -258,7 +280,10 @@ FakeIndirectAudioSynth::Speak(const nsAString& aText, const nsAString& aUri,
|
|||
nsCOMPtr<nsIRunnable> runnable = new DispatchStart(aTask);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
|
||||
if ((flags & eSuppressEnd) == 0) {
|
||||
if (flags & eFail) {
|
||||
runnable = new DispatchError(aTask, aText);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
} else if ((flags & eSuppressEnd) == 0) {
|
||||
runnable = new DispatchEnd(aTask, aText);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1230533
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1230533: Test speech is stopped from a window when unloaded</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1230533">Mozilla Bug 1230533</a>
|
||||
<p id="display"></p>
|
||||
<iframe id="testFrame"></iframe>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 525444 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var iframe;
|
||||
|
||||
function onDone() {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
iframe = document.getElementById("testFrame");
|
||||
iframe.src = "file_bfcache_frame.html";
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({ set: [
|
||||
['media.webspeech.synth.enabled', true],
|
||||
['media.webspeech.synth.force_global_queue', true],
|
||||
['browser.sessionhistory.cache_subframes', true],
|
||||
['browser.sessionhistory.max_total_viewers', 10]] }, doTest());
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -304,19 +304,18 @@ PluginPRLibrary::SetBackgroundUnknown(NPP instance)
|
|||
}
|
||||
|
||||
nsresult
|
||||
PluginPRLibrary::BeginUpdateBackground(NPP instance,
|
||||
const nsIntRect&, gfxContext** aCtx)
|
||||
PluginPRLibrary::BeginUpdateBackground(NPP instance, const nsIntRect&,
|
||||
DrawTarget** aDrawTarget)
|
||||
{
|
||||
nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
|
||||
NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
|
||||
NS_ERROR("Unexpected use of async APIs for in-process plugin.");
|
||||
*aCtx = nullptr;
|
||||
*aDrawTarget = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PluginPRLibrary::EndUpdateBackground(NPP instance,
|
||||
gfxContext*, const nsIntRect&)
|
||||
PluginPRLibrary::EndUpdateBackground(NPP instance, const nsIntRect&)
|
||||
{
|
||||
NS_RUNTIMEABORT("This should never be called");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
|
|
@ -117,10 +117,10 @@ public:
|
|||
virtual nsresult ContentsScaleFactorChanged(NPP aInstance, double aContentsScaleFactor) override;
|
||||
#endif
|
||||
virtual nsresult SetBackgroundUnknown(NPP instance) override;
|
||||
virtual nsresult BeginUpdateBackground(NPP instance,
|
||||
const nsIntRect&, gfxContext** aCtx) override;
|
||||
virtual nsresult BeginUpdateBackground(NPP instance, const nsIntRect&,
|
||||
DrawTarget** aDrawTarget) override;
|
||||
virtual nsresult EndUpdateBackground(NPP instance,
|
||||
gfxContext* aCtx, const nsIntRect&) override;
|
||||
const nsIntRect&) override;
|
||||
virtual void DidComposite(NPP aInstance) override { }
|
||||
virtual void GetLibraryPath(nsACString& aPath) { aPath.Assign(mFilePath); }
|
||||
virtual nsresult GetRunID(uint32_t* aRunID) override { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
|
|
@ -1250,7 +1250,7 @@ nsNPAPIPluginInstance::SetBackgroundUnknown()
|
|||
|
||||
nsresult
|
||||
nsNPAPIPluginInstance::BeginUpdateBackground(nsIntRect* aRect,
|
||||
gfxContext** aContext)
|
||||
DrawTarget** aDrawTarget)
|
||||
{
|
||||
if (RUNNING != mRunning)
|
||||
return NS_OK;
|
||||
|
@ -1259,12 +1259,11 @@ nsNPAPIPluginInstance::BeginUpdateBackground(nsIntRect* aRect,
|
|||
if (!library)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return library->BeginUpdateBackground(&mNPP, *aRect, aContext);
|
||||
return library->BeginUpdateBackground(&mNPP, *aRect, aDrawTarget);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNPAPIPluginInstance::EndUpdateBackground(gfxContext* aContext,
|
||||
nsIntRect* aRect)
|
||||
nsNPAPIPluginInstance::EndUpdateBackground(nsIntRect* aRect)
|
||||
{
|
||||
if (RUNNING != mRunning)
|
||||
return NS_OK;
|
||||
|
@ -1273,7 +1272,7 @@ nsNPAPIPluginInstance::EndUpdateBackground(gfxContext* aContext,
|
|||
if (!library)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return library->EndUpdateBackground(&mNPP, aContext, *aRect);
|
||||
return library->EndUpdateBackground(&mNPP, *aRect);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -83,6 +83,8 @@ private:
|
|||
typedef mozilla::PluginLibrary PluginLibrary;
|
||||
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsNPAPIPluginInstance)
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
|
||||
|
@ -108,8 +110,8 @@ public:
|
|||
nsresult NotifyPainted(void);
|
||||
nsresult GetIsOOP(bool* aIsOOP);
|
||||
nsresult SetBackgroundUnknown();
|
||||
nsresult BeginUpdateBackground(nsIntRect* aRect, gfxContext** aContext);
|
||||
nsresult EndUpdateBackground(gfxContext* aContext, nsIntRect* aRect);
|
||||
nsresult BeginUpdateBackground(nsIntRect* aRect, DrawTarget** aContext);
|
||||
nsresult EndUpdateBackground(nsIntRect* aRect);
|
||||
nsresult IsTransparent(bool* isTransparent);
|
||||
nsresult GetFormValue(nsAString& aValue);
|
||||
nsresult PushPopupsEnabledState(bool aEnabled);
|
||||
|
|
|
@ -268,25 +268,24 @@ nsPluginInstanceOwner::SetBackgroundUnknown()
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfxContext>
|
||||
already_AddRefed<mozilla::gfx::DrawTarget>
|
||||
nsPluginInstanceOwner::BeginUpdateBackground(const nsIntRect& aRect)
|
||||
{
|
||||
nsIntRect rect = aRect;
|
||||
RefPtr<gfxContext> ctx;
|
||||
RefPtr<DrawTarget> dt;
|
||||
if (mInstance &&
|
||||
NS_SUCCEEDED(mInstance->BeginUpdateBackground(&rect, getter_AddRefs(ctx)))) {
|
||||
return ctx.forget();
|
||||
NS_SUCCEEDED(mInstance->BeginUpdateBackground(&rect, getter_AddRefs(dt)))) {
|
||||
return dt.forget();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginInstanceOwner::EndUpdateBackground(gfxContext* aContext,
|
||||
const nsIntRect& aRect)
|
||||
nsPluginInstanceOwner::EndUpdateBackground(const nsIntRect& aRect)
|
||||
{
|
||||
nsIntRect rect = aRect;
|
||||
if (mInstance) {
|
||||
mInstance->EndUpdateBackground(aContext, &rect);
|
||||
mInstance->EndUpdateBackground(&rect);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ class nsPluginDOMContextMenuListener;
|
|||
class nsPluginFrame;
|
||||
class nsDisplayListBuilder;
|
||||
|
||||
#if defined(MOZ_X11) || defined(ANDROID)
|
||||
class gfxContext;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
class TextComposition;
|
||||
namespace dom {
|
||||
|
@ -54,6 +58,8 @@ class nsPluginInstanceOwner final : public nsIPluginInstanceOwner,
|
|||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
nsPluginInstanceOwner();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -236,9 +242,9 @@ public:
|
|||
// The eventual target of these operations is PluginInstanceParent,
|
||||
// but it takes several hops to get there.
|
||||
void SetBackgroundUnknown();
|
||||
already_AddRefed<gfxContext> BeginUpdateBackground(const nsIntRect& aRect);
|
||||
void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect);
|
||||
|
||||
already_AddRefed<DrawTarget> BeginUpdateBackground(const nsIntRect& aRect);
|
||||
void EndUpdateBackground(const nsIntRect& aRect);
|
||||
|
||||
bool UseAsyncRendering();
|
||||
|
||||
already_AddRefed<nsIURI> GetBaseURI() const;
|
||||
|
|
|
@ -1130,7 +1130,7 @@ PluginInstanceParent::SetBackgroundUnknown()
|
|||
|
||||
nsresult
|
||||
PluginInstanceParent::BeginUpdateBackground(const nsIntRect& aRect,
|
||||
gfxContext** aCtx)
|
||||
DrawTarget** aDrawTarget)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(
|
||||
("[InstanceParent][%p] BeginUpdateBackground for <x=%d,y=%d, w=%d,h=%d>",
|
||||
|
@ -1144,7 +1144,7 @@ PluginInstanceParent::BeginUpdateBackground(const nsIntRect& aRect,
|
|||
MOZ_ASSERT(aRect.TopLeft() == nsIntPoint(0, 0),
|
||||
"Expecting rect for whole frame");
|
||||
if (!CreateBackground(aRect.Size())) {
|
||||
*aCtx = nullptr;
|
||||
*aDrawTarget = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -1157,15 +1157,13 @@ PluginInstanceParent::BeginUpdateBackground(const nsIntRect& aRect,
|
|||
|
||||
RefPtr<gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
|
||||
CreateDrawTargetForSurface(mBackground, gfx::IntSize(sz.width, sz.height));
|
||||
RefPtr<gfxContext> ctx = new gfxContext(dt);
|
||||
ctx.forget(aCtx);
|
||||
dt.forget(aDrawTarget);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PluginInstanceParent::EndUpdateBackground(gfxContext* aCtx,
|
||||
const nsIntRect& aRect)
|
||||
PluginInstanceParent::EndUpdateBackground(const nsIntRect& aRect)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(
|
||||
("[InstanceParent][%p] EndUpdateBackground for <x=%d,y=%d, w=%d,h=%d>",
|
||||
|
|
|
@ -60,6 +60,8 @@ public:
|
|||
#endif // defined(XP_WIN)
|
||||
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
PluginInstanceParent(PluginModuleParent* parent,
|
||||
NPP npp,
|
||||
const nsCString& mimeType,
|
||||
|
@ -338,9 +340,8 @@ public:
|
|||
#endif
|
||||
nsresult SetBackgroundUnknown();
|
||||
nsresult BeginUpdateBackground(const nsIntRect& aRect,
|
||||
gfxContext** aCtx);
|
||||
nsresult EndUpdateBackground(gfxContext* aCtx,
|
||||
const nsIntRect& aRect);
|
||||
DrawTarget** aDrawTarget);
|
||||
nsresult EndUpdateBackground(const nsIntRect& aRect);
|
||||
void DidComposite();
|
||||
|
||||
bool IsUsingDirectDrawing();
|
||||
|
|
|
@ -17,11 +17,13 @@
|
|||
#include "nsSize.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
class gfxContext;
|
||||
class nsCString;
|
||||
class nsNPAPIPlugin;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class DrawTarget;
|
||||
}
|
||||
namespace layers {
|
||||
class Image;
|
||||
class ImageContainer;
|
||||
|
@ -43,6 +45,8 @@ namespace mozilla {
|
|||
class PluginLibrary
|
||||
{
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
virtual ~PluginLibrary() { }
|
||||
|
||||
/**
|
||||
|
@ -91,14 +95,12 @@ public:
|
|||
*/
|
||||
virtual nsresult SetBackgroundUnknown(NPP instance) = 0;
|
||||
virtual nsresult BeginUpdateBackground(NPP instance,
|
||||
const nsIntRect&, gfxContext**) = 0;
|
||||
virtual nsresult EndUpdateBackground(NPP instance,
|
||||
gfxContext*, const nsIntRect&) = 0;
|
||||
const nsIntRect&, DrawTarget**) = 0;
|
||||
virtual nsresult EndUpdateBackground(NPP instance, const nsIntRect&) = 0;
|
||||
virtual nsresult GetRunID(uint32_t* aRunID) = 0;
|
||||
virtual void SetHasLocalInstance() = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ifndef mozilla_PluginLibrary_h
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче