Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-06-09 13:27:49 -04:00
commit 66d2e31b68
348 changed files with 7041 additions and 4525 deletions

View File

@ -3,7 +3,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/. */
/* For documentation of the accessibility architecture,
/* For documentation of the accessibility architecture,
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
*/
@ -87,7 +87,7 @@ private:
/**
* Our native object. Private because its creation is done lazily.
* Don't access it directly. Ever. Unless you are GetNativeObject() or
* Don't access it directly. Ever. Unless you are GetNativeObject() or
* Shutdown()
*/
#if defined(__OBJC__)
@ -102,9 +102,15 @@ private:
* This can never go back to false.
* We need it because checking whether we need a native object cost time.
*/
bool mNativeInited;
bool mNativeInited;
};
#if defined(__OBJC__)
void FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType);
#else
void FireNativeEvent(id aNativeAcc, uint32_t aEventType);
#endif
Class GetTypeFromRole(roles::Role aRole);
} // namespace a11y

View File

@ -20,7 +20,7 @@ using namespace mozilla::a11y;
AccessibleWrap::
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
Accessible(aContent, aDoc), mNativeObject(nil),
Accessible(aContent, aDoc), mNativeObject(nil),
mNativeInited(false)
{
}
@ -29,20 +29,20 @@ AccessibleWrap::~AccessibleWrap()
{
}
mozAccessible*
mozAccessible*
AccessibleWrap::GetNativeObject()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) {
uintptr_t accWrap = reinterpret_cast<uintptr_t>(this);
mNativeObject = [[GetNativeType() alloc] initWithAccessible:accWrap];
}
mNativeInited = true;
return mNativeObject;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
@ -55,7 +55,7 @@ AccessibleWrap::GetNativeInterface(void** aOutInterface)
// overridden in subclasses to create the right kind of object. by default we create a generic
// 'mozAccessible' node.
Class
AccessibleWrap::GetNativeType ()
AccessibleWrap::GetNativeType ()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
@ -112,18 +112,7 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
if (!nativeAcc)
return NS_ERROR_FAILURE;
switch (eventType) {
case nsIAccessibleEvent::EVENT_FOCUS:
[nativeAcc didReceiveFocus];
break;
case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
[nativeAcc valueDidChange];
break;
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
[nativeAcc selectedTextDidChange];
break;
}
FireNativeEvent(nativeAcc, eventType);
return NS_OK;
@ -165,14 +154,14 @@ AccessibleWrap::RemoveChild(Accessible* aAccessible)
// if we for some reason have no native accessible, we should be skipped over (and traversed)
// when fetching all unignored children, etc. when counting unignored children, we will not be counted.
bool
AccessibleWrap::IsIgnored()
bool
AccessibleWrap::IsIgnored()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
mozAccessible* nativeObject = GetNativeObject();
return (!nativeObject) || [nativeObject accessibilityIsIgnored];
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
}
@ -203,9 +192,9 @@ AccessibleWrap::GetUnignoredParent() const
{
// Go up the chain to find a parent that is not ignored.
AccessibleWrap* parentWrap = static_cast<AccessibleWrap*>(Parent());
while (parentWrap && parentWrap->IsIgnored())
while (parentWrap && parentWrap->IsIgnored())
parentWrap = static_cast<AccessibleWrap*>(parentWrap->Parent());
return parentWrap;
}
@ -217,7 +206,7 @@ AccessibleWrap::AncestorIsFlat()
{
// We don't create a native object if we're child of a "flat" accessible;
// for example, on OS X buttons shouldn't have any children, because that
// makes the OS confused.
// makes the OS confused.
//
// To maintain a scripting environment where the XPCOM accessible hierarchy
// look the same on all platforms, we still let the C++ objects be created
@ -234,8 +223,29 @@ AccessibleWrap::AncestorIsFlat()
return false;
}
void
a11y::FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
switch (aEventType) {
case nsIAccessibleEvent::EVENT_FOCUS:
[aNativeAcc didReceiveFocus];
break;
case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
[aNativeAcc valueDidChange];
break;
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
[aNativeAcc selectedTextDidChange];
break;
}
NS_OBJC_END_TRY_ABORT_BLOCK;
}
Class
a11y::GetTypeFromRole(roles::Role aRole)
a11y::GetTypeFromRole(roles::Role aRole)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
@ -247,13 +257,13 @@ a11y::GetTypeFromRole(roles::Role aRole)
{
return [mozButtonAccessible class];
}
case roles::PAGETAB:
return [mozButtonAccessible class];
case roles::CHECKBUTTON:
return [mozCheckboxAccessible class];
case roles::HEADING:
return [mozHeadingAccessible class];
@ -273,11 +283,11 @@ a11y::GetTypeFromRole(roles::Role aRole)
case roles::LINK:
return [mozLinkAccessible class];
default:
return [mozAccessible class];
}
return nil;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;

View File

@ -18,13 +18,13 @@ namespace utils {
* Get a localized string from the a11y string bundle.
* Return nil if not found.
*/
NSString*
NSString*
LocalizedString(const nsString& aString)
{
nsString text;
Accessible::TranslateString(aString, text);
return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text);
}

View File

@ -47,29 +47,44 @@ ProxyCreated(ProxyAccessible* aProxy, uint32_t)
void
ProxyDestroyed(ProxyAccessible* aProxy)
{
mozAccessible* wrapper =
reinterpret_cast<mozAccessible*>(aProxy->GetWrapper());
mozAccessible* wrapper = GetNativeFromProxy(aProxy);
[wrapper expire];
[wrapper release];
aProxy->SetWrapper(0);
}
void
ProxyEvent(ProxyAccessible*, uint32_t)
ProxyEvent(ProxyAccessible* aProxy, uint32_t aEventType)
{
// ignore everything but focus-changed, value-changed, caret and selection
// events for now.
if (aEventType != nsIAccessibleEvent::EVENT_FOCUS &&
aEventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
aEventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
aEventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED)
return;
mozAccessible* wrapper = GetNativeFromProxy(aProxy);
if (wrapper)
FireNativeEvent(wrapper, aEventType);
}
void
ProxyStateChangeEvent(ProxyAccessible*, uint64_t, bool)
ProxyStateChangeEvent(ProxyAccessible* aProxy, uint64_t, bool)
{
// mac doesn't care about state change events
}
void
ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
{
mozAccessible* wrapper = GetNativeFromProxy(aTarget);
if (wrapper)
[wrapper selectedTextDidChange];
}
}
}
} // namespace a11y
} // namespace mozilla
@interface GeckoNSApplication(a11y)
-(void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;

View File

@ -3,7 +3,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/. */
/* For documentation of the accessibility architecture,
/* For documentation of the accessibility architecture,
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
*/
@ -23,7 +23,7 @@ public:
virtual ~RootAccessibleWrap();
Class GetNativeType ();
// let's our native accessible get in touch with the
// native cocoa view that is our accessible parent.
void GetNativeWidget (void **aOutView);

View File

@ -46,7 +46,7 @@ RootAccessibleWrap::GetNativeWidget(void** aOutView)
nsIWidget *widget = view->GetWidget();
if (widget) {
*aOutView = (void**)widget->GetNativeData (NS_NATIVE_WIDGET);
NS_ASSERTION (*aOutView,
NS_ASSERTION (*aOutView,
"Couldn't get the native NSView parent we need to connect the accessibility hierarchy!");
}
}

View File

@ -10,7 +10,7 @@
namespace mozilla {
namespace a11y {
typedef class TextLeafAccessible TextLeafAccessibleWrap;
} // namespace a11y

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccessibleWrap.h"
#include "ProxyAccessible.h"
#import <Cocoa/Cocoa.h>
@ -31,6 +32,12 @@ GetNativeFromGeckoAccessible(mozilla::a11y::Accessible* aAccessible)
return native;
}
inline mozAccessible*
GetNativeFromProxy(mozilla::a11y::ProxyAccessible* aProxy)
{
return reinterpret_cast<mozAccessible*>(aProxy->GetWrapper());
}
// This is OR'd with the Accessible owner to indicate the wrap-ee is a proxy.
static const uintptr_t IS_PROXY = 1;
@ -40,13 +47,13 @@ static const uintptr_t IS_PROXY = 1;
* Weak reference; it owns us.
*/
uintptr_t mGeckoAccessible;
/**
* Strong ref to array of children
*/
NSMutableArray* mChildren;
/**
/**
* Weak reference to the parent
*/
mozAccessible* mParent;
@ -122,7 +129,7 @@ static const uintptr_t IS_PROXY = 1;
// invalidates and removes all our children from our cached array.
- (void)invalidateChildren;
/**
/**
* Append a child if they are already cached.
*/
- (void)appendChild:(mozilla::a11y::Accessible*)aAccessible;

View File

@ -2,7 +2,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#import "mozAccessible.h"
#import "MacUtils.h"
@ -37,7 +37,7 @@ GetClosestInterestingAccessible(id anObject)
// this object is not ignored, so let's return it.
if (![anObject accessibilityIsIgnored])
return GetObjectOrRepresentedView(anObject);
// find the closest ancestor that is not ignored.
id unignoredObject = anObject;
while ((unignoredObject = [unignoredObject accessibilityAttributeValue:NSAccessibilityParentAttribute])) {
@ -45,12 +45,12 @@ GetClosestInterestingAccessible(id anObject)
// object is not ignored, so let's stop the search.
break;
}
// if it's a mozAccessible, we need to take care to maybe return the view we
// represent, to the AT.
if ([unignoredObject respondsToSelector:@selector(hasRepresentedView)])
return GetObjectOrRepresentedView(unignoredObject);
return unignoredObject;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
@ -59,7 +59,7 @@ GetClosestInterestingAccessible(id anObject)
#pragma mark -
@implementation mozAccessible
- (id)initWithAccessible:(uintptr_t)aGeckoAccessible
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
@ -104,7 +104,7 @@ GetClosestInterestingAccessible(id anObject)
return reinterpret_cast<ProxyAccessible*>(mGeckoAccessible & ~IS_PROXY);
}
#pragma mark -
- (BOOL)accessibilityIsIgnored
@ -128,12 +128,12 @@ GetClosestInterestingAccessible(id anObject)
// if we're expired, we don't support any attributes.
if (![self getGeckoAccessible])
return [NSArray array];
static NSArray *generalAttributes = nil;
if (!generalAttributes) {
// standard attributes that are shared and supported by all generic elements.
generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute,
generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute,
NSAccessibilityParentAttribute,
NSAccessibilityRoleAttribute,
NSAccessibilityTitleAttribute,
@ -161,7 +161,7 @@ GetClosestInterestingAccessible(id anObject)
}
- (id)accessibilityAttributeValue:(NSString*)attribute
{
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if (![self getGeckoAccessible])
@ -171,19 +171,19 @@ GetClosestInterestingAccessible(id anObject)
if ([attribute isEqualToString:@"AXMozDescription"])
return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
#endif
if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
return [self children];
if ([attribute isEqualToString:NSAccessibilityParentAttribute])
if ([attribute isEqualToString:NSAccessibilityParentAttribute])
return [self parent];
#ifdef DEBUG_hakan
NSLog (@"(%@ responding to attr %@)", self, attribute);
#endif
if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
return [self role];
if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
return [self position];
if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
return [self subrole];
@ -191,8 +191,8 @@ GetClosestInterestingAccessible(id anObject)
return [NSNumber numberWithBool:[self isEnabled]];
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
return [self value];
if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
return [self roleDescription];
if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
return [self roleDescription];
if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute])
return [self customDescription];
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
@ -213,7 +213,7 @@ GetClosestInterestingAccessible(id anObject)
}
if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
return [self help];
#ifdef DEBUG
NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
#endif
@ -228,7 +228,7 @@ GetClosestInterestingAccessible(id anObject)
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
return [self canBeFocused];
return NO;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
@ -241,7 +241,7 @@ GetClosestInterestingAccessible(id anObject)
#ifdef DEBUG_hakan
NSLog (@"[%@] %@='%@'", self, attribute, value);
#endif
// we only support focusing elements so far.
if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
[self focus];
@ -275,22 +275,22 @@ GetClosestInterestingAccessible(id anObject)
}
// if we didn't find anything, return ourself (or the first unignored ancestor).
return GetClosestInterestingAccessible(self);
return GetClosestInterestingAccessible(self);
}
- (NSArray*)accessibilityActionNames
- (NSArray*)accessibilityActionNames
{
return nil;
}
- (NSString*)accessibilityActionDescription:(NSString*)action
- (NSString*)accessibilityActionDescription:(NSString*)action
{
// by default we return whatever the MacOS API know about.
// if you have custom actions, override.
return NSAccessibilityActionDescription(action);
}
- (void)accessibilityPerformAction:(NSString*)action
- (void)accessibilityPerformAction:(NSString*)action
{
}
@ -299,14 +299,14 @@ GetClosestInterestingAccessible(id anObject)
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (!accWrap)
return nil;
Accessible* focusedGeckoChild = accWrap->FocusedChild();
if (focusedGeckoChild) {
mozAccessible *focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
if (focusedChild)
return GetClosestInterestingAccessible(focusedChild);
}
// return ourself if we can't get a native focused child.
return GetClosestInterestingAccessible(self);
}
@ -324,9 +324,9 @@ GetClosestInterestingAccessible(id anObject)
if (nativeParent)
return GetClosestInterestingAccessible(nativeParent);
}
// GetUnignoredParent() returns null when there is no unignored accessible all the way up to
// the root accessible. so we'll have to return whatever native accessible is above our root accessible
// the root accessible. so we'll have to return whatever native accessible is above our root accessible
// (which might be the owning NSWindow in the application, for example).
//
// get the native root accessible, and tell it to return its first parent unignored accessible.
@ -380,7 +380,7 @@ GetClosestInterestingAccessible(id anObject)
[mChildren addObject:GetObjectOrRepresentedView(curNative)];
}
}
#ifdef DEBUG_hakan
// make sure we're not returning any ignored accessibles.
NSEnumerator *e = [mChildren objectEnumerator];
@ -389,7 +389,7 @@ GetClosestInterestingAccessible(id anObject)
NSAssert1(![m accessibilityIsIgnored], @"we should never return an ignored accessible! (%@)", m);
}
#endif
return mChildren;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
@ -710,7 +710,7 @@ struct RoleDescrComparator
// if mChildren is nil, then we don't even need to bother
if (!mChildren)
return;
mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible);
if (curNative)
[mChildren addObject:GetObjectOrRepresentedView(curNative)];
@ -723,7 +723,7 @@ struct RoleDescrComparator
[self invalidateChildren];
mGeckoAccessible = 0;
NS_OBJC_END_TRY_ABORT_BLOCK;
}
@ -747,13 +747,13 @@ struct RoleDescrComparator
NSAssert(![self accessibilityIsIgnored], @"can't sanity check children of an ignored accessible!");
NSEnumerator *iter = [children objectEnumerator];
mozAccessible *curObj = nil;
NSLog(@"sanity checking %@", self);
while ((curObj = [iter nextObject])) {
id realSelf = GetObjectOrRepresentedView(self);
NSLog(@"checking %@", realSelf);
NSAssert2([curObj parent] == realSelf,
NSAssert2([curObj parent] == realSelf,
@"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf);
}
@ -783,26 +783,26 @@ struct RoleDescrComparator
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!");
// print this node
NSMutableString *indent = [NSMutableString stringWithCapacity:level];
unsigned i=0;
for (;i<level;i++)
[indent appendString:@" "];
NSLog (@"%@(#%i) %@", indent, level, self);
// use |children| method to make sure our children are lazily fetched first.
NSArray *children = [self children];
if (!children)
return;
if (![self accessibilityIsIgnored])
[self sanityCheckChildren];
NSEnumerator *iter = [children objectEnumerator];
mozAccessible *object = nil;
while (iter && (object = [iter nextObject]))
// print every child node's subtree, increasing the indenting
// by two for every level.

View File

@ -9,10 +9,10 @@
/* This protocol's primary use is so widget/cocoa can talk back to us
properly.
ChildView owns the topmost mozRootAccessible, and needs to take care of setting up
ChildView owns the topmost mozRootAccessible, and needs to take care of setting up
that parent/child relationship.
This protocol is thus used to make sure it knows it's talking to us, and not
just some random |id|.
*/
@ -23,7 +23,7 @@
// root accessible per window.
- (BOOL)isRoot;
// some mozAccessibles implement accessibility support in place of another object. for example,
// some mozAccessibles implement accessibility support in place of another object. for example,
// ChildView gets its support from us.
//
// instead of returning a mozAccessible to the OS when it wants an object, we need to pass the view we represent, so the

View File

@ -162,10 +162,10 @@ enum CheckboxValue {
if ([action isEqualToString:NSAccessibilityPressAction]) {
if ([self isChecked] != kUnchecked)
return @"uncheck checkbox"; // XXX: localize this later?
return @"check checkbox"; // XXX: localize this later?
}
return nil;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
@ -179,7 +179,7 @@ enum CheckboxValue {
if (state & states::CHECKED) {
return (state & states::MIXED) ? kMixed : kChecked;
}
return kUnchecked;
}
@ -207,24 +207,24 @@ enum CheckboxValue {
{
// standard attributes that are shared and supported by root accessible (AXMain) elements.
static NSMutableArray* attributes = nil;
if (!attributes) {
attributes = [[super accessibilityAttributeNames] mutableCopy];
[attributes addObject:NSAccessibilityContentsAttribute];
[attributes addObject:NSAccessibilityTabsAttribute];
}
return attributes;
return attributes;
}
- (id)accessibilityAttributeValue:(NSString *)attribute
{
{
if ([attribute isEqualToString:NSAccessibilityContentsAttribute])
return [super children];
if ([attribute isEqualToString:NSAccessibilityTabsAttribute])
return [self tabs];
return [super accessibilityAttributeValue:attribute];
return [super accessibilityAttributeValue:attribute];
}
/**
@ -256,7 +256,7 @@ enum CheckboxValue {
NSArray* children = [self children];
NSEnumerator* enumerator = [children objectEnumerator];
mTabs = [[NSMutableArray alloc] init];
id obj;
while ((obj = [enumerator nextObject]))
if ([obj isTab])

View File

@ -9,7 +9,7 @@
// our protocol that we implement (so cocoa widgets can talk to us)
#import "mozAccessibleProtocol.h"
/*
/*
The root accessible. There is one per window.
Created by the RootAccessibleWrap.
*/

View File

@ -14,7 +14,7 @@
using namespace mozilla::a11y;
static id <mozAccessible, mozView>
static id <mozAccessible, mozView>
getNativeViewFromRootAccessible(Accessible* aAccessible)
{
RootAccessibleWrap* root =
@ -31,14 +31,14 @@ getNativeViewFromRootAccessible(Accessible* aAccessible)
- (NSArray*)accessibilityAttributeNames
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
// if we're expired, we don't support any attributes.
if (![self getGeckoAccessible])
return [NSArray array];
// standard attributes that are shared and supported by root accessible (AXMain) elements.
static NSMutableArray* attributes = nil;
if (!attributes) {
attributes = [[super accessibilityAttributeNames] mutableCopy];
[attributes addObject:NSAccessibilityMainAttribute];
@ -53,14 +53,14 @@ getNativeViewFromRootAccessible(Accessible* aAccessible)
- (id)accessibilityAttributeValue:(NSString *)attribute
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
if ([attribute isEqualToString:NSAccessibilityMainAttribute])
return [NSNumber numberWithBool:[[self window] isMainWindow]];
if ([attribute isEqualToString:NSAccessibilityMinimizedAttribute])
return [NSNumber numberWithBool:[[self window] isMiniaturized]];
return [super accessibilityAttributeValue:attribute];
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
@ -72,10 +72,10 @@ getNativeViewFromRootAccessible(Accessible* aAccessible)
if (!mParallelView)
mParallelView = (id<mozView, mozAccessible>)[self representedView];
if (mParallelView)
return [mParallelView accessibilityAttributeValue:NSAccessibilityParentAttribute];
NSAssert(mParallelView, @"we're a root accessible w/o native view?");
return [super parent];
@ -94,9 +94,9 @@ getNativeViewFromRootAccessible(Accessible* aAccessible)
if (mParallelView)
return (id)mParallelView;
mParallelView = getNativeViewFromRootAccessible ([self getGeckoAccessible]);
NSAssert(mParallelView, @"can't return root accessible's native parallel view.");
return mParallelView;

View File

@ -19,7 +19,7 @@ ToNSRange(id aValue, NSRange* aRange)
{
NS_PRECONDITION(aRange, "aRange is nil");
if ([aValue isKindOfClass:[NSValue class]] &&
if ([aValue isKindOfClass:[NSValue class]] &&
strcmp([(NSValue*)aValue objCType], @encode(NSRange)) == 0) {
*aRange = [aValue rangeValue];
return true;
@ -104,7 +104,7 @@ ToNSString(id aValue)
if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
// Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
// object's AXSelectedText attribute. See bug 674612 for details.
// Also if there is no selected text, we return the full text.
// Also if there is no selected text, we return the full text.
// See bug 369710 for details.
if ([[self role] isEqualToString:NSAccessibilityStaticTextRole]) {
NSString* selectedText = [self selectedText];
@ -224,7 +224,7 @@ ToNSString(id aValue)
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
return ![self isReadOnly];
if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute] ||
[attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] ||
[attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
@ -246,7 +246,7 @@ ToNSString(id aValue)
if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
[self setText:ToNSString(value)];
return;
}
@ -282,7 +282,7 @@ ToNSString(id aValue)
textAcc->ScrollSubstringTo(range.location, range.location + range.length,
nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
return;
}
}
[super accessibilitySetValue:value forAttribute:attribute];

View File

@ -8,36 +8,22 @@
"itau.com.br": "\\(Mobile#(Android; Mobile",
// bug 826510, r7.com
"r7.com": "\\(Mobile#(Android; Mobile",
// bug 826514, estadao.com.br
"estadao.com.br": "\\(Mobile#(Android; Mobile",
// bug 826711, bb.com.br
"bb.com.br": "\\(Mobile#(Android; Mobile",
// bug 827622, bing.com
"bing.com": "\\(Mobile#(Android; Mobile",
// bug 827626, magazineluiza.com.br
"magazineluiza.com.br": "\\(Mobile#(Android; Mobile",
// bug 827633, hao123.com
"hao123.com": "\\(Mobile#(Android; Mobile",
// bug 827573, webmotors.com.br
"webmotors.com.br": "\\(Mobile#(Android; Mobile",
// bug 827670, elpais.com.co
"elpais.com.co": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
// bug 827674, avianca.com
"avianca.com": "\\(Mobile#(Android; Mobile",
// bug 827678, marca.com
"marca.com": "\\(Mobile#(Android; Mobile",
// bug 828371, ingbank.pl
"ingbank.pl": "\\(Mobile#(Android; Mobile",
// bug 828416, loteriasyapuestas.es
"loteriasyapuestas.es": "\\(Mobile#(Android; Mobile",
// bug 828418, bbva.es
"bbva.es": "\\(Mobile#(Android; Mobile",
// bug 828422, publico.es
"publico.es": "\\(Mobile#(Android; Mobile",
// bug 828439, movistar.com.ve
"movistar.com.ve": "\\(Mobile#(Android; Mobile",
// bug 843129, 11870.com
"iphonejuegosgratis.com": "\\(Mobile#(Android; Mobile",
// bug 843132, comunio.es
"comunio.es": "\\(Mobile#(Android; Mobile",
// bug 843151, citibank.com
@ -48,8 +34,6 @@
"ehow.com": "\\(Mobile#(Android; Mobile",
// bug 878228, blikk.hu
"blikk.hu": "\\(Mobile#(Android; Mobile",
// bug 878232, hazipatika.com
"hazipatika.com": "\\(Mobile#(Android; Mobile",
// bug 878238, koponyeg.hu
"koponyeg.hu": "\\(Mobile#(Android; Mobile",
// bug 878240, kuruc.info
@ -60,24 +44,12 @@
"port.hu": "\\(Mobile#(Android; Mobile",
// bug 878249, portfolio.hu
"portfolio.hu": "\\(Mobile#(Android; Mobile",
// bug 878253, vatera.hu
"vatera.hu": "\\(Mobile#(Android; Mobile",
// bug 878260, cdm.me
"cdm.me": "\\(Mobile#(Android; Mobile",
// bug 878262, download.com
"download.com": "\\(Mobile#(Android; Mobile",
// bug 878264, haber.ba
"haber.ba": "\\(Mobile#(Android; Mobile",
// bug 878271, kurir-info.rs
"kurir-info.rs": "\\(Mobile#(Android; Mobile",
// bug 878273, livescore.com
"livescore.com": "\\(Mobile#(Android; Mobile",
// bug 878277, naslovi.net
"naslovi.net": "\\(Mobile#(Android; Mobile",
// bug 878649, univision.com
"univision.com": "\\(Mobile#(Android; Mobile",
// bug 878653, redstarbelgrade.info
"redstarbelgrade.info": "\\(Mobile#(Android; Mobile",
// bug 878655, vesti-online.com
"vesti-online.com": "\\(Mobile#(Android; Mobile"
"redstarbelgrade.info": "\\(Mobile#(Android; Mobile"
}

View File

@ -123,8 +123,7 @@ const gXPInstallObserver = {
}
};
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") +
"find-and-install-add-ons";
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
let messageString;
let notification = document.getElementById("addon-install-confirmation-notification");
@ -132,17 +131,20 @@ const gXPInstallObserver = {
// None of the add-ons are verified
messageString = gNavigatorBundle.getString("addonConfirmInstallUnsigned.message");
notification.setAttribute("warning", "true");
options.learnMoreURL += "unsigned-addons";
}
else if (unsigned.length == 0) {
// All add-ons are verified or don't need to be verified
messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
notification.removeAttribute("warning");
options.learnMoreURL += "find-and-install-add-ons";
}
else {
// Some of the add-ons are unverified, the list of names will indicate
// which
messageString = gNavigatorBundle.getString("addonConfirmInstallSomeUnsigned.message");
notification.setAttribute("warning", "true");
options.learnMoreURL += "unsigned-addons";
}
let brandBundle = document.getElementById("bundle_brand");
@ -310,8 +312,7 @@ const gXPInstallObserver = {
// Add Learn More link when refusing to install an unsigned add-on
if (install.error == AddonManager.ERROR_SIGNEDSTATE_REQUIRED) {
options.learnMoreURL =
Services.prefs.getCharPref("xpinstall.signatures.infoURL");
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
}
messageString = gNavigatorBundle.getFormattedString(error, args);

View File

@ -13,7 +13,17 @@ const kWhitelist = new Set([
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
let {Reflect} = Cu.import("resource://gre/modules/reflect.jsm", {});
// Normally we would use reflect.jsm to get Reflect.parse. However, if
// we do that, then all the AST data is allocated in reflect.jsm's
// zone. That exposes a bug in our GC. The GC collects reflect.jsm's
// zone but not the zone in which our test code lives (since no new
// data is being allocated in it). The cross-compartment wrappers in
// our zone that point to the AST data never get collected, and so the
// AST data itself is never collected. We need to GC both zones at
// once to fix the problem.
const init = Components.classes["@mozilla.org/jsreflect;1"].createInstance();
init();
/**
* Check if an error should be ignored due to matching one of the whitelist

View File

@ -10,9 +10,7 @@ add_task(function* () {
// We must wait for the context menu code to build metadata.
yield openContextMenuForContentSelector(browser, 'form > input[name="search"]');
yield withBookmarksDialog(function*() {
AddKeywordForSearchField();
}, function* (dialogWin) {
yield withBookmarksDialog(AddKeywordForSearchField, function* (dialogWin) {
let acceptBtn = dialogWin.document.documentElement.getButton("accept");
ok(acceptBtn.disabled, "Accept button is disabled");

View File

@ -310,7 +310,9 @@ let withBookmarksDialog = Task.async(function* (openFn, taskFn) {
});
info("withBookmarksDialog: opening the dialog");
yield openFn();
// The dialog might be modal and could block our events loop, so executeSoon.
executeSoon(openFn);
info("withBookmarksDialog: waiting for the dialog");
let dialogWin = yield dialogPromise;

View File

@ -32,6 +32,7 @@ support-files =
[browser_toolbox_highlight.js]
[browser_toolbox_hosts.js]
[browser_toolbox_hosts_size.js]
[browser_toolbox_minimize.js]
[browser_toolbox_options.js]
[browser_toolbox_options_disable_buttons.js]
[browser_toolbox_options_disable_cache-01.js]

View File

@ -0,0 +1,75 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that when the toolbox is displayed in a bottom host, that host can be
// minimized to just the tabbar height, and maximized again.
// Also test that while minimized, switching to a tool, clicking on the
// settings, or clicking on the selected tool's tab maximizes the toolbox again.
// Finally test that the minimize button doesn't exist in other host types.
const URL = "data:text/html;charset=utf8,test page";
add_task(function*() {
info("Create a test tab and open the toolbox");
let tab = yield addTab(URL);
let target = TargetFactory.forTab(tab);
let toolbox = yield gDevTools.showToolbox(target, "webconsole");
let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
ok(button, "The minimize button exists in the default bottom host");
info("Try to minimize the toolbox");
yield minimize(toolbox);
ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0,
"The toolbox host has been hidden away with a negative-margin");
info("Try to maximize again the toolbox");
yield maximize(toolbox);
ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
"The toolbox host is shown again");
info("Minimize again and switch to another tool");
yield minimize(toolbox);
let onMaximized = toolbox._host.once("maximized");
yield toolbox.selectTool("inspector");
yield onMaximized;
info("Minimize again and click on the tab of the current tool");
yield minimize(toolbox);
onMaximized = toolbox._host.once("maximized");
let tabButton = toolbox.doc.querySelector("#toolbox-tab-inspector");
EventUtils.synthesizeMouseAtCenter(tabButton, {}, toolbox.doc.defaultView);
yield onMaximized;
info("Minimize again and click on the settings tab");
yield minimize(toolbox);
onMaximized = toolbox._host.once("maximized");
let settingsButton = toolbox.doc.querySelector("#toolbox-tab-options");
EventUtils.synthesizeMouseAtCenter(settingsButton, {}, toolbox.doc.defaultView);
yield onMaximized;
info("Switch to a different host");
yield toolbox.switchHost(devtools.Toolbox.HostType.SIDE);
button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
ok(!button, "The minimize button doesn't exist in the side host");
Services.prefs.clearUserPref("devtools.toolbox.host");
yield toolbox.destroy();
gBrowser.removeCurrentTab();
});
function* minimize(toolbox) {
let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
let onMinimized = toolbox._host.once("minimized");
EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.doc.defaultView);
yield onMinimized;
}
function* maximize(toolbox) {
let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
let onMaximized = toolbox._host.once("maximized");
EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.doc.defaultView);
yield onMaximized;
}

View File

@ -1,6 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals DOMHelpers, Services */
"use strict";
@ -49,7 +50,7 @@ BottomHost.prototype = {
/**
* Create a box at the bottom of the host tab.
*/
create: function BH_create() {
create: function() {
let deferred = promise.defer();
let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
@ -90,21 +91,69 @@ BottomHost.prototype = {
/**
* Raise the host.
*/
raise: function BH_raise() {
raise: function() {
focusTab(this.hostTab);
},
/**
* Set the toolbox title.
* Minimize this host so that only the toolbox tabbar remains visible.
* @param {Number} height The height to minimize to. Defaults to 0, which
* means that the toolbox won't be visible at all once minimized.
*/
setTitle: function BH_setTitle(title) {
// Nothing to do for this host type.
minimize: function(height=0) {
if (this.isMinimized) {
return;
}
this.isMinimized = true;
this.frame.style.marginBottom = -this.frame.height + height + "px";
this._splitter.classList.add("disabled");
let onTransitionEnd = () => {
this.frame.removeEventListener("transitionend", onTransitionEnd);
this.emit("minimized");
};
this.frame.addEventListener("transitionend", onTransitionEnd);
},
/**
* If the host was minimized before, maximize it again (the host will be
* maximized to the height it previously had).
*/
maximize: function() {
if (!this.isMinimized) {
return;
}
this.isMinimized = false;
this.frame.style.marginBottom = "0";
this._splitter.classList.remove("disabled");
let onTransitionEnd = () => {
this.frame.removeEventListener("transitionend", onTransitionEnd);
this.emit("maximized");
};
this.frame.addEventListener("transitionend", onTransitionEnd);
},
/**
* Toggle the minimize mode.
* @param {Number} minHeight The height to minimize to.
*/
toggleMinimizeMode: function(minHeight) {
this.isMinimized ? this.maximize() : this.minimize(minHeight);
},
/**
* Set the toolbox title.
* Nothing to do for this host type.
*/
setTitle: function() {},
/**
* Destroy the bottom dock.
*/
destroy: function BH_destroy() {
destroy: function() {
if (!this._destroyed) {
this._destroyed = true;
@ -115,8 +164,7 @@ BottomHost.prototype = {
return promise.resolve(null);
}
}
};
/**
* Host object for the in-browser sidebar
@ -135,7 +183,7 @@ SidebarHost.prototype = {
/**
* Create a box in the sidebar of the host tab.
*/
create: function SH_create() {
create: function() {
let deferred = promise.defer();
let gBrowser = this.hostTab.ownerDocument.defaultView.gBrowser;
@ -175,21 +223,20 @@ SidebarHost.prototype = {
/**
* Raise the host.
*/
raise: function SH_raise() {
raise: function() {
focusTab(this.hostTab);
},
/**
* Set the toolbox title.
* Nothing to do for this host type.
*/
setTitle: function SH_setTitle(title) {
// Nothing to do for this host type.
},
setTitle: function() {},
/**
* Destroy the sidebar.
*/
destroy: function SH_destroy() {
destroy: function() {
if (!this._destroyed) {
this._destroyed = true;
@ -200,7 +247,7 @@ SidebarHost.prototype = {
return promise.resolve(null);
}
}
};
/**
* Host object for the toolbox in a separate window
@ -219,14 +266,14 @@ WindowHost.prototype = {
/**
* Create a new xul window to contain the toolbox.
*/
create: function WH_create() {
create: function() {
let deferred = promise.defer();
let flags = "chrome,centerscreen,resizable,dialog=no";
let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
flags, null);
let frameLoad = (event) => {
let frameLoad = () => {
win.removeEventListener("load", frameLoad, true);
win.focus();
this.frame = win.document.getElementById("toolbox-iframe");
@ -258,21 +305,21 @@ WindowHost.prototype = {
/**
* Raise the host.
*/
raise: function RH_raise() {
raise: function() {
this._window.focus();
},
/**
* Set the toolbox title.
*/
setTitle: function WH_setTitle(title) {
setTitle: function(title) {
this._window.document.title = title;
},
/**
* Destroy the window.
*/
destroy: function WH_destroy() {
destroy: function() {
if (!this._destroyed) {
this._destroyed = true;
@ -296,14 +343,14 @@ function CustomHost(hostTab, options) {
CustomHost.prototype = {
type: "custom",
_sendMessageToTopWindow: function CH__sendMessageToTopWindow(msg, data) {
_sendMessageToTopWindow: function(msg, data) {
// It's up to the custom frame owner (parent window) to honor
// "close" or "raise" instructions.
let topWindow = this.frame.ownerDocument.defaultView;
if (!topWindow) {
return;
}
let json = {name:"toolbox-" + msg, uid: this.uid};
let json = {name: "toolbox-" + msg, uid: this.uid};
if (data) {
json.data = data;
}
@ -313,35 +360,35 @@ CustomHost.prototype = {
/**
* Create a new xul window to contain the toolbox.
*/
create: function CH_create() {
create: function() {
return promise.resolve(this.frame);
},
/**
* Raise the host.
*/
raise: function CH_raise() {
raise: function() {
this._sendMessageToTopWindow("raise");
},
/**
* Set the toolbox title.
*/
setTitle: function CH_setTitle(title) {
setTitle: function(title) {
this._sendMessageToTopWindow("title", { value: title });
},
/**
* Destroy the window.
*/
destroy: function WH_destroy() {
destroy: function() {
if (!this._destroyed) {
this._destroyed = true;
this._sendMessageToTopWindow("close");
}
return promise.resolve(null);
}
}
};
/**
* Switch to the given tab in a browser and focus the browser window

View File

@ -1,6 +1,9 @@
/* 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/. */
/* globals gDevTools, DOMHelpers, toolboxStrings, InspectorFront, Selection,
getPerformanceActorsConnection, CommandUtils, DevToolsUtils, screenManager,
oscpu, Hosts, is64Bit */
"use strict";
@ -128,6 +131,10 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._onFocus = this._onFocus.bind(this);
this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
this._updateTextboxMenuItems = this._updateTextboxMenuItems.bind(this);
this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this);
this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this);
this._target.on("close", this.destroy);
@ -314,7 +321,7 @@ Toolbox.prototype = {
/**
* Open the toolbox
*/
open: function () {
open: function() {
return Task.spawn(function*() {
let iframe = yield this._host.create();
let domReady = promise.defer();
@ -391,13 +398,15 @@ Toolbox.prototype = {
]);
// Lazily connect to the profiler here and don't wait for it to complete,
// used to intercept console.profile calls before the performance tools are open.
// used to intercept console.profile calls before the performance tools
// are open.
let profilerReady = this._connectProfiler();
// However, while testing, we must wait for the performance connection to finish,
// as most tests shut down without waiting for a toolbox destruction event,
// resulting in the shared profiler connection being opened and closed
// outside of the test that originally opened the toolbox.
// However, while testing, we must wait for the performance connection to
// finish, as most tests shut down without waiting for a toolbox
// destruction event, resulting in the shared profiler connection being
// opened and closed outside of the test that originally opened the
// toolbox.
if (gDevTools.testing) {
yield profilerReady;
}
@ -429,13 +438,13 @@ Toolbox.prototype = {
* }
*/
_prefChanged: function(event, data) {
switch(data.pref) {
case "devtools.cache.disabled":
this._applyCacheSettings();
break;
case "devtools.serviceWorkers.testing.enabled":
this._applyServiceWorkersTestingSettings();
break;
switch (data.pref) {
case "devtools.cache.disabled":
this._applyCacheSettings();
break;
case "devtools.serviceWorkers.testing.enabled":
this._applyServiceWorkersTestingSettings();
break;
}
},
@ -628,7 +637,8 @@ Toolbox.prototype = {
}
key.setAttribute("modifiers", toolDefinition.modifiers);
key.setAttribute("oncommand", "void(0);"); // needed. See bug 371900
// needed. See bug 371900
key.setAttribute("oncommand", "void(0);");
key.addEventListener("command", () => {
this.selectTool(toolId).then(() => this.fireCustomKey(toolId));
}, true);
@ -642,7 +652,8 @@ Toolbox.prototype = {
key.setAttribute("key", toolboxStrings("browserConsoleCmd.commandkey"));
key.setAttribute("modifiers", "accel,shift");
key.setAttribute("oncommand", "void(0)"); // needed. See bug 371900
// needed. See bug 371900
key.setAttribute("oncommand", "void(0)");
key.addEventListener("command", () => {
HUDService.toggleBrowserConsole();
}, true);
@ -651,9 +662,10 @@ Toolbox.prototype = {
},
/**
* Handle any custom key events. Returns true if there was a custom key binding run
* @param {string} toolId
* Which tool to run the command on (skip if not current)
* Handle any custom key events. Returns true if there was a custom key
* binding run.
* @param {string} toolId Which tool to run the command on (skip if not
* current)
*/
fireCustomKey: function(toolId) {
let toolDefinition = gDevTools.getToolDefinition(toolId);
@ -680,6 +692,32 @@ Toolbox.prototype = {
return;
}
// Bottom-type host can be minimized, add a button for this.
if (this.hostType == Toolbox.HostType.BOTTOM) {
let minimizeBtn = this.doc.createElement("toolbarbutton");
minimizeBtn.id = "toolbox-dock-bottom-minimize";
minimizeBtn.className = "maximized";
minimizeBtn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.minimize"));
// Calculate the height to which the host should be minimized so the
// tabbar is still visible.
let toolbarHeight = this.doc.querySelector(".devtools-tabbar")
.getBoxQuads({box: "content"})[0]
.bounds.height;
minimizeBtn.addEventListener("command", () => {
this._host.toggleMinimizeMode(toolbarHeight);
});
dockBox.appendChild(minimizeBtn);
// Update the label and icon when the state changes.
this._host.on("minimized", this._onBottomHostMinimized);
this._host.on("maximized", this._onBottomHostMaximized);
// Maximize again when a tool gets selected.
this.on("before-select", this._onToolSelectWhileMinimized);
// Maximize and stop listening before the host type changes.
this.once("host-will-change", this._onBottomHostWillChange);
}
if (this.hostType == Toolbox.HostType.WINDOW) {
this.closeButton.setAttribute("hidden", "true");
} else {
@ -709,6 +747,32 @@ Toolbox.prototype = {
}
},
_onBottomHostMinimized: function() {
let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
btn.className = "minimized";
btn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.maximize"));
},
_onBottomHostMaximized: function() {
let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
btn.className = "maximized";
btn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.minimize"));
},
_onToolSelectWhileMinimized: function() {
this._host.maximize();
},
_onBottomHostWillChange: function() {
this._host.maximize();
this._host.off("minimized", this._onBottomHostMinimized);
this._host.off("maximized", this._onBottomHostMaximized);
this.off("before-select", this._onToolSelectWhileMinimized);
},
/**
* Add tabs to the toolbox UI for registered tools
*/
@ -835,8 +899,9 @@ Toolbox.prototype = {
button: button,
label: button.getAttribute("tooltiptext"),
visibilityswitch: "devtools." + options.id + ".enabled",
isTargetSupported: options.isTargetSupported ? options.isTargetSupported
: target => target.isLocalTab
isTargetSupported: options.isTargetSupported
? options.isTargetSupported
: target => target.isLocalTab
};
}).filter(button=>button);
},
@ -847,7 +912,7 @@ Toolbox.prototype = {
*/
setToolboxButtonsVisibility: function() {
this.toolboxButtons.forEach(buttonSpec => {
let { visibilityswitch, id, button, isTargetSupported } = buttonSpec;
let { visibilityswitch, button, isTargetSupported } = buttonSpec;
let on = true;
try {
on = Services.prefs.getBoolPref(visibilityswitch);
@ -939,7 +1004,7 @@ Toolbox.prototype = {
if (toolDefinition.label && !toolDefinition.iconOnly) {
let label = this.doc.createElement("label");
label.setAttribute("value", toolDefinition.label)
label.setAttribute("value", toolDefinition.label);
label.setAttribute("crop", "end");
label.setAttribute("flex", "1");
radio.appendChild(label);
@ -1018,7 +1083,7 @@ Toolbox.prototype = {
let definition = gDevTools.getToolDefinition(id);
if (!definition) {
deferred.reject(new Error("no such tool id "+id));
deferred.reject(new Error("no such tool id " + id));
return deferred.promise;
}
@ -1112,7 +1177,7 @@ Toolbox.prototype = {
let callback = () => {
iframe.removeEventListener("DOMContentLoaded", callback);
onLoad();
}
};
iframe.addEventListener("DOMContentLoaded", callback);
}
@ -1126,6 +1191,8 @@ Toolbox.prototype = {
* The id of the tool to switch to
*/
selectTool: function(id) {
this.emit("before-select", id);
let selected = this.doc.querySelector(".devtools-tab[selected]");
if (selected) {
selected.removeAttribute("selected");
@ -1364,7 +1431,7 @@ Toolbox.prototype = {
this._host.setTitle(title);
},
_listFrames: function (event) {
_listFrames: function(event) {
if (!this._target.activeTab || !this._target.activeTab.traits.frames) {
// We are not targetting a regular TabActor
// it can be either an addon or browser toolbox actor
@ -1379,7 +1446,7 @@ Toolbox.prototype = {
});
},
selectFrame: function (event) {
selectFrame: function(event) {
let windowId = event.target.getAttribute("data-window-id");
let packet = {
to: this._target.form.actor,
@ -1390,7 +1457,7 @@ Toolbox.prototype = {
// Wait for frameUpdate event to update the UI
},
_updateFrames: function (event, data) {
_updateFrames: function(event, data) {
if (!Services.prefs.getBoolPref("devtools.command-button-frames.enabled")) {
return;
}
@ -1420,7 +1487,7 @@ Toolbox.prototype = {
menu.removeAttribute("checked");
}
// Uncheck the previously selected frame
let selected = menu.querySelector("menuitem[checked=true]")
let selected = menu.querySelector("menuitem[checked=true]");
if (selected) {
selected.removeAttribute("checked");
}
@ -1458,8 +1525,8 @@ Toolbox.prototype = {
* Create a host object based on the given host type.
*
* Warning: some hosts require that the toolbox target provides a reference to
* the attached tab. Not all Targets have a tab property - make sure you correctly
* mix and match hosts and targets.
* the attached tab. Not all Targets have a tab property - make sure you
* correctly mix and match hosts and targets.
*
* @param {string} hostType
* The host type of the new host object
@ -1490,6 +1557,8 @@ Toolbox.prototype = {
return null;
}
this.emit("host-will-change", hostType);
let newHost = this._createHost(hostType);
return newHost.create().then(iframe => {
// change toolbox document's parent to the new host
@ -1670,32 +1739,72 @@ Toolbox.prototype = {
screenManager.primaryScreen.GetRect({}, {}, width, height);
let dims = width.value + "x" + height.value;
if (width.value < 800 || height.value < 600) return 0;
if (dims === "800x600") return 1;
if (dims === "1024x768") return 2;
if (dims === "1280x800") return 3;
if (dims === "1280x1024") return 4;
if (dims === "1366x768") return 5;
if (dims === "1440x900") return 6;
if (dims === "1920x1080") return 7;
if (dims === "2560×1440") return 8;
if (dims === "2560×1600") return 9;
if (dims === "2880x1800") return 10;
if (width.value > 2880 || height.value > 1800) return 12;
if (width.value < 800 || height.value < 600) {
return 0;
}
if (dims === "800x600") {
return 1;
}
if (dims === "1024x768") {
return 2;
}
if (dims === "1280x800") {
return 3;
}
if (dims === "1280x1024") {
return 4;
}
if (dims === "1366x768") {
return 5;
}
if (dims === "1440x900") {
return 6;
}
if (dims === "1920x1080") {
return 7;
}
if (dims === "2560×1440") {
return 8;
}
if (dims === "2560×1600") {
return 9;
}
if (dims === "2880x1800") {
return 10;
}
if (width.value > 2880 || height.value > 1800) {
return 12;
}
return 11; // Other dimension such as a VM.
// Other dimension such as a VM.
return 11;
},
_getOsCpu: function() {
if (oscpu.includes("NT 5.1") || oscpu.includes("NT 5.2")) return 0;
if (oscpu.includes("NT 6.0")) return 1;
if (oscpu.includes("NT 6.1")) return 2;
if (oscpu.includes("NT 6.2")) return 3;
if (oscpu.includes("NT 6.3")) return 4;
if (oscpu.includes("OS X")) return 5;
if (oscpu.includes("Linux")) return 6;
if (oscpu.includes("NT 5.1") || oscpu.includes("NT 5.2")) {
return 0;
}
if (oscpu.includes("NT 6.0")) {
return 1;
}
if (oscpu.includes("NT 6.1")) {
return 2;
}
if (oscpu.includes("NT 6.2")) {
return 3;
}
if (oscpu.includes("NT 6.3")) {
return 4;
}
if (oscpu.includes("OS X")) {
return 5;
}
if (oscpu.includes("Linux")) {
return 6;
}
return 12; // Other OS.
// Other OS.
return 12;
},
/**
@ -1869,8 +1978,8 @@ Toolbox.prototype = {
*/
_updateTextboxMenuItems: function() {
let window = this.doc.defaultView;
['cmd_undo', 'cmd_delete', 'cmd_cut',
'cmd_copy', 'cmd_paste','cmd_selectAll'].forEach(window.goUpdateCommand);
["cmd_undo", "cmd_delete", "cmd_cut",
"cmd_copy", "cmd_paste", "cmd_selectAll"].forEach(window.goUpdateCommand);
},
getPerformanceActorsConnection: function() {
@ -1920,7 +2029,7 @@ Toolbox.prototype = {
* Opens source in style editor. Falls back to plain "view-source:".
* @see browser/devtools/shared/source-utils.js
*/
viewSourceInStyleEditor: function (sourceURL, sourceLine) {
viewSourceInStyleEditor: function(sourceURL, sourceLine) {
return sourceUtils.viewSourceInStyleEditor(this, sourceURL, sourceLine);
},
@ -1928,7 +2037,7 @@ Toolbox.prototype = {
* Opens source in debugger. Falls back to plain "view-source:".
* @see browser/devtools/shared/source-utils.js
*/
viewSourceInDebugger: function (sourceURL, sourceLine) {
viewSourceInDebugger: function(sourceURL, sourceLine) {
return sourceUtils.viewSourceInDebugger(this, sourceURL, sourceLine);
},
@ -1941,7 +2050,7 @@ Toolbox.prototype = {
*
* @see browser/devtools/shared/source-utils.js
*/
viewSourceInScratchpad: function (sourceURL, sourceLine) {
viewSourceInScratchpad: function(sourceURL, sourceLine) {
return sourceUtils.viewSourceInScratchpad(sourceURL, sourceLine);
},
@ -1949,7 +2058,7 @@ Toolbox.prototype = {
* Opens source in plain "view-source:".
* @see browser/devtools/shared/source-utils.js
*/
viewSource: function (sourceURL, sourceLine) {
viewSource: function(sourceURL, sourceLine) {
return sourceUtils.viewSource(this, sourceURL, sourceLine);
},
};

View File

@ -437,6 +437,7 @@
@RESPATH@/components/nsUpdateTimerManager.js
@RESPATH@/components/addoncompat.manifest
@RESPATH@/components/multiprocessShims.js
@RESPATH@/components/defaultShims.js
@RESPATH@/components/remoteTagService.js
@RESPATH@/components/pluginGlue.manifest
@RESPATH@/components/ProcessSingleton.manifest

View File

@ -6,6 +6,20 @@ toolboxDockButtons.bottom.tooltip=Dock to bottom of browser window
toolboxDockButtons.side.tooltip=Dock to side of browser window
toolboxDockButtons.window.tooltip=Show in separate window
# LOCALIZATION NOTE (toolboxDockButtons.bottom.minimize): This string is shown
# as a tooltip that appears in the toolbox when it is in "bottom host" mode and
# when hovering over the minimize button in the toolbar. When clicked, the
# button minimizes the toolbox so that just the toolbar is visible at the
# bottom.
toolboxDockButtons.bottom.minimize=Minimize the toolbox
# LOCALIZATION NOTE (toolboxDockButtons.bottom.maximize): This string is shown
# as a tooltip that appears in the toolbox when it is in "bottom host" mode and
# when hovering over the maximize button in the toolbar. When clicked, the
# button maximizes the toolbox again (if it had been minimized before) so that
# the whole toolbox is visible again.
toolboxDockButtons.bottom.maximize=Maximize the toolbox
# LOCALIZATION NOTE (toolboxToggleButton.errors): Semi-colon list of plural
# forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals

View File

@ -377,6 +377,8 @@ browser.jar:
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-bottom-minimize@2x.png (../shared/devtools/images/dock-bottom-minimize@2x.png)
skin/classic/browser/devtools/dock-bottom-maximize@2x.png (../shared/devtools/images/dock-bottom-maximize@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)

View File

@ -495,6 +495,8 @@ browser.jar:
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-bottom-minimize@2x.png (../shared/devtools/images/dock-bottom-minimize@2x.png)
skin/classic/browser/devtools/dock-bottom-maximize@2x.png (../shared/devtools/images/dock-bottom-maximize@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
* skin/classic/browser/devtools/inspector.css (../shared/devtools/inspector.css)
skin/classic/browser/devtools/profiler-stopwatch.svg (../shared/devtools/images/profiler-stopwatch.svg)

View File

@ -22,6 +22,11 @@
%endif
}
/* Bottom-docked toolbox minimize transition */
.devtools-toolbox-bottom-iframe {
transition: margin-bottom .1s;
}
/* Splitters */
.devtools-horizontal-splitter {
-moz-appearance: none;
@ -48,6 +53,11 @@
cursor: e-resize;
}
.devtools-horizontal-splitter.disabled,
.devtools-side-splitter.disabled {
pointer-events: none;
}
.devtools-toolbox-side-iframe {
min-width: 465px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -24,6 +24,6 @@
</g>
</g>
</defs>
<use xlink:href="#pseudo-class-shape" id="pseudo-class" color="#edf0f1"/>
<use xlink:href="#pseudo-class-shape" id="pseudo-class" color="#babec3"/>
<use xlink:href="#pseudo-class-shape" id="pseudo-class-checked" color="#3089C9"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -657,6 +657,14 @@
background-image: url("chrome://browser/skin/devtools/undock@2x.png");
}
#toolbox-dock-bottom-minimize > image {
background-image: url("chrome://browser/skin/devtools/dock-bottom-minimize@2x.png");
}
#toolbox-dock-bottom-minimize.minimized > image {
background-image: url("chrome://browser/skin/devtools/dock-bottom-maximize@2x.png");
}
#toolbox-dock-window,
#toolbox-dock-bottom,
#toolbox-dock-side {

View File

@ -467,6 +467,8 @@ browser.jar:
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-bottom-minimize@2x.png (../shared/devtools/images/dock-bottom-minimize@2x.png)
skin/classic/browser/devtools/dock-bottom-maximize@2x.png (../shared/devtools/images/dock-bottom-maximize@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
skin/classic/browser/devtools/floating-scrollbars-light.css (devtools/floating-scrollbars-light.css)

View File

@ -49,14 +49,16 @@ this.mozIApplication = function(aApp) {
mozIApplication.prototype = {
hasPermission: function(aPermission) {
let uri = Services.io.newURI(this.origin, null, null);
let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
// This helper checks an URI inside |aApp|'s origin and part of |aApp| has a
// specific permission. It is not checking if browsers inside |aApp| have such
// permission.
let principal = secMan.getAppCodebasePrincipal(uri, this.localId,
/*mozbrowser*/false);
let principal = this.principal;
if (this.installerIsBrowser) {
let uri = Services.io.newURI(this.origin, null, null);
principal =
Services.scriptSecurityManager.getAppCodebasePrincipal(uri, this.localId,
/*mozbrowser*/false);
}
let perm = Services.perms.testExactPermissionFromPrincipal(principal,
aPermission);
return (perm === Ci.nsIPermissionManager.ALLOW_ACTION);
@ -71,6 +73,26 @@ mozIApplication.prototype = {
return this.widgetPages.find(equalCriterion) !== undefined;
},
get principal() {
if (this._principal) {
return this._principal;
}
this._principal = null;
try {
this._principal = Services.scriptSecurityManager.getAppCodebasePrincipal(
Services.io.newURI(this.origin, null, null),
this.localId,
this.installerIsBrowser
);
} catch(e) {
dump("Could not create app principal " + e + "\n");
}
return this._principal;
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.mozIApplication) ||
aIID.equals(Ci.nsISupports))

View File

@ -0,0 +1,65 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const {interfaces: Ci, utils: Cu} = Components;
Cu.import("resource:///modules/AppsUtils.jsm");
add_test(() => {
let app = {
name: "TestApp",
csp: "aCsp",
installOrigin: "http://installorigin.com",
origin: "http://www.example.com",
installTime: Date.now(),
manifestURL: "http://www.example.com/manifest.webapp",
appStatus: Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED,
removable: false,
id: 123,
localId: 123,
basePath: "/",
progress: 1.0,
installState: "installed",
downloadAvailable: false,
downloading: false,
lastUpdateCheck: Date.now(),
updateTime: Date.now(),
etag: "aEtag",
packageEtag: "aPackageEtag",
manifestHash: "aManifestHash",
packageHash: "aPackageHash",
staged: false,
installerAppId: 345,
installerIsBrowser: false,
storeId: "aStoreId",
storeVersion: 1,
role: "aRole",
redirects: "aRedirects",
kind: "aKind",
enabled: true,
sideloaded: false
};
let mozapp = new mozIApplication(app);
Object.keys(app).forEach((key) => {
if (key == "principal") {
return;
}
Assert.equal(app[key], mozapp[key],
"app[" + key + "] should be equal to mozapp[" + key + "]");
});
Assert.ok(mozapp.principal, "app principal should exist");
let expectedPrincipalOrigin = app.origin + "!appId=" + app.localId;
Assert.equal(mozapp.principal.origin, expectedPrincipalOrigin,
"app principal origin ok");
Assert.equal(mozapp.principal.appId, app.localId, "app principal appId ok");
Assert.equal(mozapp.principal.isInBrowserElement, app.installerIsBrowser,
"app principal isInBrowserElement ok");
run_next_test();
});
function run_test() {
run_next_test();
}

View File

@ -6,3 +6,4 @@ tail =
[test_inter_app_comm_service.js]
[test_manifestSanitizer.js]
[test_manifestHelper.js]
[test_moziapplication.js]

View File

@ -253,6 +253,7 @@ bool nsContentUtils::sIsResourceTimingEnabled = false;
bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
bool nsContentUtils::sEncodeDecodeURLHash = false;
bool nsContentUtils::sPrivacyResistFingerprinting = false;
uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
@ -533,6 +534,9 @@ nsContentUtils::Init()
Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
"dom.url.encode_decode_hash", false);
Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
"privacy.resistFingerprinting", false);
Preferences::AddUintVarCache(&sHandlingInputTimeout,
"dom.event.handling-user-input-time-limit",
1000);
@ -1989,6 +1993,16 @@ nsContentUtils::IsCallerChrome()
return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
}
bool
nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell)
{
if (!aDocShell) {
return false;
}
bool isChrome = nsContentUtils::IsChromeDoc(aDocShell->GetDocument());
return !isChrome && sPrivacyResistFingerprinting;
}
namespace mozilla {
namespace dom {
namespace workers {
@ -7773,3 +7787,16 @@ nsContentUtils::FirePageShowEvent(nsIDocShellTreeItem* aItem,
doc->OnPageShow(true, aChromeEventHandler);
}
}
/* static */
already_AddRefed<nsPIWindowRoot>
nsContentUtils::GetWindowRoot(nsIDocument* aDoc)
{
if (aDoc) {
nsPIDOMWindow* win = aDoc->GetWindow();
if (win) {
return win->GetTopWindowRoot();
}
}
return nullptr;
}

View File

@ -99,6 +99,7 @@ class nsViewportInfo;
class nsWrapperCache;
class nsAttrValue;
class nsITransferable;
class nsPIWindowRoot;
struct JSPropertyDescriptor;
struct JSRuntime;
@ -198,6 +199,9 @@ public:
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc);
// Check whether we should avoid leaking distinguishing information to JS/CSS.
static bool ShouldResistFingerprinting(nsIDocShell* aDocShell);
/**
* Returns the parent node of aChild crossing document boundaries.
* Uses the parent node in the composed document.
@ -1912,6 +1916,16 @@ public:
return sEncodeDecodeURLHash;
}
/*
* Returns true if the browser should attempt to prevent content scripts
* from collecting distinctive information about the browser that could
* be used to "fingerprint" and track the user across websites.
*/
static bool ResistFingerprinting()
{
return sPrivacyResistFingerprinting;
}
/**
* Returns true if the doc tree branch which contains aDoc contains any
* plugins which we don't control event dispatch for, i.e. do any plugins
@ -2350,6 +2364,8 @@ public:
static void FirePageHideEvent(nsIDocShellTreeItem* aItem,
mozilla::dom::EventTarget* aChromeEventHandler);
static already_AddRefed<nsPIWindowRoot> GetWindowRoot(nsIDocument* aDoc);
private:
static bool InitializeEventTable();
@ -2450,6 +2466,7 @@ private:
static bool sIsUserTimingLoggingEnabled;
static bool sIsExperimentalAutocompleteEnabled;
static bool sEncodeDecodeURLHash;
static bool sPrivacyResistFingerprinting;
static nsHtml5StringParser* sHTMLFragmentParser;
static nsIParser* sXMLFragmentParser;

View File

@ -70,7 +70,6 @@
#endif
#include "Layers.h"
#include "mozilla/layers/ShadowLayers.h"
#include "gfxPrefs.h"
#include "mozilla/dom/Element.h"
@ -82,6 +81,8 @@
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/layers/FrameUniformityData.h"
#include "mozilla/layers/ShadowLayers.h"
#include "nsPrintfCString.h"
#include "nsViewportInfo.h"
#include "nsIFormControl.h"
@ -3716,6 +3717,27 @@ nsDOMWindowUtils::SetChromeMargin(int32_t aTop,
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetFrameUniformityTestData(JSContext* aContext,
JS::MutableHandleValue aOutFrameUniformity)
{
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
nsIWidget* widget = GetWidget();
if (!widget) {
return NS_ERROR_NOT_AVAILABLE;
}
nsRefPtr<LayerManager> manager = widget->GetLayerManager();
if (!manager) {
return NS_ERROR_NOT_AVAILABLE;
}
FrameUniformityData outData;
manager->GetFrameUniformity(&outData);
outData.ToJS(aOutFrameUniformity, aContext);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::XpconnectArgument(nsIDOMWindowUtils* aThis)
{

View File

@ -5003,6 +5003,12 @@ nsGlobalWindow::GetOuterSize(ErrorResult& aError)
{
MOZ_ASSERT(IsOuterWindow());
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
CSSIntSize size;
aError = GetInnerSize(size);
return nsIntSize(size.width, size.height);
}
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
if (!treeOwnerAsWin) {
aError.Throw(NS_ERROR_FAILURE);
@ -5167,6 +5173,11 @@ nsGlobalWindow::GetScreenXY(ErrorResult& aError)
{
MOZ_ASSERT(IsOuterWindow());
// When resisting fingerprinting, always return (0,0)
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
return nsIntPoint(0, 0);
}
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
if (!treeOwnerAsWin) {
aError.Throw(NS_ERROR_FAILURE);
@ -5240,6 +5251,11 @@ nsGlobalWindow::GetMozInnerScreenX(ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenX, (aError), aError, 0);
// When resisting fingerprinting, always return 0.
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
return 0.0;
}
nsRect r = GetInnerScreenRect();
return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
}
@ -5258,6 +5274,11 @@ nsGlobalWindow::GetMozInnerScreenY(ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenY, (aError), aError, 0);
// Return 0 to prevent fingerprinting.
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
return 0.0;
}
nsRect r = GetInnerScreenRect();
return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
}
@ -5286,6 +5307,10 @@ nsGlobalWindow::GetDevicePixelRatio(ErrorResult& aError)
return 1.0;
}
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
return 1.0;
}
return float(nsPresContext::AppUnitsPerCSSPixel())/
presContext->AppUnitsPerDevPixel();
}

View File

@ -1059,6 +1059,8 @@ public:
bool aShowDialog, mozilla::ErrorResult& aError);
uint64_t GetMozPaintCount(mozilla::ErrorResult& aError);
bool ShouldResistFingerprinting();
mozilla::dom::MozSelfSupport* GetMozSelfSupport(mozilla::ErrorResult& aError);
already_AddRefed<nsIDOMWindow> OpenDialog(JSContext* aCx,

View File

@ -9,14 +9,21 @@
#include "nsISupports.h"
#include "mozilla/dom/EventTarget.h"
#include "nsWeakReference.h"
class nsPIDOMWindow;
class nsIControllers;
class nsIController;
namespace mozilla {
namespace dom {
class TabParent;
}
}
#define NS_IWINDOWROOT_IID \
{ 0x728a2682, 0x55c0, 0x4860, \
{ 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } }
{ 0x238edca0, 0xb30d, 0x46d3, \
{ 0xb2, 0x6a, 0x17, 0xb6, 0x21, 0x28, 0x89, 0x7e } }
class nsPIWindowRoot : public mozilla::dom::EventTarget
{
@ -38,6 +45,15 @@ public:
virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0;
virtual mozilla::dom::EventTarget* GetParentTarget() = 0;
// Stores a weak reference to the browser.
virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) = 0;
virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) = 0;
typedef void (*BrowserEnumerator)(mozilla::dom::TabParent* aTab, void* aArg);
// Enumerate all stored browsers that for which the weak reference is valid.
virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID)

View File

@ -68,6 +68,11 @@ NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper)
int32_t
nsScreen::GetPixelDepth(ErrorResult& aRv)
{
// Return 24 to prevent fingerprinting.
if (ShouldResistFingerprinting()) {
return 24;
}
nsDeviceContext* context = GetDeviceContext();
if (!context) {
@ -111,6 +116,11 @@ nsScreen::GetDeviceContext()
nsresult
nsScreen::GetRect(nsRect& aRect)
{
// Return window inner rect to prevent fingerprinting.
if (ShouldResistFingerprinting()) {
return GetWindowInnerRect(aRect);
}
nsDeviceContext *context = GetDeviceContext();
if (!context) {
@ -130,6 +140,11 @@ nsScreen::GetRect(nsRect& aRect)
nsresult
nsScreen::GetAvailRect(nsRect& aRect)
{
// Return window inner rect to prevent fingerprinting.
if (ShouldResistFingerprinting()) {
return GetWindowInnerRect(aRect);
}
nsDeviceContext *context = GetDeviceContext();
if (!context) {
@ -166,22 +181,26 @@ nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration)
void
nsScreen::GetMozOrientation(nsString& aOrientation)
{
switch (mOrientation) {
case eScreenOrientation_PortraitPrimary:
aOrientation.AssignLiteral("portrait-primary");
break;
case eScreenOrientation_PortraitSecondary:
aOrientation.AssignLiteral("portrait-secondary");
break;
case eScreenOrientation_LandscapePrimary:
if (ShouldResistFingerprinting()) {
aOrientation.AssignLiteral("landscape-primary");
break;
case eScreenOrientation_LandscapeSecondary:
aOrientation.AssignLiteral("landscape-secondary");
break;
case eScreenOrientation_None:
default:
MOZ_CRASH("Unacceptable mOrientation value");
} else {
switch (mOrientation) {
case eScreenOrientation_PortraitPrimary:
aOrientation.AssignLiteral("portrait-primary");
break;
case eScreenOrientation_PortraitSecondary:
aOrientation.AssignLiteral("portrait-secondary");
break;
case eScreenOrientation_LandscapePrimary:
aOrientation.AssignLiteral("landscape-primary");
break;
case eScreenOrientation_LandscapeSecondary:
aOrientation.AssignLiteral("landscape-secondary");
break;
case eScreenOrientation_None:
default:
MOZ_CRASH("Unacceptable mOrientation value");
}
}
}
@ -373,3 +392,27 @@ nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent)
return NS_OK;
}
nsresult
nsScreen::GetWindowInnerRect(nsRect& aRect)
{
aRect.x = 0;
aRect.y = 0;
nsCOMPtr<nsIDOMWindow> win = GetOwner();
if (!win) {
return NS_ERROR_FAILURE;
}
nsresult rv = win->GetInnerWidth(&aRect.width);
NS_ENSURE_SUCCESS(rv, rv);
return win->GetInnerHeight(&aRect.height);
}
bool nsScreen::ShouldResistFingerprinting() const
{
bool resist = false;
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
if (owner) {
resist = nsContentUtils::ShouldResistFingerprinting(owner->GetDocShell());
}
return resist;
}

View File

@ -131,6 +131,7 @@ protected:
nsDeviceContext* GetDeviceContext();
nsresult GetRect(nsRect& aRect);
nsresult GetAvailRect(nsRect& aRect);
nsresult GetWindowInnerRect(nsRect& aRect);
mozilla::dom::ScreenOrientation mOrientation;
@ -158,6 +159,8 @@ private:
bool IsDeviceSizePageSize();
bool ShouldResistFingerprinting() const;
nsRefPtr<FullScreenEventListener> mEventListener;
};

View File

@ -24,6 +24,7 @@
#include "nsIController.h"
#include "xpcpublic.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/dom/TabParent.h"
#ifdef MOZ_XUL
#include "nsIDOMXULElement.h"
@ -385,6 +386,46 @@ nsWindowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return mozilla::dom::WindowRootBinding::Wrap(aCx, this, aGivenProto);
}
void
nsWindowRoot::AddBrowser(mozilla::dom::TabParent* aBrowser)
{
nsWeakPtr weakBrowser = do_GetWeakReference(static_cast<nsITabParent*>(aBrowser));
mWeakBrowsers.PutEntry(weakBrowser);
}
void
nsWindowRoot::RemoveBrowser(mozilla::dom::TabParent* aBrowser)
{
nsWeakPtr weakBrowser = do_GetWeakReference(static_cast<nsITabParent*>(aBrowser));
mWeakBrowsers.RemoveEntry(weakBrowser);
}
static PLDHashOperator
WeakBrowserEnumFunc(nsRefPtrHashKey<nsIWeakReference>* aKey, void* aArg)
{
nsTArray<nsRefPtr<TabParent>>* tabParents =
static_cast<nsTArray<nsRefPtr<TabParent>>*>(aArg);
nsCOMPtr<nsITabParent> tabParent(do_QueryReferent((*aKey).GetKey()));
TabParent* tab = TabParent::GetFrom(tabParent);
if (tab) {
tabParents->AppendElement(tab);
}
return PL_DHASH_NEXT;
}
void
nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg)
{
// Collect strong references to all browsers in a separate array in
// case aEnumFunc alters mWeakBrowsers.
nsTArray<nsRefPtr<TabParent>> tabParents;
mWeakBrowsers.EnumerateEntries(WeakBrowserEnumFunc, &tabParents);
for (uint32_t i = 0; i < tabParents.Length(); ++i) {
aEnumFunc(tabParents[i], aArg);
}
}
///////////////////////////////////////////////////////////////////////////////////
already_AddRefed<EventTarget>

View File

@ -69,6 +69,10 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot,
nsIDOMEventTarget)
virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) override;
virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) override;
virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void *aArg) override;
protected:
virtual ~nsWindowRoot();
@ -84,6 +88,10 @@ protected:
nsCOMPtr<nsIDOMNode> mPopupNode; // [OWNER]
nsCOMPtr<mozilla::dom::EventTarget> mParent;
// The TabParents that are currently registered with this top-level window.
typedef nsTHashtable<nsRefPtrHashKey<nsIWeakReference>> WeakBrowserTable;
WeakBrowserTable mWeakBrowsers;
};
extern already_AddRefed<mozilla::dom::EventTarget>

View File

@ -0,0 +1,71 @@
// The main test function.
let test = function (isContent) {
SimpleTest.waitForExplicitFinish();
let { ww } = SpecialPowers.Services;
window.chromeWindow = ww.activeWindow;
// The pairs of values expected to be the same when
// fingerprinting resistance is enabled.
let pairs = [
["screenX", 0],
["screenY", 0],
["mozInnerScreenX", 0],
["mozInnerScreenY", 0],
["screen.pixelDepth", 24],
["screen.colorDepth", 24],
["screen.availWidth", "innerWidth"],
["screen.availHeight", "innerHeight"],
["screen.left", 0],
["screen.top", 0],
["screen.availLeft", 0],
["screen.availTop", 0],
["screen.width", "innerWidth"],
["screen.height", "innerHeight"],
["screen.mozOrientation", "'landscape-primary'"],
["devicePixelRatio", 1]
];
// checkPair: tests if members of pair [a, b] are equal when evaluated.
let checkPair = function (a, b) {
is(eval(a), eval(b), a + " should be equal to " + b);
};
// Returns generator object that iterates through pref values.
let prefVals = (for (prefVal of [false, true]) prefVal);
// The main test function, runs until all pref values are exhausted.
let nextTest = function () {
let {value : prefValue, done} = prefVals.next();
if (done) {
SimpleTest.finish();
return;
}
SpecialPowers.pushPrefEnv({set : [["privacy.resistFingerprinting", prefValue]]},
function () {
// We will be resisting fingerprinting if the pref is enabled,
// and we are in a content script (not chrome).
let resisting = prefValue && isContent;
// Check each of the pairs.
pairs.map(function ([item, onVal]) {
if (resisting) {
checkPair("window." + item, onVal);
} else {
if (!item.startsWith("moz")) {
checkPair("window." + item, "chromeWindow." + item);
}
}
});
if (!resisting) {
// Hard to predict these values, but we can enforce constraints:
ok(window.mozInnerScreenX >= chromeWindow.mozInnerScreenX,
"mozInnerScreenX");
ok(window.mozInnerScreenY >= chromeWindow.mozInnerScreenY,
"mozInnerScreenY");
}
nextTest();
});
}
nextTest();
}

View File

@ -3,6 +3,7 @@ skip-if = buildapp == 'b2g'
support-files =
blockNoPlugins.xml
blockPluginHard.xml
bug418986-1.js
cpows_child.js
cpows_parent.xul
file_bug391728.html
@ -31,6 +32,7 @@ support-files =
[test_bug380418.html^headers^]
[test_bug383430.html]
[test_bug391728.html]
[test_bug418986-1.xul]
[test_bug421622.xul]
[test_bug429785.xul]
[test_bug430050.xul]

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=418986-1
-->
<window title="Mozilla Bug 418986 (Part 1)"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986-1"
target="_blank">Mozilla Bug 418986 (Part 1)</a>
<script type="application/javascript;version=1.7" src="bug418986-1.js"></script>
<!-- test code goes here -->
<script type="application/javascript"><![CDATA[
window.onload = function() {
test(false);
};
]]></script>
</body>
</window>

View File

@ -56,6 +56,7 @@ support-files =
bug704320.sjs
bug704320_counter.sjs
bug819051.sjs
chrome/bug418986-1.js
copypaste.js
delayedServerEvents.sjs
echo.sjs
@ -453,6 +454,7 @@ support-files = test_bug402150.html^headers^
[test_bug417255.html]
[test_bug417384.html]
[test_bug418214.html]
[test_bug418986-1.html]
[test_bug419132.html]
[test_bug419527.xhtml]
[test_bug420609.xhtml]

View File

@ -0,0 +1,24 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=418986
-->
<head>
<meta charset="utf-8">
<title>Test 1/3 for Bug 418986 - Resist fingerprinting by preventing exposure of screen and system info</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript;version=1.7" src="chrome/bug418986-1.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">Bug 418986</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script>
window.onload = function() {
test(true);
};
</script>
</body>
</html>

View File

@ -890,6 +890,13 @@ Event::GetScreenCoords(nsPresContext* aPresContext,
WidgetEvent* aEvent,
LayoutDeviceIntPoint aPoint)
{
if (!nsContentUtils::IsCallerChrome() &&
nsContentUtils::ResistFingerprinting()) {
// When resisting fingerprinting, return client coordinates instead.
CSSIntPoint clientCoords = GetClientCoords(aPresContext, aEvent, aPoint, CSSIntPoint(0, 0));
return LayoutDeviceIntPoint(clientCoords.x, clientCoords.y);
}
if (EventStateManager::sIsPointerLocked) {
return EventStateManager::sLastScreenPoint;
}

View File

@ -0,0 +1,69 @@
SimpleTest.waitForExplicitFinish();
// The main testing function.
let test = function (isContent) {
// Each definition is [eventType, prefSetting]
// Where we are setting the "privacy.resistFingerprinting" pref.
let eventDefs = [["mousedown", true],
["mouseup", true],
["mousedown", false],
["mouseup", false]];
let testCounter = 0;
// Declare ahead of time.
let setup;
// This function is called when the event handler fires.
let handleEvent = function (event, prefVal) {
let resisting = prefVal && isContent;
if (resisting) {
is(event.screenX, event.clientX, "event.screenX and event.clientX should be the same");
is(event.screenY, event.clientY, "event.screenY and event.clientY should be the same");
} else {
// We can't be sure about X coordinates not being equal, but we can test Y.
isnot(event.screenY, event.clientY, "event.screenY !== event.clientY");
}
++testCounter;
if (testCounter < eventDefs.length) {
nextTest();
} else {
SimpleTest.finish();
}
};
// In this function, we set up the nth div and event handler,
// and then synthesize a mouse event in the div, to test
// whether the resulting events resist fingerprinting by
// suppressing absolute screen coordinates.
nextTest = function () {
let [eventType, prefVal] = eventDefs[testCounter];
SpecialPowers.pushPrefEnv({set:[["privacy.resistFingerprinting", prefVal]]},
function () {
// The following code creates a new div for each event in eventDefs,
// attaches a listener to listen for the event, and then generates
// a fake event at the center of the div.
let div = document.createElement("div");
div.style.width = "10px";
div.style.height = "10px";
div.style.backgroundColor = "red";
// Name the div after the event we're listening for.
div.id = eventType;
document.getElementById("body").appendChild(div);
// Seems we can't add an event listener in chrome unless we run
// it in a later task.
window.setTimeout(function() {
div.addEventListener(eventType, event => handleEvent(event, prefVal), false);
// For some reason, the following synthesizeMouseAtCenter call only seems to run if we
// wrap it in a window.setTimeout(..., 0).
window.setTimeout(function () {
synthesizeMouseAtCenter(div, {type : eventType});
}, 0);
}, 0);
});
};
// Now run by starting with the 0th event.
nextTest();
};

View File

@ -3,6 +3,7 @@ skip-if = buildapp == 'b2g'
support-files =
bug415498-doc1.html
bug415498-doc2.html
bug418986-3.js
bug591249_iframe.xul
bug602962.xul
file_bug679494.html
@ -12,6 +13,7 @@ support-files =
[test_bug336682_2.xul]
[test_bug368835.html]
[test_bug415498.xul]
[test_bug418986-3.xul]
[test_bug524674.xul]
[test_bug586961.xul]
[test_bug591249.xul]

View File

@ -7,6 +7,7 @@ support-files =
bug426082.html
bug648573.html
bug656379-1.html
bug418986-3.js
error_event_worker.js
empty.js
window_bug493251.html
@ -38,6 +39,9 @@ support-files = test_bug336682.js
[test_bug409604.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
[test_bug412567.html]
[test_bug418986-3.html]
# Sometimes fails to finish after tests pass on 'B2G ICS Emulator'.
skip-if = (os == 'b2g')
[test_bug422132.html]
skip-if = buildapp == 'b2g' || e10s # b2g(2 failures out of 8, mousewheel test) b2g-debug(2 failures out of 8, mousewheel test) b2g-desktop(2 failures out of 8, mousewheel test)
[test_bug426082.html]

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=418986
-->
<head>
<meta charset="utf-8">
<title>Test 3/3 for Bug 418986 - Resist fingerprinting by preventing exposure of screen and system info</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body id="body">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">Bug 418986</a>
<p id="display"></p>
<pre id="test"></pre>
<script type="application/javascript;version=1.7" src="bug418986-3.js"></script>
<script type="application/javascript;version=1.7">
// This test produces fake mouse events and checks that the screenX and screenY
// properties of the received event objects provide client window coordinates.
// Run the test once the window has loaded.
window.onload = () => test(true);
</script>
</body>
</html>

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<!--
Bug 418986
-->
<window title="Mozilla Bug 418986"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<body id="body" xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">
Mozilla Bug 418986</a>
</body>
<script type="application/javascript;version=1.7" src="bug418986-3.js"></script>
<script type="application/javascript;version=1.7"><![CDATA[
// This test produces fake mouse events and checks that the screenX and screenY
// properties of the received event objects provide client window coordinates.
// Run the test once the window has loaded.
test(false);
]]></script>
</window>

View File

@ -105,6 +105,8 @@ static PRLogModuleInfo* gMediaElementEventsLog;
#include "nsIPermissionManager.h"
#include "nsContentTypeParser.h"
#include "mozilla/EventStateManager.h"
using namespace mozilla::layers;
using mozilla::net::nsMediaFragmentURIParser;
@ -2183,6 +2185,13 @@ HTMLMediaElement::ResetConnectionState()
void
HTMLMediaElement::Play(ErrorResult& aRv)
{
// Prevent media element from being auto-started by a script when
// media.autoplay.enabled=false
if (!IsAutoplayEnabled() && !EventStateManager::IsHandlingUserInput() && !nsContentUtils::IsCallerChrome()) {
LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
return;
}
StopSuspendingAfterFirstFrame();
SetPlayedOrSeeked(true);
@ -2711,11 +2720,7 @@ nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
return NS_ERROR_FAILURE;
}
double duration = aOriginal->GetDuration();
if (duration >= 0) {
decoder->SetDuration(duration);
decoder->SetMediaSeekable(aOriginal->IsMediaSeekable());
}
decoder->SetMediaSeekable(aOriginal->IsMediaSeekable());
nsRefPtr<MediaResource> resource = originalResource->CloneData(decoder);
if (!resource) {

View File

@ -3256,7 +3256,6 @@ nsHTMLDocument::ExecCommand(const nsAString& commandID,
// if editing is not on, bail
if (!isCutCopy && !IsEditingOnAfterFlush()) {
rv.Throw(NS_ERROR_FAILURE);
return false;
}
@ -3293,7 +3292,6 @@ nsHTMLDocument::ExecCommand(const nsAString& commandID,
bool restricted = commandID.LowerCaseEqualsLiteral("paste");
if (restricted && !nsContentUtils::IsCallerChrome()) {
rv = NS_ERROR_DOM_SECURITY_ERR;
return false;
}
@ -3390,7 +3388,6 @@ nsHTMLDocument::QueryCommandEnabled(const nsAString& commandID, ErrorResult& rv)
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
rv.Throw(NS_ERROR_FAILURE);
return false;
}
@ -3433,7 +3430,6 @@ nsHTMLDocument::QueryCommandIndeterm(const nsAString& commandID, ErrorResult& rv
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
rv.Throw(NS_ERROR_FAILURE);
return false;
}
@ -3494,7 +3490,6 @@ nsHTMLDocument::QueryCommandState(const nsAString& commandID, ErrorResult& rv)
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
rv.Throw(NS_ERROR_FAILURE);
return false;
}
@ -3617,7 +3612,6 @@ nsHTMLDocument::QueryCommandValue(const nsAString& commandID,
// if editing is not on, bail
if (!IsEditingOnAfterFlush()) {
rv.Throw(NS_ERROR_FAILURE);
return;
}

View File

@ -9,8 +9,6 @@
"Editable b: execCommand() must not throw, uncanceled":true,
"Editable b: beforeinput event, uncanceled":true,
"Editable b: input event, uncanceled":true,
"No editable content: execCommand() must not throw, canceled":true,
"No editable content: execCommand() must not throw, uncanceled":true,
"Changing selection from handler: beforeinput event, canceled":true,
"Changing selection from handler: input event, canceled":true,
"Changing selection from handler: beforeinput event, uncanceled":true,

View File

@ -7,11 +7,13 @@
#include "domstubs.idl"
interface nsIPrincipal;
/**
* We expose Gecko-internal helpers related to "web apps" through this
* sub-interface.
*/
[scriptable, uuid(1d856b11-ac29-47d3-bd52-a86e3d45fcf5)]
[scriptable, uuid(e76aa5e0-80b2-404f-bccc-1067828bb6ed)]
interface mozIApplication: nsISupports
{
/* Return true if this app has |permission|. */
@ -58,4 +60,7 @@ interface mozIApplication: nsISupports
/* Returns the kind of the app. */
readonly attribute DOMString kind;
/* Returns the app's principal */
readonly attribute nsIPrincipal principal;
};

View File

@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
interface nsIContentPermissionRequest;
interface nsIObserver;
[scriptable, uuid(098d9f0d-7809-4d3c-8fc6-e5b3fb71835b)]
[scriptable, uuid(ec176f3b-2886-4090-938e-dded103c5f1c)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -1814,6 +1814,14 @@ interface nsIDOMWindowUtils : nsISupports {
attribute boolean serviceWorkersTestingEnabled;
/**
* Returns a JSObject which contains a list of frame uniformities
* when the pref gfx.vsync.collect-scroll-data is enabled.
* Every result contains a layer address and a frame uniformity for that layer.
* A negative frame uniformity value indicates an invalid frame uniformity and an error has occured.
*/
[implicit_jscontext] jsval getFrameUniformityTestData();
/*
* Increase the chaos mode activation level. An equivalent number of
* calls to leaveChaosMode must be made in order to restore the original
* chaos mode state. If the activation level is nonzero all chaos mode

View File

@ -25,6 +25,7 @@ using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
using struct gfxSize from "gfxPoint.h";
using CSSRect from "Units.h";
using CSSSize from "Units.h";
using LayoutDeviceIntRect from "Units.h";
using mozilla::LayoutDeviceIntPoint from "Units.h";
using ScreenIntSize from "Units.h";
@ -548,7 +549,7 @@ child:
CacheFileDescriptor(nsString path, FileDescriptor fd);
UpdateDimensions(IntRect rect, ScreenIntSize size, ScreenOrientation orientation,
UpdateDimensions(CSSRect rect, CSSSize size, ScreenOrientation orientation,
LayoutDeviceIntPoint chromeDisp) compressall;
UpdateFrame(FrameMetrics frame);

View File

@ -92,6 +92,7 @@
#include "nsIPermissionManager.h"
#include "nsIScriptError.h"
#include "mozilla/EventForwards.h"
#include "nsDeviceContext.h"
#define BROWSER_ELEMENT_CHILD_SCRIPT \
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
@ -165,7 +166,6 @@ NS_IMPL_ISUPPORTS(TabChild::DelayedFireContextMenuEvent,
TabChildBase::TabChildBase()
: mContentDocumentIsDisplayed(false)
, mTabChildGlobal(nullptr)
, mInnerSize(0, 0)
{
mozilla::HoldJSObjects(this);
}
@ -236,9 +236,11 @@ TabChildBase::InitializeRootMetrics()
mLastRootMetrics.SetViewport(CSSRect(CSSPoint(), kDefaultViewportSize));
mLastRootMetrics.SetCompositionBounds(ParentLayerRect(
ParentLayerPoint(),
ParentLayerSize(ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenIsParentLayerForRoot))));
ParentLayerSize(
ViewAs<ParentLayerPixel>(GetInnerSize(),
PixelCastJustification::ScreenIsParentLayerForRoot))));
mLastRootMetrics.SetZoom(CSSToParentLayerScale2D(
ConvertScaleForRoot(CalculateIntrinsicScale(mInnerSize, kDefaultViewportSize))));
ConvertScaleForRoot(CalculateIntrinsicScale(GetInnerSize(), kDefaultViewportSize))));
mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale());
// We use ParentLayerToLayerScale(1) below in order to turn the
// async zoom amount into the gecko zoom amount.
@ -299,14 +301,14 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
}
TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n",
Stringify(aOldScreenSize).c_str(), Stringify(mInnerSize).c_str());
Stringify(aOldScreenSize).c_str(), Stringify(GetInnerSize()).c_str());
nsCOMPtr<nsIDocument> document(GetDocument());
if (!document) {
return false;
}
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, GetInnerSize());
uint32_t presShellId = 0;
mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID;
bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
@ -323,8 +325,8 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
constraints);
}
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
float screenW = GetInnerSize().width;
float screenH = GetInnerSize().height;
CSSSize viewport(viewportInfo.GetSize());
// We're not being displayed in any way; don't bother doing anything because
@ -358,15 +360,15 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
ScreenIntSize oldScreenSize = aOldScreenSize;
if (oldScreenSize == ScreenIntSize()) {
oldScreenSize = mInnerSize;
oldScreenSize = GetInnerSize();
}
FrameMetrics metrics(mLastRootMetrics);
metrics.SetViewport(CSSRect(CSSPoint(), viewport));
// Calculate the composition bounds based on mInnerSize, excluding the sizes
// Calculate the composition bounds based on the inner size, excluding the sizes
// of the scrollbars if they are not overlay scrollbars.
ScreenSize compositionSize(mInnerSize);
ScreenSize compositionSize(GetInnerSize());
nsCOMPtr<nsIPresShell> shell = GetPresShell();
if (shell) {
nsMargin scrollbarsAppUnits =
@ -380,7 +382,9 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
metrics.SetCompositionBounds(ParentLayerRect(
ParentLayerPoint(),
ParentLayerSize(ViewAs<ParentLayerPixel>(compositionSize, PixelCastJustification::ScreenIsParentLayerForRoot))));
ParentLayerSize(
ViewAs<ParentLayerPixel>(GetInnerSize(),
PixelCastJustification::ScreenIsParentLayerForRoot))));
metrics.SetRootCompositionSize(
ScreenSize(compositionSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.GetDevPixelsPerCSSPixel());
@ -397,7 +401,7 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
// within the screen width. Note that "actual content" may be different with
// respect to CSS pixels because of the CSS viewport size changing.
CSSToScreenScale oldIntrinsicScale = CalculateIntrinsicScale(oldScreenSize, oldBrowserSize);
CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(mInnerSize, viewport);
CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(GetInnerSize(), viewport);
metrics.ZoomBy(newIntrinsicScale.scale / oldIntrinsicScale.scale);
// Changing the zoom when we're not doing a first paint will get ignored
@ -881,7 +885,6 @@ TabChild::TabChild(nsIContentChild* aManager,
, mManager(aManager)
, mChromeFlags(aChromeFlags)
, mLayersId(0)
, mOuterRect(0, 0, 0, 0)
, mActivePointerId(-1)
, mAppPackageFileDescriptorRecved(false)
, mLastBackgroundColor(NS_RGB(255, 255, 255))
@ -922,9 +925,9 @@ TabChild::HandleEvent(nsIDOMEvent* aEvent)
if (eventType.EqualsLiteral("DOMMetaAdded")) {
// This meta data may or may not have been a meta viewport tag. If it was,
// we should handle it immediately.
HandlePossibleViewportChange(mInnerSize);
HandlePossibleViewportChange(GetInnerSize());
} else if (eventType.EqualsLiteral("FullZoomChange")) {
HandlePossibleViewportChange(mInnerSize);
HandlePossibleViewportChange(GetInnerSize());
}
return NS_OK;
@ -966,14 +969,14 @@ TabChild::Observe(nsISupports *aSubject,
// In some cases before-first-paint gets called before
// RecvUpdateDimensions is called and therefore before we have an
// mInnerSize value set. In such cases defer initializing the viewport
// inner size value set. In such cases defer initializing the viewport
// until we we get an inner size.
if (HasValidInnerSize()) {
InitializeRootMetrics();
if (shell) {
nsLayoutUtils::SetResolutionAndScaleTo(shell, mLastRootMetrics.GetPresShellResolution());
}
HandlePossibleViewportChange(mInnerSize);
HandlePossibleViewportChange(GetInnerSize());
}
}
}
@ -1313,17 +1316,18 @@ NS_IMETHODIMP
TabChild::GetDimensions(uint32_t aFlags, int32_t* aX,
int32_t* aY, int32_t* aCx, int32_t* aCy)
{
ScreenIntRect rect = GetOuterRect();
if (aX) {
*aX = mOuterRect.x;
*aX = rect.x;
}
if (aY) {
*aY = mOuterRect.y;
*aY = rect.y;
}
if (aCx) {
*aCx = mOuterRect.width;
*aCx = rect.width;
}
if (aCy) {
*aCy = mOuterRect.height;
*aCy = rect.height;
}
return NS_OK;
@ -2050,37 +2054,41 @@ TabChild::RecvShow(const ScreenIntSize& aSize,
}
bool
TabChild::RecvUpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
const ScreenOrientation& orientation, const LayoutDeviceIntPoint& chromeDisp)
TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
const ScreenOrientation& orientation,
const LayoutDeviceIntPoint& chromeDisp)
{
if (!mRemoteFrame) {
return true;
}
mOuterRect = rect;
mUnscaledOuterRect = rect;
mChromeDisp = chromeDisp;
bool initialSizing = !HasValidInnerSize()
&& (size.width != 0 && size.height != 0);
mOrientation = orientation;
ScreenIntSize oldScreenSize = GetInnerSize();
SetUnscaledInnerSize(size);
ScreenIntSize screenSize = GetInnerSize();
bool sizeChanged = true;
if (initialSizing) {
mHasValidInnerSize = true;
} else if (mInnerSize == size) {
} else if (screenSize == oldScreenSize) {
sizeChanged = false;
}
mOrientation = orientation;
ScreenIntSize oldScreenSize = mInnerSize;
mInnerSize = size;
mWidget->Resize(rect.x + chromeDisp.x, rect.y + chromeDisp.y, size.width, size.height,
true);
ScreenIntRect screenRect = GetOuterRect();
mWidget->Resize(screenRect.x + chromeDisp.x, screenRect.y + chromeDisp.y,
screenSize.width, screenSize.height, true);
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, size.width, size.height,
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
true);
if (initialSizing && mContentDocumentIsDisplayed) {
// If this is the first time we're getting a valid mInnerSize, and the
// If this is the first time we're getting a valid inner size, and the
// before-first-paint event has already been handled, then we need to set
// up our default viewport here. See the corresponding call to
// InitializeRootMetrics in the before-first-paint handler.
@ -3244,6 +3252,7 @@ TabChild::RecvRequestNotifyAfterRemotePaint()
bool
TabChild::RecvUIResolutionChanged()
{
ScreenIntSize oldScreenSize = GetInnerSize();
mDPI = 0;
mDefaultScale = 0;
static_cast<PuppetWidget*>(mWidget.get())->ClearBackingScaleCache();
@ -3252,9 +3261,21 @@ TabChild::RecvUIResolutionChanged()
if (presShell) {
nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
if (presContext) {
presContext->UIResolutionChanged();
presContext->UIResolutionChangedSync();
}
}
ScreenIntSize screenSize = GetInnerSize();
if (mHasValidInnerSize && oldScreenSize != screenSize) {
ScreenIntRect screenRect = GetOuterRect();
mWidget->Resize(screenRect.x + mChromeDisp.x, screenRect.y + mChromeDisp.y,
screenSize.width, screenSize.height, true);
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
true);
}
return true;
}
@ -3316,6 +3337,22 @@ TabChild::CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut)
return rv;
}
ScreenIntSize
TabChild::GetInnerSize()
{
LayoutDeviceIntSize innerSize =
RoundedToInt(mUnscaledInnerSize * mWidget->GetDefaultScale());
return ViewAs<ScreenPixel>(innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
};
ScreenIntRect
TabChild::GetOuterRect()
{
LayoutDeviceIntRect outerRect =
RoundedToInt(mUnscaledOuterRect * mWidget->GetDefaultScale());
return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
}
TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild)
: mTabChild(aTabChild)
{

View File

@ -189,6 +189,8 @@ public:
const bool& aIsRoot,
const mozilla::layers::ZoomConstraints& aConstraints) = 0;
virtual ScreenIntSize GetInnerSize() = 0;
protected:
virtual ~TabChildBase();
CSSSize GetPageSize(nsCOMPtr<nsIDocument> aDocument, const CSSSize& aViewport);
@ -222,7 +224,6 @@ protected:
CSSSize mOldViewportSize;
bool mContentDocumentIsDisplayed;
nsRefPtr<TabChildGlobal> mTabChildGlobal;
ScreenIntSize mInnerSize;
mozilla::layers::FrameMetrics mLastRootMetrics;
nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome;
};
@ -319,8 +320,8 @@ public:
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame,
const bool& aParentIsActive) override;
virtual bool RecvUpdateDimensions(const nsIntRect& rect,
const ScreenIntSize& size,
virtual bool RecvUpdateDimensions(const CSSRect& rect,
const CSSSize& size,
const ScreenOrientation& orientation,
const LayoutDeviceIntPoint& chromeDisp) override;
virtual bool RecvUpdateFrame(const layers::FrameMetrics& aFrameMetrics) override;
@ -510,6 +511,8 @@ public:
}
bool AsyncPanZoomEnabled() { return mAsyncPanZoomEnabled; }
virtual ScreenIntSize GetInnerSize() override;
protected:
virtual ~TabChild();
@ -599,6 +602,12 @@ private:
void SetTabId(const TabId& aTabId);
ScreenIntRect GetOuterRect();
void SetUnscaledInnerSize(const CSSSize& aSize) {
mUnscaledInnerSize = aSize;
}
class CachedFileDescriptorInfo;
class CachedFileDescriptorCallbackRunnable;
class DelayedDeleteRunnable;
@ -611,7 +620,7 @@ private:
nsRefPtr<nsIContentChild> mManager;
uint32_t mChromeFlags;
uint64_t mLayersId;
nsIntRect mOuterRect;
CSSRect mUnscaledOuterRect;
// When we're tracking a possible tap gesture, this is the "down"
// point of the touchstart.
LayoutDevicePoint mGestureDownPoint;
@ -646,6 +655,7 @@ private:
bool mIPCOpen;
bool mParentIsActive;
bool mAsyncPanZoomEnabled;
CSSSize mUnscaledInnerSize;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

View File

@ -90,6 +90,7 @@
#include "nsPIWindowRoot.h"
#include "gfxDrawable.h"
#include "ImageOps.h"
#include "UnitTransforms.h"
#include <algorithm>
using namespace mozilla::dom;
@ -333,9 +334,27 @@ TabParent::SetOwnerElement(Element* aElement)
// If we held previous content then unregister for its events.
RemoveWindowListeners();
// If we change top-level documents then we need to change our
// registration with them.
nsRefPtr<nsPIWindowRoot> curTopLevelWin, newTopLevelWin;
if (mFrameElement) {
curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc());
}
if (aElement) {
newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc());
}
bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin;
if (curTopLevelWin && !isSameTopLevelWin) {
curTopLevelWin->RemoveBrowser(this);
}
// Update to the new content, and register to listen for events from it.
mFrameElement = aElement;
if (newTopLevelWin && !isSameTopLevelWin) {
newTopLevelWin->AddBrowser(this);
}
AddWindowListeners();
TryCacheDPIAndScale();
}
@ -966,7 +985,21 @@ TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size)
mOrientation = orientation;
mChromeOffset = chromeOffset;
unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, mChromeOffset);
CSSToLayoutDeviceScale widgetScale;
if (widget) {
widgetScale = widget->GetDefaultScale();
}
LayoutDeviceIntRect devicePixelRect =
ViewAs<LayoutDevicePixel>(mRect,
PixelCastJustification::LayoutDeviceIsScreenForTabDims);
LayoutDeviceIntSize devicePixelSize =
ViewAs<LayoutDevicePixel>(mDimensions.ToUnknownSize(),
PixelCastJustification::LayoutDeviceIsScreenForTabDims);
CSSRect unscaledRect = devicePixelRect / widgetScale;
CSSSize unscaledSize = devicePixelSize / widgetScale;
unused << SendUpdateDimensions(unscaledRect, unscaledSize, orientation, chromeOffset);
}
}
@ -985,6 +1018,7 @@ TabParent::UIResolutionChanged()
// TryCacheDPIAndScale()'s cache is keyed off of
// mDPI being greater than 0, so this invalidates it.
mDPI = -1;
TryCacheDPIAndScale();
unused << SendUIResolutionChanged();
}
}

View File

@ -74,9 +74,6 @@ public:
// Return the duration of the media in microseconds.
virtual int64_t GetMediaDuration() = 0;
// Set the duration of the media in microseconds.
virtual void SetMediaDuration(int64_t aDuration) = 0;
// Sets the duration of the media in microseconds. The MediaDecoder
// fires a durationchange event to its owner (e.g., an HTML audio
// tag).
@ -100,9 +97,6 @@ public:
virtual void RemoveMediaTracks() = 0;
// Set the media end time in microseconds
virtual void SetMediaEndTime(int64_t aTime) = 0;
// May be called by the reader to notify this decoder that the metadata from
// the media file has been read. Call on the decode thread only.
virtual void OnReadMetadataCompleted() = 0;

View File

@ -69,10 +69,8 @@ private:
bool mStreamFinishedOnMainThread;
};
DecodedStreamData::DecodedStreamData(int64_t aInitialTime,
SourceMediaStream* aStream)
DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream)
: mAudioFramesWritten(0)
, mInitialTime(aInitialTime)
, mNextVideoTime(-1)
, mNextAudioTime(-1)
, mStreamInitialized(false)
@ -103,9 +101,9 @@ DecodedStreamData::IsFinished() const
}
int64_t
DecodedStreamData::GetClock() const
DecodedStreamData::GetPosition() const
{
return mInitialTime + mListener->GetLastOutputTime();
return mListener->GetLastOutputTime();
}
class OutputStreamListener : public MediaStreamListener {
@ -223,7 +221,7 @@ DecodedStream::DestroyData()
}
void
DecodedStream::RecreateData(int64_t aInitialTime, MediaStreamGraph* aGraph)
DecodedStream::RecreateData(MediaStreamGraph* aGraph)
{
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
@ -235,7 +233,7 @@ DecodedStream::RecreateData(int64_t aInitialTime, MediaStreamGraph* aGraph)
}
auto source = aGraph->CreateSourceStream(nullptr);
DestroyData();
mData.reset(new DecodedStreamData(aInitialTime, source));
mData.reset(new DecodedStreamData(source));
// Note that the delay between removing ports in DestroyDecodedStream
// and adding new ones won't cause a glitch since all graph operations

View File

@ -37,19 +37,16 @@ class Image;
*/
class DecodedStreamData {
public:
DecodedStreamData(int64_t aInitialTime, SourceMediaStream* aStream);
explicit DecodedStreamData(SourceMediaStream* aStream);
~DecodedStreamData();
bool IsFinished() const;
int64_t GetClock() const;
int64_t GetPosition() const;
/* The following group of fields are protected by the decoder's monitor
* and can be read or written on any thread.
*/
// Count of audio frames written to the stream
int64_t mAudioFramesWritten;
// Saved value of aInitialTime. Timestamp of the first audio and/or
// video packet written.
const int64_t mInitialTime; // microseconds
// mNextVideoTime is the end timestamp for the last packet sent to the stream.
// Therefore video packets starting at or after this time need to be copied
// to the output stream.
@ -95,7 +92,7 @@ public:
explicit DecodedStream(ReentrantMonitor& aMonitor);
DecodedStreamData* GetData() const;
void DestroyData();
void RecreateData(int64_t aInitialTime, MediaStreamGraph* aGraph);
void RecreateData(MediaStreamGraph* aGraph);
nsTArray<OutputStreamData>& OutputStreams();
ReentrantMonitor& GetReentrantMonitor() const;
void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);

View File

@ -32,8 +32,9 @@
#include "WMFDecoder.h"
#endif
using namespace mozilla::layers;
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::media;
// Default timeout msecs until try to enter dormant state by heuristic.
static const int DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS = 60000;
@ -48,6 +49,12 @@ namespace mozilla {
// fluctuating bitrates.
static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
// The amount of instability we tollerate in calls to
// MediaDecoder::UpdateEstimatedMediaDuration(); changes of duration
// less than this are ignored, as they're assumed to be the result of
// instability in the duration estimation.
static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
// avoid redefined macro in unified build
#undef DECODER_LOG
@ -343,6 +350,10 @@ MediaDecoder::MediaDecoder() :
mMediaSeekable(true),
mSameOriginMedia(false),
mReentrantMonitor("media.decoder"),
mEstimatedDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mEstimatedDuration (Canonical)"),
mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
"MediaDecoder::mExplicitDuration (Canonical)"),
mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
"MediaDecoder::mPlayState (Canonical)"),
mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
@ -488,7 +499,6 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
void MediaDecoder::SetStateMachineParameters()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mDecoderStateMachine->SetDuration(mDuration);
if (mMinimizePreroll) {
mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
}
@ -1065,12 +1075,12 @@ void MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisib
}
}
void MediaDecoder::DurationChanged()
void MediaDecoder::DurationChanged(TimeUnit aNewDuration)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
int64_t oldDuration = mDuration;
mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
mDuration = aNewDuration.ToMicroseconds();
// Duration has changed so we should recompute playback rate
UpdatePlaybackRate();
@ -1080,33 +1090,10 @@ void MediaDecoder::DurationChanged()
DECODER_LOG("Duration changed to %lld", mDuration);
mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
}
}
void MediaDecoder::SetDuration(double aDuration)
{
MOZ_ASSERT(NS_IsMainThread());
if (mozilla::IsInfinite(aDuration)) {
SetInfinite(true);
} else if (IsNaN(aDuration)) {
mDuration = -1;
SetInfinite(true);
} else {
mDuration = static_cast<int64_t>(NS_round(aDuration * static_cast<double>(USECS_PER_S)));
if (CurrentPosition() > aNewDuration.ToMicroseconds()) {
Seek(aNewDuration.ToSeconds(), SeekTarget::Accurate);
}
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mDecoderStateMachine) {
mDecoderStateMachine->SetDuration(mDuration);
}
// Duration has changed so we should recompute playback rate
UpdatePlaybackRate();
}
void MediaDecoder::SetMediaDuration(int64_t aDuration)
{
NS_ENSURE_TRUE_VOID(GetStateMachine());
GetStateMachine()->SetDuration(aDuration);
}
void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
@ -1114,8 +1101,17 @@ void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
if (mPlayState <= PLAY_STATE_LOADING) {
return;
}
NS_ENSURE_TRUE_VOID(GetStateMachine());
GetStateMachine()->UpdateEstimatedDuration(aDuration);
// The duration is only changed if its significantly different than the
// the current estimate, as the incoming duration is an estimate and so
// often is unstable as more data is read and the estimate is updated.
// Can result in a durationchangeevent. aDuration is in microseconds.
if (mEstimatedDuration.Ref().isSome() &&
mozilla::Abs(mEstimatedDuration.Ref().ref().ToMicroseconds() - aDuration) < ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
return;
}
mEstimatedDuration = Some(TimeUnit::FromMicroseconds(aDuration));
}
void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
@ -1164,12 +1160,6 @@ void MediaDecoder::SetFragmentEndTime(double aTime)
}
}
void MediaDecoder::SetMediaEndTime(int64_t aTime)
{
NS_ENSURE_TRUE_VOID(GetStateMachine());
GetStateMachine()->SetMediaEndTime(aTime);
}
void MediaDecoder::Suspend()
{
MOZ_ASSERT(NS_IsMainThread());

View File

@ -456,14 +456,6 @@ public:
// Call on the main thread only.
virtual bool IsEndedOrShutdown() const;
// Set the duration of the media resource in units of seconds.
// This is called via a channel listener if it can pick up the duration
// from a content header. Must be called from the main thread only.
virtual void SetDuration(double aDuration);
// Sets the initial duration of the media. Called while the media metadata
// is being read and the decode is being setup.
void SetMediaDuration(int64_t aDuration) override;
// Updates the media duration. This is called while the media is being
// played, calls before the media has reached loaded metadata are ignored.
// The duration is assumed to be an estimate, and so a degree of
@ -491,9 +483,6 @@ public:
// this point the media pauses. aTime is in seconds.
virtual void SetFragmentEndTime(double aTime);
// Set the end time of the media. aTime is in microseconds.
void SetMediaEndTime(int64_t aTime) final override;
// Invalidate the frame.
void Invalidate();
void InvalidateWithFlags(uint32_t aFlags);
@ -525,7 +514,7 @@ public:
// Called by the state machine to notify the decoder that the duration
// has changed.
void DurationChanged();
void DurationChanged(media::TimeUnit aNewDuration);
bool OnStateMachineTaskQueue() const override;
@ -993,7 +982,26 @@ private:
nsRefPtr<CDMProxy> mProxy;
#endif
// Media duration according to the demuxer's current estimate.
//
// Note that it's quite bizarre for this to live on the main thread - it would
// make much more sense for this to be owned by the demuxer's task queue. But
// currently this is only every changed in NotifyDataArrived, which runs on
// the main thread. That will need to be cleaned up at some point.
Canonical<media::NullableTimeUnit> mEstimatedDuration;
public:
AbstractCanonical<media::NullableTimeUnit>* CanonicalEstimatedDuration() { return &mEstimatedDuration; }
protected:
// Media duration set explicitly by JS. At present, this is only ever present
// for MSE.
Canonical<Maybe<double>> mExplicitDuration;
double ExplicitDuration() { return mExplicitDuration.Ref().ref(); }
void SetExplicitDuration(double aValue) { mExplicitDuration.Set(Some(aValue)); }
public:
AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration() { return &mExplicitDuration; }
protected:
// Set to one of the valid play states.
// This can only be changed on the main thread while holding the decoder
// monitor. Thus, it can be safely read while holding the decoder monitor

View File

@ -44,9 +44,10 @@
namespace mozilla {
using namespace mozilla::layers;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::media;
#define NS_DispatchToMainThread(...) CompileError_UseAbstractThreadDispatchInstead
@ -162,12 +163,6 @@ static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS,
} // namespace detail
// The amount of instability we tollerate in calls to
// MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
// less than this are ignored, as they're assumed to be the result of
// instability in the duration estimation.
static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
static TimeDuration UsecsToDuration(int64_t aUsecs) {
return TimeDuration::FromMicroseconds(aUsecs);
}
@ -197,6 +192,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mStartTime(-1),
mEndTime(-1),
mDurationSet(false),
mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
"MediaDecoderStateMachine::EstimatedDuration (Mirror)"),
mExplicitDuration(mTaskQueue, Maybe<double>(),
"MediaDecoderStateMachine::mExplicitDuration (Mirror)"),
mObservedDuration(TimeUnit(), "MediaDecoderStateMachine::mObservedDuration"),
mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING,
"MediaDecoderStateMachine::mPlayState (Mirror)"),
mNextPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_PAUSED,
@ -208,6 +208,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mFragmentEndTime(-1),
mReader(aReader),
mCurrentPosition(mTaskQueue, 0, "MediaDecoderStateMachine::mCurrentPosition (Canonical)"),
mStreamStartTime(-1),
mAudioStartTime(-1),
mAudioEndTime(-1),
mDecodedAudioEndTime(-1),
@ -227,7 +228,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mAudioCompleted(false, "MediaDecoderStateMachine::mAudioCompleted"),
mGotDurationFromMetaData(false),
mDispatchedEventToDecode(false),
mStopAudioThread(true),
mQuickBuffering(false),
mMinimizePreroll(false),
mDecodeThreadWaiting(false),
@ -294,6 +294,8 @@ MediaDecoderStateMachine::InitializationTask()
MOZ_ASSERT(OnTaskQueue());
// Connect mirrors.
mEstimatedDuration.Connect(mDecoder->CanonicalEstimatedDuration());
mExplicitDuration.Connect(mDecoder->CanonicalExplicitDuration());
mPlayState.Connect(mDecoder->CanonicalPlayState());
mNextPlayState.Connect(mDecoder->CanonicalNextPlayState());
mLogicallySeeking.Connect(mDecoder->CanonicalLogicallySeeking());
@ -307,6 +309,9 @@ MediaDecoderStateMachine::InitializationTask()
mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::UpdateStreamBlockingForPlayState);
@ -358,7 +363,7 @@ void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
// This logic has to mimic AudioSink closely to make sure we write
// the exact same silences
CheckedInt64 audioWrittenOffset = aStream->mAudioFramesWritten +
UsecsToFrames(mInfo.mAudio.mRate, aStream->mInitialTime + mStartTime);
UsecsToFrames(mInfo.mAudio.mRate, mStreamStartTime);
CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime);
if (!audioWrittenOffset.isValid() ||
@ -443,6 +448,7 @@ void MediaDecoderStateMachine::SendStreamData()
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
MOZ_ASSERT(mStreamStartTime != -1);
DecodedStreamData* stream = GetDecodedStream();
@ -461,7 +467,7 @@ void MediaDecoderStateMachine::SendStreamData()
SourceMediaStream::ADDTRACK_QUEUED);
stream->mStream->DispatchWhenNotEnoughBuffered(audioTrackId,
TaskQueue(), GetWakeDecoderRunnable());
stream->mNextAudioTime = mStartTime + stream->mInitialTime;
stream->mNextAudioTime = mStreamStartTime;
}
if (mInfo.HasVideo()) {
TrackID videoTrackId = mInfo.mVideo.mTrackId;
@ -471,11 +477,7 @@ void MediaDecoderStateMachine::SendStreamData()
stream->mStream->DispatchWhenNotEnoughBuffered(videoTrackId,
TaskQueue(), GetWakeDecoderRunnable());
// TODO: We can't initialize |mNextVideoTime| until |mStartTime|
// is set. This is a good indication that DecodedStreamData is in
// deep coupling with the state machine and we should move the class
// into MediaDecoderStateMachine.
stream->mNextVideoTime = mStartTime + stream->mInitialTime;
stream->mNextVideoTime = mStreamStartTime;
}
mediaStream->FinishAddTracks();
stream->mStreamInitialized = true;
@ -578,7 +580,7 @@ void MediaDecoderStateMachine::SendStreamData()
}
endPosition = std::max(endPosition,
mediaStream->MicrosecondsToStreamTimeRoundDown(
stream->mNextVideoTime - stream->mInitialTime));
stream->mNextVideoTime - mStreamStartTime));
}
if (!stream->mHaveSentFinish) {
@ -1344,15 +1346,8 @@ void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
NS_ASSERTION(mStartTime >= 0, "Should have positive mStartTime");
mCurrentPosition = aTime - mStartTime;
NS_ASSERTION(mCurrentPosition >= 0, "CurrentTime should be positive!");
if (aTime > mEndTime) {
NS_ASSERTION(mCurrentPosition > GetDuration(),
"CurrentTime must be after duration if aTime > endTime!");
DECODER_LOG("Setting new end time to %lld", aTime);
mEndTime = aTime;
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
AbstractThread::MainThread()->Dispatch(event.forget());
}
mObservedDuration = std::max(mObservedDuration.Ref(),
TimeUnit::FromMicroseconds(mCurrentPosition.Ref()));
}
void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime)
@ -1438,79 +1433,70 @@ int64_t MediaDecoderStateMachine::GetEndTime()
return mEndTime;
}
// Runnable which dispatches an event to the main thread to seek to the new
// aSeekTarget.
class SeekRunnable : public nsRunnable {
public:
SeekRunnable(MediaDecoder* aDecoder, double aSeekTarget)
: mDecoder(aDecoder), mSeekTarget(aSeekTarget) {}
NS_IMETHOD Run() {
mDecoder->Seek(mSeekTarget, SeekTarget::Accurate);
return NS_OK;
}
private:
nsRefPtr<MediaDecoder> mDecoder;
double mSeekTarget;
};
void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
void MediaDecoderStateMachine::RecomputeDuration()
{
MOZ_ASSERT(NS_IsMainThread() || OnDecodeTaskQueue());
AssertCurrentThreadInMonitor();
MOZ_ASSERT(OnTaskQueue());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (aDuration < 0) {
mDurationSet = false;
// We dispatch DurationChanged to the MediaDecoder when the duration changes
// sometime after initialization, unless it has already been fired by the code
// that set the new duration.
bool fireDurationChanged = false;
TimeUnit duration;
if (mExplicitDuration.Ref().isSome()) {
double d = mExplicitDuration.Ref().ref();
if (IsNaN(d)) {
// We have an explicit duration (which means that we shouldn't look at
// any other duration sources), but the duration isn't ready yet.
return;
}
// We don't fire duration changed for this case because it should have
// already been fired on the main thread when the explicit duration was set.
duration = TimeUnit::FromSeconds(d);
} else if (mEstimatedDuration.Ref().isSome()) {
duration = mEstimatedDuration.Ref().ref();
fireDurationChanged = true;
} else if (mInfo.mMetadataDuration.isSome()) {
duration = mInfo.mMetadataDuration.ref();
} else if (mInfo.mMetadataEndTime.isSome() && mStartTime >= 0) {
duration = mInfo.mMetadataEndTime.ref() - TimeUnit::FromMicroseconds(mStartTime);
} else {
return;
}
if (duration < mObservedDuration.Ref()) {
duration = mObservedDuration;
fireDurationChanged = true;
}
fireDurationChanged = fireDurationChanged && duration.ToMicroseconds() != GetDuration();
SetDuration(duration);
if (fireDurationChanged) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethodWithArg<TimeUnit>(mDecoder, &MediaDecoder::DurationChanged, duration);
AbstractThread::MainThread()->Dispatch(event.forget());
}
}
void MediaDecoderStateMachine::SetDuration(TimeUnit aDuration)
{
MOZ_ASSERT(OnTaskQueue());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(aDuration.ToMicroseconds() >= 0);
mDurationSet = true;
if (mStartTime == -1) {
SetStartTime(0);
}
if (aDuration == INT64_MAX) {
if (aDuration.IsInfinite()) {
mEndTime = -1;
return;
}
mEndTime = mStartTime + aDuration;
if (mDecoder && mEndTime >= 0 && mEndTime < mCurrentPosition.ReadOnWrongThread()) {
// The current playback position is now past the end of the element duration
// the user agent must also seek to the time of the end of the media
// resource.
if (NS_IsMainThread()) {
// Seek synchronously.
mDecoder->Seek(double(mEndTime) / USECS_PER_S, SeekTarget::Accurate);
} else {
// Queue seek to new end position.
nsCOMPtr<nsIRunnable> task =
new SeekRunnable(mDecoder, double(mEndTime) / USECS_PER_S);
AbstractThread::MainThread()->Dispatch(task.forget());
}
}
}
void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
{
AssertCurrentThreadInMonitor();
int64_t duration = GetDuration();
if (aDuration != duration &&
mozilla::Abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
SetDuration(aDuration);
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
AbstractThread::MainThread()->Dispatch(event.forget());
}
}
void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
{
MOZ_ASSERT(OnDecodeTaskQueue());
AssertCurrentThreadInMonitor();
mEndTime = aEndTime;
mEndTime = mStartTime + aDuration.ToMicroseconds();
}
void MediaDecoderStateMachine::SetFragmentEndTime(int64_t aEndTime)
@ -1799,17 +1785,6 @@ void MediaDecoderStateMachine::StopAudioThread()
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
if (mStopAudioThread) {
// Audio sink is being stopped in another thread. Wait until finished.
while (mAudioSink) {
mDecoder->GetReentrantMonitor().Wait();
}
return;
}
mStopAudioThread = true;
// Wake up audio sink so that it can reach the finish line.
mDecoder->GetReentrantMonitor().NotifyAll();
if (mAudioSink) {
DECODER_LOG("Shutdown audio thread");
mAudioSink->PrepareToShutdown();
@ -1819,8 +1794,6 @@ void MediaDecoderStateMachine::StopAudioThread()
}
mAudioSink = nullptr;
}
// Wake up those waiting for audio sink to finish.
mDecoder->GetReentrantMonitor().NotifyAll();
}
nsresult
@ -1922,16 +1895,9 @@ MediaDecoderStateMachine::InitiateSeek()
mCurrentSeek.mTarget.mTime = seekTime;
if (mAudioCaptured) {
// TODO: We should re-create the decoded stream after seek completed as we do
// for audio thread since it is until then we know which position we seek to
// as far as fast-seek is concerned. It also fix the problem where stream
// clock seems to go backwards during seeking.
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethodWithArgs<int64_t, MediaStreamGraph*>(this,
&MediaDecoderStateMachine::RecreateDecodedStream,
seekTime - mStartTime,
nullptr);
AbstractThread::MainThread()->Dispatch(event.forget());
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArgs<MediaStreamGraph*>(
this, &MediaDecoderStateMachine::RecreateDecodedStream, nullptr);
AbstractThread::MainThread()->Dispatch(r.forget());
}
mDropAudioUntilNextDiscontinuity = HasAudio();
@ -2092,11 +2058,10 @@ MediaDecoderStateMachine::StartAudioThread()
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
if (mAudioCaptured) {
NS_ASSERTION(mStopAudioThread, "mStopAudioThread must always be true if audio is captured");
MOZ_ASSERT(!mAudioSink);
return NS_OK;
}
mStopAudioThread = false;
if (HasAudio() && !mAudioSink) {
// The audio end time should always be at least the audio start time.
mAudioEndTime = mAudioStartTime;
@ -2241,6 +2206,10 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
mInfo = aMetadata->mInfo;
mMetadataTags = aMetadata->mTags.forget();
if (mInfo.mMetadataDuration.isSome() || mInfo.mMetadataEndTime.isSome()) {
RecomputeDuration();
}
if (HasVideo()) {
DECODER_LOG("Video decode isAsync=%d HWAccel=%d videoQueueSize=%d",
mReader->IsAsync(),
@ -2250,7 +2219,6 @@ MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
mDecoder->StartProgressUpdates();
mGotDurationFromMetaData = (GetDuration() != -1) || mDurationSet;
if (mGotDurationFromMetaData) {
// We have all the information required: duration and size
// Inform the element that we've loaded the metadata.
@ -2486,6 +2454,7 @@ MediaDecoderStateMachine::SeekCompleted()
} else {
newCurrentTime = video ? video->mTime : seekTime;
}
mStreamStartTime = newCurrentTime;
mPlayDuration = newCurrentTime - mStartTime;
mDecoder->StartProgressUpdates();
@ -2578,6 +2547,8 @@ MediaDecoderStateMachine::FinishShutdown()
mPendingWakeDecoder = nullptr;
// Disconnect canonicals and mirrors before shutting down our task queue.
mEstimatedDuration.DisconnectIfConnected();
mExplicitDuration.DisconnectIfConnected();
mPlayState.DisconnectIfConnected();
mNextPlayState.DisconnectIfConnected();
mLogicallySeeking.DisconnectIfConnected();
@ -2621,11 +2592,6 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
mDelayedScheduler.Reset(); // Must happen on state machine task queue.
mDispatchedStateMachine = false;
// If audio is being captured, stop the audio sink if it's running
if (mAudioCaptured) {
StopAudioThread();
}
MediaResource* resource = mDecoder->GetResource();
NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
@ -2805,6 +2771,7 @@ MediaDecoderStateMachine::Reset()
mVideoFrameEndTime = -1;
mDecodedVideoEndTime = -1;
mStreamStartTime = -1;
mAudioStartTime = -1;
mAudioEndTime = -1;
mDecodedAudioEndTime = -1;
@ -2891,6 +2858,14 @@ MediaDecoderStateMachine::GetAudioClock() const
(mAudioSink ? mAudioSink->GetPosition() : 0);
}
int64_t MediaDecoderStateMachine::GetStreamClock() const
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
MOZ_ASSERT(mStreamStartTime != -1);
return mStreamStartTime + GetDecodedStream()->GetPosition();
}
int64_t MediaDecoderStateMachine::GetVideoStreamPosition() const
{
AssertCurrentThreadInMonitor();
@ -2920,17 +2895,15 @@ int64_t MediaDecoderStateMachine::GetClock() const
clock_time = mPlayDuration + mStartTime;
} else {
if (mAudioCaptured) {
clock_time = mStartTime + GetDecodedStream()->GetClock();
clock_time = GetStreamClock();
} else if (HasAudio() && !mAudioCompleted) {
clock_time = GetAudioClock();
} else {
// Audio is disabled on this system. Sync to the system clock.
clock_time = GetVideoStreamPosition();
}
// Ensure the clock can never go backwards.
// Note we allow clock going backwards in capture mode - we should fix this in bug 1162381.
NS_ASSERTION(GetMediaTime() <= clock_time || mPlaybackRate <= 0 || mAudioCaptured,
"Clock should go forwards.");
NS_ASSERTION(GetMediaTime() <= clock_time || mPlaybackRate <= 0,
"Clock should go forwards.");
}
return clock_time;
@ -3241,7 +3214,10 @@ void MediaDecoderStateMachine::SetStartTime(int64_t aStartTimeUsecs)
// first actual audio frame we have, we'll inject silence during playback
// to ensure the audio starts at the correct time.
mAudioStartTime = mStartTime;
mStreamStartTime = mStartTime;
DECODER_LOG("Set media start time to %lld", mStartTime);
RecomputeDuration();
}
void MediaDecoderStateMachine::UpdateNextFrameStatus()
@ -3523,6 +3499,12 @@ void MediaDecoderStateMachine::DispatchAudioCaptured()
MOZ_ASSERT(self->OnTaskQueue());
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
if (!self->mAudioCaptured) {
// Stop the audio sink if it's running.
self->StopAudioThread();
// GetMediaTime() could return -1 because we haven't decoded
// the 1st frame. But this is OK since we will update mStreamStartTime
// again in SetStartTime().
self->mStreamStartTime = self->GetMediaTime();
self->mAudioCaptured = true;
self->ScheduleStateMachine();
}
@ -3538,7 +3520,7 @@ void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (!GetDecodedStream()) {
RecreateDecodedStream(mCurrentPosition.ReadOnWrongThread(), aStream->Graph());
RecreateDecodedStream(aStream->Graph());
}
mDecodedStream.Connect(aStream, aFinishWhenEnded);
DispatchAudioCaptured();
@ -3577,13 +3559,11 @@ void MediaDecoderStateMachine::UpdateStreamBlockingForStateMachinePlaying()
}
}
void MediaDecoderStateMachine::RecreateDecodedStream(int64_t aInitialTime,
MediaStreamGraph* aGraph)
void MediaDecoderStateMachine::RecreateDecodedStream(MediaStreamGraph* aGraph)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
DECODER_LOG("RecreateDecodedStream aInitialTime=%lld!", aInitialTime);
mDecodedStream.RecreateData(aInitialTime, aGraph);
mDecodedStream.RecreateData(aGraph);
}
} // namespace mozilla

View File

@ -171,9 +171,8 @@ private:
// Recreates mDecodedStream. Call this to create mDecodedStream at first,
// and when seeking, to ensure a new stream is set up with fresh buffers.
// aInitialTime is relative to mStartTime.
// Decoder monitor must be held.
void RecreateDecodedStream(int64_t aInitialTime, MediaStreamGraph* aGraph);
void RecreateDecodedStream(MediaStreamGraph* aGraph);
void Shutdown();
public:
@ -205,19 +204,7 @@ public:
// media metadata. The decoder monitor must be obtained before calling this.
// aDuration is in microseconds.
// A value of INT64_MAX will be treated as infinity.
void SetDuration(int64_t aDuration);
// Called while decoding metadata to set the end time of the media
// resource. The decoder monitor must be obtained before calling this.
// aEndTime is in microseconds.
void SetMediaEndTime(int64_t aEndTime);
// Called from main thread to update the duration with an estimated value.
// The duration is only changed if its significantly different than the
// the current duration, as the incoming duration is an estimate and so
// often is unstable as more data is read and the estimate is updated.
// Can result in a durationchangeevent. aDuration is in microseconds.
void UpdateEstimatedDuration(int64_t aDuration);
void SetDuration(media::TimeUnit aDuration);
// Functions used by assertions to ensure we're calling things
// on the appropriate threads.
@ -549,6 +536,8 @@ protected:
// Called on the state machine thread.
int64_t GetAudioClock() const;
int64_t GetStreamClock() const;
// Get the video stream position, taking the |playbackRate| change into
// account. This is a position in the media, not the duration of the playback
// so far.
@ -899,11 +888,24 @@ public:
// It will be set to -1 if the duration is infinite
int64_t mEndTime;
// Recomputes the canonical duration from various sources.
void RecomputeDuration();
// Will be set when SetDuration has been called with a value != -1
// mDurationSet false doesn't indicate that we do not have a valid duration
// as mStartTime and mEndTime could have been set separately.
bool mDurationSet;
// The duration according to the demuxer's current estimate, mirrored from the main thread.
Mirror<media::NullableTimeUnit> mEstimatedDuration;
// The duration explicitly set by JS, mirrored from the main thread.
Mirror<Maybe<double>> mExplicitDuration;
// The highest timestamp that our position has reached. Monotonically
// increasing.
Watchable<media::TimeUnit> mObservedDuration;
// The current play state and next play state, mirrored from the main thread.
Mirror<MediaDecoder::PlayState> mPlayState;
Mirror<MediaDecoder::PlayState> mNextPlayState;
@ -999,6 +1001,9 @@ protected:
public:
AbstractCanonical<int64_t>* CanonicalCurrentPosition() { return &mCurrentPosition; }
protected:
// The presentation time of the first audio/video frame that is sent to the
// media stream.
int64_t mStreamStartTime;
// The presentation time of the first audio frame that was played in
// microseconds. We can add this to the audio stream position to determine
@ -1186,8 +1191,8 @@ protected:
}
// True if we shouldn't play our audio (but still write it to any capturing
// streams). When this is true, mStopAudioThread is always true and
// the audio thread will never start again after it has stopped.
// streams). When this is true, the audio thread will never start again after
// it has stopped.
bool mAudioCaptured;
// True if an event to notify about a change in the playback
@ -1215,10 +1220,6 @@ protected:
// unneccessary runnables, since the decode thread runs in a loop.
bool mDispatchedEventToDecode;
// False while audio thread should be running. Accessed state machine
// and audio threads. Syncrhonised by decoder monitor.
bool mStopAudioThread;
// If this is true while we're in buffering mode, we can exit early,
// as it's likely we may be able to playback. This happens when we enter
// buffering mode soon after the decode starts, because the decode-ahead

View File

@ -24,6 +24,8 @@
#include "mozilla/CDMProxy.h"
#endif
using namespace mozilla::media;
using mozilla::layers::Image;
using mozilla::layers::LayerManager;
using mozilla::layers::LayersBackend;
@ -332,8 +334,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
int64_t duration = std::max(videoDuration, audioDuration);
if (duration != -1) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(duration);
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
}
mSeekable = mDemuxer->IsSeekable();

View File

@ -15,6 +15,7 @@
#include "ImageTypes.h"
#include "MediaData.h"
#include "StreamBuffer.h" // for TrackID
#include "TimeUnits.h"
namespace mozilla {
@ -348,6 +349,14 @@ public:
VideoInfo mVideo;
AudioInfo mAudio;
// If the metadata includes a duration, we store it here.
media::NullableTimeUnit mMetadataDuration;
// The Ogg reader tries to kinda-sorta compute the duration by seeking to the
// end and determining the timestamp of the last frame. This isn't useful as
// a duration until we know the start time, so we need to track it separately.
media::NullableTimeUnit mMetadataEndTime;
EncryptionInfo mCrypto;
};

View File

@ -33,6 +33,8 @@
#include "nsProxyRelease.h"
#include "nsIContentPolicy.h"
using mozilla::media::TimeUnit;
PRLogModuleInfo* gMediaResourceLog;
#define RESOURCE_LOG(msg, ...) MOZ_LOG(gMediaResourceLog, mozilla::LogLevel::Debug, \
(msg, ##__VA_ARGS__))
@ -236,37 +238,6 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
dataIsBounded = true;
}
if (mOffset == 0) {
// Look for duration headers from known Ogg content systems.
// In the case of multiple options for obtaining the duration
// the order of precedence is:
// 1) The Media resource metadata if possible (done by the decoder itself).
// 2) Content-Duration message header.
// 3) X-AMZ-Meta-Content-Duration.
// 4) X-Content-Duration.
// 5) Perform a seek in the decoder to find the value.
nsAutoCString durationText;
nsresult ec = NS_OK;
rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("Content-Duration"), durationText);
if (NS_FAILED(rv)) {
rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-AMZ-Meta-Content-Duration"), durationText);
}
if (NS_FAILED(rv)) {
rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-Content-Duration"), durationText);
}
// If there is a Content-Duration header with a valid value, record
// the duration.
if (NS_SUCCEEDED(rv)) {
double duration = durationText.ToDouble(&ec);
if (ec == NS_OK && duration >= 0) {
mDecoder->SetDuration(duration);
// We know the resource must be bounded.
dataIsBounded = true;
}
}
}
// Assume Range requests have a bounded upper limit unless the
// Content-Range header tells us otherwise.
bool boundedSeekLimit = true;

View File

@ -19,6 +19,7 @@
#include "mozilla/net/RtspChannelChild.h"
#endif
using namespace mozilla::net;
using namespace mozilla::media;
PRLogModuleInfo* gRtspMediaResourceLog;
#define RTSP_LOG(msg, ...) MOZ_LOG(gRtspMediaResourceLog, mozilla::LogLevel::Debug, \
@ -725,7 +726,6 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
// Not live stream.
mIsLiveStream = false;
mDecoder->SetInfinite(false);
mDecoder->SetDuration((double)(durationUs) / USECS_PER_S);
} else {
// Live stream.
// Check the preference "media.realtime_decoder.enabled".

View File

@ -143,7 +143,8 @@ public:
Watchable(const T& aInitialValue, const char* aName)
: WatchTarget(aName), mValue(aInitialValue) {}
operator const T&() const { return mValue; }
const T& Ref() const { return mValue; }
operator const T&() const { return Ref(); }
Watchable& operator=(const T& aNewValue)
{
if (aNewValue != mValue) {

View File

@ -10,6 +10,7 @@
#include "Intervals.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Maybe.h"
#include "mozilla/dom/TimeRanges.h"
namespace mozilla {
@ -25,7 +26,6 @@ struct nsTArray_CopyChooser<mozilla::media::TimeIntervals>
};
namespace mozilla {
namespace media {
// Number of microseconds per second. 1e6.
static const int64_t USECS_PER_S = 1000000;
@ -33,6 +33,8 @@ static const int64_t USECS_PER_S = 1000000;
// Number of microseconds per millisecond.
static const int64_t USECS_PER_MS = 1000;
namespace media {
// Number of nanoseconds per second. 1e9.
static const int64_t NSECS_PER_S = 1000000000;
@ -203,6 +205,8 @@ private:
CheckedInt64 mValue;
};
typedef Maybe<TimeUnit> NullableTimeUnit;
typedef Interval<TimeUnit> TimeInterval;
class TimeIntervals : public IntervalSet<TimeUnit>

View File

@ -137,12 +137,6 @@ CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate);
// overflow while calulating the conversion.
CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
// Number of microseconds per second. 1e6.
static const int64_t USECS_PER_S = 1000000;
// Number of microseconds per millisecond.
static const int64_t USECS_PER_MS = 1000;
// Converts milliseconds to seconds.
#define MS_TO_SECONDS(ms) ((double)(ms) / (PR_MSEC_PER_SEC))

View File

@ -18,6 +18,7 @@
namespace mozilla {
using namespace mozilla::gfx;
using namespace mozilla::media;
typedef mozilla::layers::Image Image;
typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
@ -55,8 +56,7 @@ nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo,
int64_t durationUs;
mPlugin->GetDuration(mPlugin, &durationUs);
if (durationUs) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(durationUs);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(durationUs));
}
if (mPlugin->HasVideo(mPlugin)) {

View File

@ -19,6 +19,8 @@
// 1152 is most memory efficient.
#define MAX_AUDIO_FRAMES 128
using namespace mozilla::media;
namespace mozilla {
extern PRLogModuleInfo* gMediaDecoderLog;
@ -413,11 +415,11 @@ AppleMP3Reader::ReadMetadata(MediaInfo* aInfo,
aInfo->mAudio.mChannels = mAudioChannels;
}
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDuration = mMP3FrameParser.GetDuration();
mDecoder->SetMediaDuration(mDuration);
}
// This special snowflake reader doesn't seem to set *aInfo = mInfo like all
// the others. Yuck.
mDuration = mMP3FrameParser.GetDuration();
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mDuration));
aInfo->mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mDuration));
return NS_OK;
}

View File

@ -14,6 +14,8 @@
#include "MediaResource.h"
#include "VideoUtils.h"
using namespace mozilla::media;
namespace mozilla {
@ -194,10 +196,6 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
mInfo.mAudio.mBitDepth = format.wBitsPerSample;
mBytesPerSample = format.wBitsPerSample / 8;
*aInfo = mInfo;
// Note: The SourceFilter strips ID3v2 tags out of the stream.
*aTags = nullptr;
// Begin decoding!
hr = mControl->Run();
NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
@ -207,8 +205,7 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
int64_t duration = mMP3FrameParser.GetDuration();
if (SUCCEEDED(hr)) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(duration);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
}
LOG("Successfully initialized DirectShow MP3 decoder.");
@ -218,6 +215,10 @@ DirectShowReader::ReadMetadata(MediaInfo* aInfo,
RefTimeToUsecs(duration),
mBytesPerSample);
*aInfo = mInfo;
// Note: The SourceFilter strips ID3v2 tags out of the stream.
*aTags = nullptr;
return NS_OK;
}

View File

@ -32,6 +32,7 @@ using mozilla::layers::Image;
using mozilla::layers::LayerManager;
using mozilla::layers::ImageContainer;
using mozilla::layers::LayersBackend;
using mozilla::media::TimeUnit;
PRLogModuleInfo* GetDemuxerLog() {
static PRLogModuleInfo* log = nullptr;
@ -431,8 +432,7 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
duration = mDemuxer->Duration();
}
if (duration != -1) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(duration);
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
}
*aInfo = mInfo;

View File

@ -25,6 +25,7 @@ namespace mozilla {
using namespace gfx;
using namespace layers;
using namespace media;
// Un-comment to enable logging of seek bisections.
//#define SEEK_LOGGING
@ -470,10 +471,9 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
if (isMP3 && mMP3FrameParser.IsMP3()) {
// The MP3FrameParser has reported a duration; use that over the gstreamer
// reported duration for inter-platform consistency.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mUseParserDuration = true;
mLastParserDuration = mMP3FrameParser.GetDuration();
mDecoder->SetMediaDuration(mLastParserDuration);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration));
} else {
LOG(LogLevel::Debug, "querying duration");
// Otherwise use the gstreamer duration.
@ -485,10 +485,9 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
if (gst_element_query_duration(GST_ELEMENT(mPlayBin),
&format, &duration) && format == GST_FORMAT_TIME) {
#endif
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
LOG(LogLevel::Debug, "have duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration));
duration = GST_TIME_AS_USECONDS (duration);
mDecoder->SetMediaDuration(duration);
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
}
}

View File

@ -20,14 +20,16 @@ extern PRLogModuleInfo* GetMediaSourceLog();
#define MSE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
#define MSE_DEBUGV(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
using namespace mozilla::media;
namespace mozilla {
class SourceBufferDecoder;
MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
: mMediaSource(nullptr)
, mMediaSourceDuration(UnspecifiedNaN<double>())
{
SetExplicitDuration(UnspecifiedNaN<double>());
Init(aElement);
}
@ -169,46 +171,14 @@ MediaSourceDecoder::IsExpectingMoreData()
return !mReader->IsEnded();
}
class DurationChangedRunnable : public nsRunnable {
public:
DurationChangedRunnable(MediaSourceDecoder* aDecoder,
double aOldDuration,
double aNewDuration)
: mDecoder(aDecoder)
, mOldDuration(aOldDuration)
, mNewDuration(aNewDuration)
{ }
NS_IMETHOD Run() override final {
mDecoder->DurationChanged(mOldDuration, mNewDuration);
return NS_OK;
}
private:
RefPtr<MediaSourceDecoder> mDecoder;
double mOldDuration;
double mNewDuration;
};
void
MediaSourceDecoder::DurationChanged(double aOldDuration, double aNewDuration)
{
MOZ_ASSERT(NS_IsMainThread());
// Run the MediaSource duration changed algorithm
if (mMediaSource) {
mMediaSource->DurationChange(aOldDuration, aNewDuration);
}
// Run the MediaElement duration changed algorithm
MediaDecoder::DurationChanged();
}
void
MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
{
MOZ_ASSERT(NS_IsMainThread());
// Only use the decoded duration if one wasn't already
// set.
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!mMediaSource || !IsNaN(mMediaSourceDuration)) {
if (!mMediaSource || !IsNaN(ExplicitDuration())) {
return;
}
double duration = aDuration;
@ -222,8 +192,9 @@ MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
void
MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
double oldDuration = mMediaSourceDuration;
double oldDuration = ExplicitDuration();
if (aDuration >= 0) {
int64_t checkedDuration;
if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) {
@ -231,39 +202,17 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio
// We want a very bigger number, but not infinity.
checkedDuration = INT64_MAX - 1;
}
GetStateMachine()->SetDuration(checkedDuration);
mMediaSourceDuration = aDuration;
SetExplicitDuration(aDuration);
} else {
GetStateMachine()->SetDuration(INT64_MAX);
mMediaSourceDuration = PositiveInfinity<double>();
SetExplicitDuration(PositiveInfinity<double>());
}
if (mReader) {
mReader->SetMediaSourceDuration(mMediaSourceDuration);
mReader->SetMediaSourceDuration(ExplicitDuration());
}
ScheduleDurationChange(oldDuration, aDuration, aAction);
}
void
MediaSourceDecoder::ScheduleDurationChange(double aOldDuration,
double aNewDuration,
MSRangeRemovalAction aAction)
{
if (aAction == MSRangeRemovalAction::SKIP) {
if (NS_IsMainThread()) {
MediaDecoder::DurationChanged();
} else {
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableMethod(this, &MediaDecoder::DurationChanged);
NS_DispatchToMainThread(task);
}
} else {
if (NS_IsMainThread()) {
DurationChanged(aOldDuration, aNewDuration);
} else {
nsCOMPtr<nsIRunnable> task =
new DurationChangedRunnable(this, aOldDuration, aNewDuration);
NS_DispatchToMainThread(task);
}
MediaDecoder::DurationChanged(TimeUnit::FromSeconds(ExplicitDuration()));
if (mMediaSource && aAction != MSRangeRemovalAction::SKIP) {
mMediaSource->DurationChange(oldDuration, aDuration);
}
}
@ -271,7 +220,7 @@ double
MediaSourceDecoder::GetMediaSourceDuration()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return mMediaSourceDuration;
return ExplicitDuration();
}
void
@ -329,7 +278,7 @@ double
MediaSourceDecoder::GetDuration()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return mMediaSourceDuration;
return ExplicitDuration();
}
already_AddRefed<SourceBufferDecoder>

View File

@ -62,7 +62,6 @@ public:
void SetInitialDuration(int64_t aDuration);
void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
double GetMediaSourceDuration();
void DurationChanged(double aOldDuration, double aNewDuration);
// Called whenever a TrackBuffer has new data appended or a new decoder
// initializes. Safe to call from any thread.
@ -94,18 +93,12 @@ public:
private:
void DoSetMediaSourceDuration(double aDuration);
void ScheduleDurationChange(double aOldDuration,
double aNewDuration,
MSRangeRemovalAction aAction);
// The owning MediaSource holds a strong reference to this decoder, and
// calls Attach/DetachMediaSource on this decoder to set and clear
// mMediaSource.
dom::MediaSource* mMediaSource;
nsRefPtr<MediaSourceReader> mReader;
// Protected by GetReentrantMonitor()
double mMediaSourceDuration;
};
} // namespace mozilla

View File

@ -1137,7 +1137,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
mInfo.mCrypto.AddInitData(info.mCrypto);
MSE_DEBUG("audio reader=%p duration=%lld",
mAudioSourceDecoder.get(),
mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration());
mInfo.mMetadataDuration.isSome() ? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1);
}
if (mVideoTrack) {
@ -1150,7 +1150,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
mInfo.mCrypto.AddInitData(info.mCrypto);
MSE_DEBUG("video reader=%p duration=%lld",
GetVideoReader(),
GetVideoReader()->GetDecoder()->GetMediaDuration());
mInfo.mMetadataDuration.isSome() ? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1);
}
*aInfo = mInfo;

View File

@ -37,7 +37,6 @@ SourceBufferDecoder::SourceBufferDecoder(MediaResource* aResource,
, mParentDecoder(aParentDecoder)
, mReader(nullptr)
, mTimestampOffset(aTimestampOffset)
, mMediaDuration(-1)
, mRealMediaDuration(0)
, mTrimmedOffset(-1)
{
@ -64,12 +63,6 @@ SourceBufferDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
MSE_DEBUG("UNIMPLEMENTED");
}
int64_t
SourceBufferDecoder::GetMediaDuration()
{
return mMediaDuration;
}
VideoFrameContainer*
SourceBufferDecoder::GetVideoFrameContainer()
{
@ -120,12 +113,6 @@ SourceBufferDecoder::RemoveMediaTracks()
MSE_DEBUG("UNIMPLEMENTED");
}
void
SourceBufferDecoder::SetMediaEndTime(int64_t aTime)
{
MSE_DEBUG("UNIMPLEMENTED");
}
bool
SourceBufferDecoder::HasInitializationData()
{
@ -179,12 +166,6 @@ SourceBufferDecoder::NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded,
return mParentDecoder->NotifyDecodedFrames(aParsed, aDecoded, aDropped);
}
void
SourceBufferDecoder::SetMediaDuration(int64_t aDuration)
{
mMediaDuration = aDuration;
}
void
SourceBufferDecoder::SetRealMediaDuration(int64_t aDuration)
{

View File

@ -36,7 +36,7 @@ public:
virtual bool IsTransportSeekable() final override;
virtual bool OnDecodeTaskQueue() const final override;
virtual bool OnStateMachineTaskQueue() const final override;
virtual int64_t GetMediaDuration() final override;
virtual int64_t GetMediaDuration() final override { MOZ_ASSERT_UNREACHABLE(""); return -1; };
virtual layers::ImageContainer* GetImageContainer() final override;
virtual MediaDecoderOwner* GetOwner() final override;
virtual SourceBufferResource* GetResource() const final override;
@ -54,8 +54,6 @@ public:
virtual void OnReadMetadataCompleted() final override;
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) final override;
virtual void RemoveMediaTracks() final override;
virtual void SetMediaDuration(int64_t aDuration) final override;
virtual void SetMediaEndTime(int64_t aTime) final override;
virtual void SetMediaSeekable(bool aMediaSeekable) final override;
virtual void UpdateEstimatedMediaDuration(int64_t aDuration) final override;
virtual bool HasInitializationData() final override;
@ -150,8 +148,6 @@ private:
nsRefPtr<MediaDecoderReader> mReader;
// in microseconds
int64_t mTimestampOffset;
// mMediaDuration contains the apparent buffer duration, excluding trimmed data.
int64_t mMediaDuration;
// mRealMediaDuration contains the real buffer duration, including trimmed data.
int64_t mRealMediaDuration;
// in seconds

View File

@ -809,7 +809,9 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
return;
}
int64_t duration = aDecoder->GetMediaDuration();
int64_t duration = mInfo.mMetadataDuration.isSome()
? mInfo.mMetadataDuration.ref().ToMicroseconds() : -1;
if (!duration) {
// Treat a duration of 0 as infinity
duration = -1;

View File

@ -24,6 +24,7 @@ extern "C" {
#include "gfx2DGlue.h"
using namespace mozilla::gfx;
using namespace mozilla::media;
namespace mozilla {
@ -289,9 +290,8 @@ void OggReader::SetupTargetSkeleton(SkeletonState* aSkeletonState)
BuildSerialList(tracks);
int64_t duration = 0;
if (NS_SUCCEEDED(aSkeletonState->GetDuration(tracks, duration))) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(duration);
LOG(LogLevel::Debug, ("Got duration from Skeleton index %lld", duration));
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration));
}
}
}
@ -474,10 +474,8 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MediaResource* resource = mDecoder->GetResource();
if (mDecoder->GetMediaDuration() == -1 &&
!mDecoder->IsShutdown() &&
resource->GetLength() >= 0 &&
mDecoder->IsMediaSeekable())
if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsShutdown() &&
resource->GetLength() >= 0 && mDecoder->IsMediaSeekable())
{
// We didn't get a duration from the index or a Content-Duration header.
// Seek to the end of file to find the end time.
@ -491,7 +489,7 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
endTime = RangeEndTime(length);
}
if (endTime != -1) {
mDecoder->SetMediaEndTime(endTime);
mInfo.mMetadataEndTime.emplace(TimeUnit::FromMicroseconds(endTime));
LOG(LogLevel::Debug, ("Got Ogg duration from seeking to end %lld", endTime));
}
}

View File

@ -42,6 +42,7 @@
using namespace android;
using namespace mozilla::layers;
using namespace mozilla::media;
namespace mozilla {
@ -712,8 +713,7 @@ MediaCodecReader::HandleResourceAllocated()
}
int64_t duration = audioDuration > videoDuration ? audioDuration : videoDuration;
if (duration >= INT64_C(0)) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(duration);
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(duration));
}
// Video track's frame sizes will not overflow. Activate the video track.

View File

@ -23,6 +23,7 @@
#define MAX_VIDEO_DECODE_SECONDS 0.1
using namespace mozilla::gfx;
using namespace mozilla::media;
using namespace android;
namespace mozilla {
@ -281,16 +282,14 @@ void MediaOmxReader::HandleResourceAllocated()
if (mLastParserDuration >= 0) {
// Prefer the parser duration if we have it.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(mLastParserDuration);
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(mLastParserDuration));
} else {
// MP3 parser failed to find a duration.
// Set the total duration (the max of the audio and video track).
int64_t durationUs;
mOmxDecoder->GetDuration(&durationUs);
if (durationUs) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(durationUs);
mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(durationUs));
}
}

View File

@ -12,6 +12,7 @@
#include "gfx2DGlue.h"
using namespace mozilla;
using namespace mozilla::media;
RawReader::RawReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder),
@ -97,10 +98,8 @@ nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
int64_t length = resource->GetLength();
if (length != -1) {
ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor());
mDecoder->SetMediaDuration(USECS_PER_S *
(length - sizeof(RawVideoHeader)) /
(mFrameSize * mFrameRate));
mInfo.mMetadataDuration.emplace(TimeUnit::FromSeconds((length - sizeof(RawVideoHeader)) /
(mFrameSize * mFrameRate)));
}
*aInfo = mInfo;

View File

@ -1,2 +1 @@
X-Content-Duration: 1.96
Cache-Control: no-store

View File

@ -1,2 +1 @@
X-Content-Duration: 9000
Cache-Control: no-store

View File

@ -1,25 +0,0 @@
function handleRequest(request, response)
{
var file = Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("CurWorkD", Components.interfaces.nsILocalFile);
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
createInstance(Components.interfaces.nsIFileInputStream);
var bis = Components.classes["@mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
var paths = "tests/dom/media/test/320x240.ogv";
var split = paths.split("/");
for(var i = 0; i < split.length; ++i) {
file.append(split[i]);
}
fis.init(file, -1, -1, false);
bis.setInputStream(fis);
var bytes = bis.readBytes(bis.available());
response.setHeader("Content-Duration", "0.233", false);
response.setHeader("Content-Type", "video/ogg", false);
response.write(bytes, bytes.length);
// Make this request async to prevent a default Content-Length from being provided.
response.processAsync();
response.finish();
bis.close();
}

View File

@ -1,25 +0,0 @@
function handleRequest(request, response)
{
var file = Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("CurWorkD", Components.interfaces.nsILocalFile);
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
createInstance(Components.interfaces.nsIFileInputStream);
var bis = Components.classes["@mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
var paths = "tests/dom/media/test/320x240.ogv";
var split = paths.split("/");
for(var i = 0; i < split.length; ++i) {
file.append(split[i]);
}
fis.init(file, -1, -1, false);
bis.setInputStream(fis);
var bytes = bis.readBytes(bis.available());
response.setHeader("x-amz-meta-content-duration", "0.233", false);
response.setHeader("Content-Type", "video/ogg", false);
response.write(bytes, bytes.length);
// Make this request async to prevent a default Content-Length from being provided.
response.processAsync();
response.finish();
bis.close();
}

View File

@ -1,25 +0,0 @@
function handleRequest(request, response)
{
var file = Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get("CurWorkD", Components.interfaces.nsILocalFile);
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
createInstance(Components.interfaces.nsIFileInputStream);
var bis = Components.classes["@mozilla.org/binaryinputstream;1"].
createInstance(Components.interfaces.nsIBinaryInputStream);
var paths = "tests/dom/media/test/320x240.ogv";
var split = paths.split("/");
for(var i = 0; i < split.length; ++i) {
file.append(split[i]);
}
fis.init(file, -1, -1, false);
bis.setInputStream(fis);
var bytes = bis.readBytes(bis.available());
response.setStatusLine(request.httpVersion, 200, "Content Follows");
response.setHeader("Content-Type", "video/ogg", false);
response.write(bytes, bytes.length);
// Make this request async to prevent a default Content-Length from being provided.
response.processAsync();
response.finish();
bis.close();
}

Some files were not shown because too many files have changed in this diff Show More