mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound a=merge
This commit is contained in:
commit
ac8b2c0ce1
@ -67,14 +67,6 @@ Accessible::ScrollTo(uint32_t aHow) const
|
||||
nsCoreUtils::ScrollTo(mDoc->PresShell(), mContent, aHow);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Accessible::UpdateChildren()
|
||||
{
|
||||
AutoTreeMutation mut(this);
|
||||
InvalidateChildren();
|
||||
return EnsureChildren();
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -2012,8 +2012,7 @@ Accessible::InvalidateChildren()
|
||||
{
|
||||
int32_t childCount = mChildren.Length();
|
||||
for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
|
||||
Accessible* child = mChildren.ElementAt(childIdx);
|
||||
child->UnbindFromParent();
|
||||
mChildren.ElementAt(childIdx)->UnbindFromParent();
|
||||
}
|
||||
|
||||
mEmbeddedObjCollector = nullptr;
|
||||
@ -2446,23 +2445,17 @@ Accessible::TestChildCache(Accessible* aCachedChild) const
|
||||
#endif
|
||||
}
|
||||
|
||||
// Accessible public
|
||||
bool
|
||||
void
|
||||
Accessible::EnsureChildren()
|
||||
{
|
||||
if (IsDefunct()) {
|
||||
SetChildrenFlag(eChildrenUninitialized);
|
||||
return true;
|
||||
}
|
||||
NS_ASSERTION(!IsDefunct(), "Caching children for defunct accessible!");
|
||||
|
||||
if (!IsChildrenFlag(eChildrenUninitialized))
|
||||
return false;
|
||||
return;
|
||||
|
||||
// State is embedded children until text leaf accessible is appended.
|
||||
SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
|
||||
|
||||
CacheChildren();
|
||||
return false;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
|
@ -366,14 +366,9 @@ public:
|
||||
{ mRoleMapEntry = aRoleMapEntry; }
|
||||
|
||||
/**
|
||||
* Update the children cache.
|
||||
* Cache children if necessary.
|
||||
*/
|
||||
bool UpdateChildren();
|
||||
|
||||
/**
|
||||
* Cache children if necessary. Return true if the accessible is defunct.
|
||||
*/
|
||||
bool EnsureChildren();
|
||||
void EnsureChildren();
|
||||
|
||||
/**
|
||||
* Set the child count to -1 (unknown) and null out cached child pointers.
|
||||
@ -587,6 +582,7 @@ public:
|
||||
HyperTextAccessible* AsHyperText();
|
||||
|
||||
bool IsHTMLBr() const { return mType == eHTMLBRType; }
|
||||
bool IsHTMLCombobox() const { return mType == eHTMLComboboxType; }
|
||||
bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; }
|
||||
|
||||
bool IsHTMLListItem() const { return mType == eHTMLLiType; }
|
||||
@ -870,6 +866,19 @@ public:
|
||||
bool NeedsDOMUIEvent() const
|
||||
{ return !(mStateFlags & eIgnoreDOMUIEvent); }
|
||||
|
||||
/**
|
||||
* Get/set survivingInUpdate bit on child indicating that parent recollects
|
||||
* its children.
|
||||
*/
|
||||
bool IsSurvivingInUpdate() const { return mStateFlags & eSurvivingInUpdate; }
|
||||
void SetSurvivingInUpdate(bool aIsSurviving)
|
||||
{
|
||||
if (aIsSurviving)
|
||||
mStateFlags |= eSurvivingInUpdate;
|
||||
else
|
||||
mStateFlags &= ~eSurvivingInUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this accessible has a parent whose name depends on this
|
||||
* accessible.
|
||||
@ -953,8 +962,9 @@ protected:
|
||||
eGroupInfoDirty = 1 << 5, // accessible needs to update group info
|
||||
eSubtreeMutating = 1 << 6, // subtree is being mutated
|
||||
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
||||
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
||||
|
||||
eLastStateFlag = eIgnoreDOMUIEvent
|
||||
eLastStateFlag = eSurvivingInUpdate
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1068,7 +1078,7 @@ protected:
|
||||
int32_t mIndexInParent;
|
||||
|
||||
static const uint8_t kChildrenFlagsBits = 2;
|
||||
static const uint8_t kStateFlagsBits = 8;
|
||||
static const uint8_t kStateFlagsBits = 9;
|
||||
static const uint8_t kContextFlagsBits = 1;
|
||||
static const uint8_t kTypeBits = 6;
|
||||
static const uint8_t kGenericTypesBits = 13;
|
||||
|
@ -1308,19 +1308,10 @@ DocAccessible::ProcessInvalidationList()
|
||||
// children are recached.
|
||||
for (uint32_t idx = 0; idx < mInvalidationList.Length(); idx++) {
|
||||
nsIContent* content = mInvalidationList[idx];
|
||||
Accessible* accessible = GetAccessible(content);
|
||||
if (!accessible) {
|
||||
if (!HasAccessible(content)) {
|
||||
Accessible* container = GetContainerAccessible(content);
|
||||
if (container) {
|
||||
container->UpdateChildren();
|
||||
accessible = GetAccessible(content);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the subtree is created.
|
||||
if (accessible) {
|
||||
AutoTreeMutation mut(accessible);
|
||||
CacheChildrenInSubtree(accessible);
|
||||
if (container)
|
||||
UpdateTreeOnInsertion(container);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1633,8 +1624,6 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
||||
if (!HasAccessible(aContainer->GetNode()))
|
||||
return;
|
||||
|
||||
bool containerNotUpdated = true;
|
||||
|
||||
for (uint32_t idx = 0; idx < aInsertedContent->Length(); idx++) {
|
||||
// The container might be changed, for example, because of the subsequent
|
||||
// overlapping content insertion (i.e. other content was inserted between
|
||||
@ -1644,108 +1633,73 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
||||
// Note, the inserted content might be not in tree at all at this point what
|
||||
// means there's no container. Ignore the insertion too.
|
||||
|
||||
Accessible* presentContainer =
|
||||
Accessible* container =
|
||||
GetContainerAccessible(aInsertedContent->ElementAt(idx));
|
||||
if (presentContainer != aContainer)
|
||||
if (container != aContainer)
|
||||
continue;
|
||||
|
||||
if (containerNotUpdated) {
|
||||
containerNotUpdated = false;
|
||||
|
||||
if (aContainer == this) {
|
||||
// If new root content has been inserted then update it.
|
||||
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
|
||||
if (rootContent != mContent) {
|
||||
mContent = rootContent;
|
||||
SetRoleMapEntry(aria::GetRoleMap(mContent));
|
||||
}
|
||||
|
||||
// Continue to update the tree even if we don't have root content.
|
||||
// For example, elements may be inserted under the document element while
|
||||
// there is no HTML body element.
|
||||
if (container == this) {
|
||||
// If new root content has been inserted then update it.
|
||||
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
|
||||
if (rootContent != mContent) {
|
||||
mContent = rootContent;
|
||||
SetRoleMapEntry(aria::GetRoleMap(mContent));
|
||||
}
|
||||
|
||||
// XXX: Invalidate parent-child relations for container accessible and its
|
||||
// children because there's no good way to find insertion point of new child
|
||||
// accessibles into accessible tree. We need to invalidate children even
|
||||
// there's no inserted accessibles in the end because accessible children
|
||||
// are created while parent recaches child accessibles.
|
||||
// XXX Group invalidation here may be redundant with invalidation in
|
||||
// UpdateTree.
|
||||
AutoTreeMutation mut(aContainer);
|
||||
aContainer->InvalidateChildren();
|
||||
CacheChildrenInSubtree(aContainer);
|
||||
// Continue to update the tree even if we don't have root content.
|
||||
// For example, elements may be inserted under the document element while
|
||||
// there is no HTML body element.
|
||||
}
|
||||
|
||||
UpdateTree(aContainer, aInsertedContent->ElementAt(idx), true);
|
||||
// HTML comboboxes have no-content list accessible as an intermidiate
|
||||
// containing all options.
|
||||
if (container->IsHTMLCombobox())
|
||||
container = container->FirstChild();
|
||||
|
||||
// We have a DOM/layout change under the container accessible, and its tree
|
||||
// might need an update. Since DOM/layout change of the element may affect
|
||||
// on the accessibleness of adjacent elements (for example, insertion of
|
||||
// extra HTML:body make the old body accessible) then we have to recache
|
||||
// children of the container, and then fire show/hide events for a change.
|
||||
UpdateTreeOnInsertion(container);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
|
||||
bool aIsInsert)
|
||||
DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer)
|
||||
{
|
||||
uint32_t updateFlags = eNoAccessible;
|
||||
for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
|
||||
Accessible* child = aContainer->ContentChildAt(idx);
|
||||
child->SetSurvivingInUpdate(true);
|
||||
}
|
||||
|
||||
// If child node is not accessible then look for its accessible children.
|
||||
Accessible* child = GetAccessible(aChildNode);
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree)) {
|
||||
logging::MsgBegin("TREE", "process content %s",
|
||||
(aIsInsert ? "insertion" : "removal"));
|
||||
logging::Node("container", aContainer->GetNode());
|
||||
logging::Node("child", aChildNode);
|
||||
if (child)
|
||||
logging::Address("child", child);
|
||||
else
|
||||
logging::MsgEntry("child accessible: null");
|
||||
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
AutoTreeMutation mut(aContainer);
|
||||
aContainer->InvalidateChildren();
|
||||
aContainer->EnsureChildren();
|
||||
|
||||
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
||||
AutoTreeMutation mut(aContainer);
|
||||
|
||||
if (child) {
|
||||
updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
|
||||
} else {
|
||||
if (aIsInsert) {
|
||||
TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
|
||||
|
||||
while ((child = walker.NextChild()))
|
||||
updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
|
||||
} else {
|
||||
// aChildNode may not coorespond to a particular accessible, to handle
|
||||
// this we go through all the children of aContainer. Then if a child
|
||||
// has aChildNode as an ancestor, or does not have the node for
|
||||
// aContainer as an ancestor remove that child of aContainer. Note that
|
||||
// when we are called aChildNode may already have been removed
|
||||
// from the DOM so we can't expect it to have a parent or what was it's
|
||||
// parent to have it as a child.
|
||||
nsINode* containerNode = aContainer->GetNode();
|
||||
for (uint32_t idx = 0; idx < aContainer->ContentChildCount();) {
|
||||
Accessible* child = aContainer->ContentChildAt(idx);
|
||||
|
||||
// If accessible doesn't have its own content then we assume parent
|
||||
// will handle its update. If child is DocAccessible then we don't
|
||||
// handle updating it here either.
|
||||
if (!child->HasOwnContent() || child->IsDoc()) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
nsINode* childNode = child->GetContent();
|
||||
while (childNode != aChildNode && childNode != containerNode &&
|
||||
(childNode = childNode->GetParentNode()));
|
||||
|
||||
if (childNode != containerNode) {
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
uint32_t updateFlags = eNoAccessible;
|
||||
for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
|
||||
Accessible* child = aContainer->ContentChildAt(idx);
|
||||
if (child->IsSurvivingInUpdate()) {
|
||||
child->SetSurvivingInUpdate(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// A new child has been created, update its tree.
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree)) {
|
||||
logging::MsgBegin("TREE", "process content insertion");
|
||||
logging::Node("container", aContainer->GetNode());
|
||||
logging::Node("child", child->GetContent());
|
||||
logging::Address("child", child);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
updateFlags |= UpdateTreeInternal(child, true, reorderEvent);
|
||||
}
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
@ -1754,7 +1708,7 @@ DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
|
||||
|
||||
// Check to see if change occurred inside an alert, and fire an EVENT_ALERT
|
||||
// if it did.
|
||||
if (aIsInsert && !(updateFlags & eAlertAccessible)) {
|
||||
if (!(updateFlags & eAlertAccessible)) {
|
||||
// XXX: tree traversal is perf issue, accessible should know if they are
|
||||
// children of alert accessible to avoid this.
|
||||
Accessible* ancestor = aContainer;
|
||||
@ -1773,9 +1727,71 @@ DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
|
||||
}
|
||||
|
||||
MaybeNotifyOfValueChange(aContainer);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
// Fire reorder event so the MSAA clients know the children have changed. Also
|
||||
// the event is used internally by MSAA layer.
|
||||
void
|
||||
DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode)
|
||||
{
|
||||
// If child node is not accessible then look for its accessible children.
|
||||
Accessible* child = GetAccessible(aChildNode);
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree)) {
|
||||
logging::MsgBegin("TREE", "process content removal");
|
||||
logging::Node("container", aContainer->GetNode());
|
||||
logging::Node("child", aChildNode);
|
||||
if (child)
|
||||
logging::Address("child", child);
|
||||
else
|
||||
logging::MsgEntry("child accessible: null");
|
||||
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t updateFlags = eNoAccessible;
|
||||
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
||||
AutoTreeMutation mut(aContainer);
|
||||
|
||||
if (child) {
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
} else {
|
||||
// aChildNode may not coorespond to a particular accessible, to handle
|
||||
// this we go through all the children of aContainer. Then if a child
|
||||
// has aChildNode as an ancestor, or does not have the node for
|
||||
// aContainer as an ancestor remove that child of aContainer. Note that
|
||||
// when we are called aChildNode may already have been removed from the DOM
|
||||
// so we can't expect it to have a parent or what was it's parent to have
|
||||
// it as a child.
|
||||
nsINode* containerNode = aContainer->GetNode();
|
||||
for (uint32_t idx = 0; idx < aContainer->ContentChildCount();) {
|
||||
Accessible* child = aContainer->ContentChildAt(idx);
|
||||
|
||||
// If accessible doesn't have its own content then we assume parent
|
||||
// will handle its update. If child is DocAccessible then we don't
|
||||
// handle updating it here either.
|
||||
if (!child->HasOwnContent() || child->IsDoc()) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
nsINode* childNode = child->GetContent();
|
||||
while (childNode != aChildNode && childNode != containerNode &&
|
||||
(childNode = childNode->GetParentNode()));
|
||||
|
||||
if (childNode != containerNode) {
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
} else {
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
if (updateFlags == eNoAccessible)
|
||||
return;
|
||||
|
||||
MaybeNotifyOfValueChange(aContainer);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
|
@ -321,7 +321,7 @@ public:
|
||||
{
|
||||
// Update the whole tree of this document accessible when the container is
|
||||
// null (document element is removed).
|
||||
UpdateTree((aContainer ? aContainer : this), aChildNode, false);
|
||||
UpdateTreeOnRemoval((aContainer ? aContainer : this), aChildNode);
|
||||
}
|
||||
void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode)
|
||||
{
|
||||
@ -465,13 +465,17 @@ protected:
|
||||
void ProcessInvalidationList();
|
||||
|
||||
/**
|
||||
* Update the accessible tree for content insertion or removal.
|
||||
* Update the tree on content insertion.
|
||||
*/
|
||||
void UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
|
||||
bool aIsInsert);
|
||||
void UpdateTreeOnInsertion(Accessible* aContainer);
|
||||
|
||||
/**
|
||||
* Helper for UpdateTree() method. Go down to DOM subtree and updates
|
||||
* Update the accessible tree for content removal.
|
||||
*/
|
||||
void UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode);
|
||||
|
||||
/**
|
||||
* Helper for UpdateTreeOn methods. Go down to DOM subtree and updates
|
||||
* accessible tree. Return one of these flags.
|
||||
*/
|
||||
enum EUpdateTreeFlags {
|
||||
|
@ -359,6 +359,7 @@ HTMLComboboxAccessible::
|
||||
HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
AccessibleWrap(aContent, aDoc)
|
||||
{
|
||||
mType = eHTMLComboboxType;
|
||||
mGenericTypes |= eCombobox;
|
||||
}
|
||||
|
||||
|
@ -336,6 +336,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
function insertReferredElm(aContainerID)
|
||||
{
|
||||
this.containerNode = getNode(aContainerID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.lastChild; }, this.containerNode),
|
||||
new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.firstChild; }, this.containerNode),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
this.invoke = function insertReferredElm_invoke()
|
||||
{
|
||||
this.containerNode.innerHTML =
|
||||
"<span id='insertReferredElms_span'></span><input aria-labelledby='insertReferredElms_span'>";
|
||||
}
|
||||
|
||||
this.getID = function insertReferredElm_getID()
|
||||
{
|
||||
return "insert inaccessible element and then insert referring element to make it accessible";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Target getters.
|
||||
*/
|
||||
@ -343,6 +365,10 @@
|
||||
{
|
||||
return [aNode.firstChild];
|
||||
}
|
||||
function getLastChild(aNode)
|
||||
{
|
||||
return [aNode.lastChild];
|
||||
}
|
||||
|
||||
function getNEnsureFirstChild(aNode)
|
||||
{
|
||||
@ -457,6 +483,7 @@
|
||||
gQueue.push(new test2("testContainer", "testContainer2"));
|
||||
gQueue.push(new test2("testContainer", "testNestedContainer"));
|
||||
gQueue.push(new test3("testContainer"));
|
||||
gQueue.push(new insertReferredElm("testContainer3"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
@ -516,5 +543,6 @@
|
||||
<div id="testNestedContainer"></div>
|
||||
</div>
|
||||
<div id="testContainer2"></div>
|
||||
<div id="testContainer3"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -86,7 +86,7 @@
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
role: ROLE_COMBOBOX_OPTION,
|
||||
children: [
|
||||
|
@ -21,6 +21,7 @@
|
||||
{
|
||||
this.selectNode = getNode(aID);
|
||||
this.select = getAccessible(this.selectNode);
|
||||
this.selectList = this.select.firstChild;
|
||||
|
||||
this.invoke = function addOptGroup_invoke()
|
||||
{
|
||||
@ -39,7 +40,7 @@
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.select)
|
||||
new invokerChecker(EVENT_REORDER, this.selectList)
|
||||
];
|
||||
|
||||
this.finalCheck = function addOptGroup_finalCheck()
|
||||
|
@ -21,6 +21,7 @@
|
||||
{
|
||||
this.selectNode = getNode(aID);
|
||||
this.select = getAccessible(this.selectNode);
|
||||
this.selectList = this.select.firstChild;
|
||||
|
||||
this.invoke = function addOptions_invoke()
|
||||
{
|
||||
@ -34,7 +35,7 @@
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.select)
|
||||
new invokerChecker(EVENT_REORDER, this.selectList)
|
||||
];
|
||||
|
||||
this.finalCheck = function addOptions_finalCheck()
|
||||
|
@ -376,18 +376,12 @@ support-files =
|
||||
[browser_removeUnsafeProtocolsFromURLBarPaste.js]
|
||||
skip-if = e10s
|
||||
[browser_restore_isAppTab.js]
|
||||
[browser_sanitize-download-history.js]
|
||||
skip-if = true # bug 432425
|
||||
[browser_sanitize-passwordDisabledHosts.js]
|
||||
[browser_sanitize-sitepermissions.js]
|
||||
[browser_sanitize-timespans.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_sanitizeDialog.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_sanitizeDialog_treeView.js]
|
||||
skip-if = true # disabled until the tree view is added
|
||||
# back to the clear recent history dialog (sanitize.xul), if
|
||||
# it ever is (bug 480169)
|
||||
[browser_save_link-perwindowpb.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
[browser_save_private_link_perwindowpb.js]
|
||||
|
@ -1,142 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
function test()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Tests (defined locally for scope's sake)
|
||||
|
||||
function test_checkedAndDisabledAtStart(aWin)
|
||||
{
|
||||
let doc = aWin.document;
|
||||
let downloads = doc.getElementById("downloads-checkbox");
|
||||
let history = doc.getElementById("history-checkbox");
|
||||
|
||||
ok(history.checked, "history checkbox is checked");
|
||||
ok(downloads.disabled, "downloads checkbox is disabled");
|
||||
ok(downloads.checked, "downloads checkbox is checked");
|
||||
}
|
||||
|
||||
function test_checkedAndDisabledOnHistoryToggle(aWin)
|
||||
{
|
||||
let doc = aWin.document;
|
||||
let downloads = doc.getElementById("downloads-checkbox");
|
||||
let history = doc.getElementById("history-checkbox");
|
||||
|
||||
EventUtils.synthesizeMouse(history, 0, 0, {}, aWin);
|
||||
ok(!history.checked, "history checkbox is not checked");
|
||||
ok(downloads.disabled, "downloads checkbox is disabled");
|
||||
ok(downloads.checked, "downloads checkbox is checked");
|
||||
}
|
||||
|
||||
function test_checkedAfterAddingDownload(aWin)
|
||||
{
|
||||
let doc = aWin.document;
|
||||
let downloads = doc.getElementById("downloads-checkbox");
|
||||
let history = doc.getElementById("history-checkbox");
|
||||
|
||||
// Add download to DB
|
||||
let file = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
|
||||
file.append("sanitize-dm-test.file");
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
|
||||
let testPath = Services.io.newFileURI(file).spec;
|
||||
let data = {
|
||||
name: "381603.patch",
|
||||
source: "https://bugzilla.mozilla.org/attachment.cgi?id=266520",
|
||||
target: testPath,
|
||||
startTime: 1180493839859230,
|
||||
endTime: 1180493839859239,
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "a1bcD23eF4g5"
|
||||
};
|
||||
let db = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager).DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"INSERT INTO moz_downloads (name, source, target, startTime, endTime, " +
|
||||
"state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
|
||||
"VALUES (:name, :source, :target, :startTime, :endTime, :state, " +
|
||||
":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
// Toggle history to get everything to update
|
||||
EventUtils.synthesizeMouse(history, 0, 0, {}, aWin);
|
||||
EventUtils.synthesizeMouse(history, 0, 0, {}, aWin);
|
||||
|
||||
ok(!history.checked, "history checkbox is not checked");
|
||||
ok(!downloads.disabled, "downloads checkbox is not disabled");
|
||||
ok(downloads.checked, "downloads checkbox is checked");
|
||||
}
|
||||
|
||||
function test_checkedAndDisabledWithHistoryChecked(aWin)
|
||||
{
|
||||
let doc = aWin.document;
|
||||
let downloads = doc.getElementById("downloads-checkbox");
|
||||
let history = doc.getElementById("history-checkbox");
|
||||
|
||||
EventUtils.synthesizeMouse(history, 0, 0, {}, aWin);
|
||||
ok(history.checked, "history checkbox is checked");
|
||||
ok(downloads.disabled, "downloads checkbox is disabled");
|
||||
ok(downloads.checked, "downloads checkbox is checked");
|
||||
}
|
||||
|
||||
let tests = [
|
||||
test_checkedAndDisabledAtStart,
|
||||
test_checkedAndDisabledOnHistoryToggle,
|
||||
test_checkedAfterAddingDownload,
|
||||
test_checkedAndDisabledWithHistoryChecked,
|
||||
];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Run the tests
|
||||
|
||||
let dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
let db = dm.DBConnection;
|
||||
|
||||
// Empty any old downloads
|
||||
db.executeSimpleSQL("DELETE FROM moz_downloads");
|
||||
|
||||
// Close the UI if necessary
|
||||
let win = Services.ww.getWindowByName("Sanitize", null);
|
||||
if (win && (win instanceof Ci.nsIDOMWindow))
|
||||
win.close();
|
||||
|
||||
// Start the test when the sanitize window loads
|
||||
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
|
||||
Services.ww.unregisterNotification(arguments.callee);
|
||||
aSubject.QueryInterface(Ci.nsIDOMEventTarget)
|
||||
.addEventListener("DOMContentLoaded", doTest, false);
|
||||
});
|
||||
|
||||
// Let the methods that run onload finish before we test
|
||||
let doTest = function() setTimeout(function() {
|
||||
let win = Services.ww.getWindowByName("Sanitize", null)
|
||||
.QueryInterface(Ci.nsIDOMWindow);
|
||||
|
||||
for (let i = 0; i < tests.length; i++)
|
||||
tests[i](win);
|
||||
|
||||
win.close();
|
||||
finish();
|
||||
}, 0);
|
||||
|
||||
// Show the UI
|
||||
Services.ww.openWindow(window,
|
||||
"chrome://browser/content/sanitize.xul",
|
||||
"Sanitize",
|
||||
"chrome,titlebar,centerscreen",
|
||||
null);
|
||||
|
||||
waitForExplicitFinish();
|
||||
}
|
@ -1,632 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Tests the sanitize dialog (a.k.a. the clear recent history dialog).
|
||||
* See bug 480169.
|
||||
*
|
||||
* The purpose of this test is not to fully flex the sanitize timespan code;
|
||||
* browser/base/content/test/general/browser_sanitize-timespans.js does that. This
|
||||
* test checks the UI of the dialog and makes sure it's correctly connected to
|
||||
* the sanitize timespan code.
|
||||
*
|
||||
* Some of this code, especially the history creation parts, was taken from
|
||||
* browser/base/content/test/general/browser_sanitize-timespans.js.
|
||||
*/
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader).
|
||||
loadSubScript("chrome://browser/content/sanitize.js");
|
||||
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
const formhist = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
// Add tests here. Each is a function that's called by doNextTest().
|
||||
var gAllTests = [
|
||||
|
||||
/**
|
||||
* Moves the grippy around, makes sure it works OK.
|
||||
*/
|
||||
function () {
|
||||
// Add history (within the past hour) to get some rows in the tree.
|
||||
let uris = [];
|
||||
let places = [];
|
||||
let pURI;
|
||||
for (let i = 0; i < 30; i++) {
|
||||
pURI = makeURI("http://" + i + "-minutes-ago.com/");
|
||||
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
|
||||
uris.push(pURI);
|
||||
}
|
||||
|
||||
addVisits(places, function() {
|
||||
// Open the dialog and do our tests.
|
||||
openWindow(function (aWin) {
|
||||
let wh = new WindowHelper(aWin);
|
||||
wh.selectDuration(Sanitizer.TIMESPAN_HOUR);
|
||||
wh.checkGrippy("Grippy should be at last row after selecting HOUR " +
|
||||
"duration",
|
||||
wh.getRowCount() - 1);
|
||||
|
||||
// Move the grippy around.
|
||||
let row = wh.getGrippyRow();
|
||||
while (row !== 0) {
|
||||
row--;
|
||||
wh.moveGrippyBy(-1);
|
||||
wh.checkGrippy("Grippy should be moved up one row", row);
|
||||
}
|
||||
wh.moveGrippyBy(-1);
|
||||
wh.checkGrippy("Grippy should remain at first row after trying to move " +
|
||||
"it up",
|
||||
0);
|
||||
while (row !== wh.getRowCount() - 1) {
|
||||
row++;
|
||||
wh.moveGrippyBy(1);
|
||||
wh.checkGrippy("Grippy should be moved down one row", row);
|
||||
}
|
||||
wh.moveGrippyBy(1);
|
||||
wh.checkGrippy("Grippy should remain at last row after trying to move " +
|
||||
"it down",
|
||||
wh.getRowCount() - 1);
|
||||
|
||||
// Cancel the dialog, make sure history visits are not cleared.
|
||||
wh.checkPrefCheckbox("history", false);
|
||||
|
||||
wh.cancelDialog();
|
||||
yield promiseHistoryClearedState(uris, false);
|
||||
|
||||
// OK, done, cleanup after ourselves.
|
||||
blankSlate();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the combined history-downloads checkbox clears both history
|
||||
* visits and downloads when checked; the dialog respects simple timespan.
|
||||
*/
|
||||
function () {
|
||||
// Add history (within the past hour).
|
||||
let uris = [];
|
||||
let places = [];
|
||||
let pURI;
|
||||
for (let i = 0; i < 30; i++) {
|
||||
pURI = makeURI("http://" + i + "-minutes-ago.com/");
|
||||
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
|
||||
uris.push(pURI);
|
||||
}
|
||||
// Add history (over an hour ago).
|
||||
let olderURIs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
pURI = makeURI("http://" + (60 + i) + "-minutes-ago.com/");
|
||||
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(60 + i)});
|
||||
olderURIs.push(pURI);
|
||||
}
|
||||
|
||||
addVisits(places, function() {
|
||||
// Add downloads (within the past hour).
|
||||
let downloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
downloadIDs.push(addDownloadWithMinutesAgo(i));
|
||||
}
|
||||
// Add downloads (over an hour ago).
|
||||
let olderDownloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i));
|
||||
}
|
||||
let totalHistoryVisits = uris.length + olderURIs.length;
|
||||
|
||||
// Open the dialog and do our tests.
|
||||
openWindow(function (aWin) {
|
||||
let wh = new WindowHelper(aWin);
|
||||
wh.selectDuration(Sanitizer.TIMESPAN_HOUR);
|
||||
wh.checkGrippy("Grippy should be at proper row after selecting HOUR " +
|
||||
"duration",
|
||||
uris.length);
|
||||
|
||||
// Accept the dialog, make sure history visits and downloads within one
|
||||
// hour are cleared.
|
||||
wh.checkPrefCheckbox("history", true);
|
||||
wh.acceptDialog();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
ensureDownloadsClearedState(downloadIDs, true);
|
||||
|
||||
// Make sure visits and downloads > 1 hour still exist.
|
||||
yield promiseHistoryClearedState(olderURIs, false);
|
||||
ensureDownloadsClearedState(olderDownloadIDs, false);
|
||||
|
||||
// OK, done, cleanup after ourselves.
|
||||
blankSlate();
|
||||
yield promiseHistoryClearedState(olderURIs, true);
|
||||
ensureDownloadsClearedState(olderDownloadIDs, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the combined history-downloads checkbox removes neither
|
||||
* history visits nor downloads when not checked.
|
||||
*/
|
||||
function () {
|
||||
// Add history, downloads, form entries (within the past hour).
|
||||
let uris = [];
|
||||
let places = [];
|
||||
let pURI;
|
||||
for (let i = 0; i < 5; i++) {
|
||||
pURI = makeURI("http://" + i + "-minutes-ago.com/");
|
||||
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(i)});
|
||||
uris.push(pURI);
|
||||
}
|
||||
|
||||
addVisits(places, function() {
|
||||
let downloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
downloadIDs.push(addDownloadWithMinutesAgo(i));
|
||||
}
|
||||
let formEntries = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
formEntries.push(addFormEntryWithMinutesAgo(i));
|
||||
}
|
||||
|
||||
// Open the dialog and do our tests.
|
||||
openWindow(function (aWin) {
|
||||
let wh = new WindowHelper(aWin);
|
||||
wh.selectDuration(Sanitizer.TIMESPAN_HOUR);
|
||||
wh.checkGrippy("Grippy should be at last row after selecting HOUR " +
|
||||
"duration",
|
||||
wh.getRowCount() - 1);
|
||||
|
||||
// Remove only form entries, leave history (including downloads).
|
||||
wh.checkPrefCheckbox("history", false);
|
||||
wh.checkPrefCheckbox("formdata", true);
|
||||
wh.acceptDialog();
|
||||
|
||||
// Of the three only form entries should be cleared.
|
||||
yield promiseHistoryClearedState(uris, false);
|
||||
ensureDownloadsClearedState(downloadIDs, false);
|
||||
ensureFormEntriesClearedState(formEntries, true);
|
||||
|
||||
// OK, done, cleanup after ourselves.
|
||||
blankSlate();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
ensureDownloadsClearedState(downloadIDs, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the "Everything" duration option works.
|
||||
*/
|
||||
function () {
|
||||
// Add history.
|
||||
let uris = [];
|
||||
let places = [];
|
||||
let pURI;
|
||||
// within past hour, within past two hours, within past four hours and
|
||||
// outside past four hours
|
||||
[10, 70, 130, 250].forEach(function(aValue) {
|
||||
pURI = makeURI("http://" + aValue + "-minutes-ago.com/");
|
||||
places.push({uri: pURI, visitDate: visitTimeForMinutesAgo(aValue)});
|
||||
uris.push(pURI);
|
||||
});
|
||||
addVisits(places, function() {
|
||||
|
||||
// Open the dialog and do our tests.
|
||||
openWindow(function (aWin) {
|
||||
let wh = new WindowHelper(aWin);
|
||||
wh.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
|
||||
wh.checkPrefCheckbox("history", true);
|
||||
wh.acceptDialog();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
// Used as the download database ID for a new download. Incremented for each
|
||||
// new download. See addDownloadWithMinutesAgo().
|
||||
var gDownloadId = 5555551;
|
||||
|
||||
// Index in gAllTests of the test currently being run. Incremented for each
|
||||
// test run. See doNextTest().
|
||||
var gCurrTest = 0;
|
||||
|
||||
var now_uSec = Date.now() * 1000;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This wraps the dialog and provides some convenience methods for interacting
|
||||
* with it.
|
||||
*
|
||||
* A warning: Before you call any function that uses the tree (or any function
|
||||
* that calls a function that uses the tree), you must set a non-everything
|
||||
* duration by calling selectDuration(). The dialog does not initialize the
|
||||
* tree if it does not yet need to be shown.
|
||||
*
|
||||
* @param aWin
|
||||
* The dialog's nsIDOMWindow
|
||||
*/
|
||||
function WindowHelper(aWin) {
|
||||
this.win = aWin;
|
||||
}
|
||||
|
||||
WindowHelper.prototype = {
|
||||
/**
|
||||
* "Presses" the dialog's OK button.
|
||||
*/
|
||||
acceptDialog: function () {
|
||||
is(this.win.document.documentElement.getButton("accept").disabled, false,
|
||||
"Dialog's OK button should not be disabled");
|
||||
this.win.document.documentElement.acceptDialog();
|
||||
},
|
||||
|
||||
/**
|
||||
* "Presses" the dialog's Cancel button.
|
||||
*/
|
||||
cancelDialog: function () {
|
||||
this.win.document.documentElement.cancelDialog();
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the grippy row is in the right place, tree selection is OK,
|
||||
* and that the grippy's visible.
|
||||
*
|
||||
* @param aMsg
|
||||
* Passed to is() when checking grippy location
|
||||
* @param aExpectedRow
|
||||
* The row that the grippy should be at
|
||||
*/
|
||||
checkGrippy: function (aMsg, aExpectedRow) {
|
||||
is(this.getGrippyRow(), aExpectedRow, aMsg);
|
||||
this.checkTreeSelection();
|
||||
this.ensureGrippyIsVisible();
|
||||
},
|
||||
|
||||
/**
|
||||
* (Un)checks a history scope checkbox (browser & download history,
|
||||
* form history, etc.).
|
||||
*
|
||||
* @param aPrefName
|
||||
* The final portion of the checkbox's privacy.cpd.* preference name
|
||||
* @param aCheckState
|
||||
* True if the checkbox should be checked, false otherwise
|
||||
*/
|
||||
checkPrefCheckbox: function (aPrefName, aCheckState) {
|
||||
var pref = "privacy.cpd." + aPrefName;
|
||||
var cb = this.win.document.querySelectorAll(
|
||||
"#itemList > [preference='" + pref + "']");
|
||||
is(cb.length, 1, "found checkbox for " + pref + " preference");
|
||||
if (cb[0].checked != aCheckState)
|
||||
cb[0].click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the tree selection is appropriate to the grippy row. (A
|
||||
* single, contiguous selection should exist from the first row all the way
|
||||
* to the grippy.)
|
||||
*/
|
||||
checkTreeSelection: function () {
|
||||
let grippyRow = this.getGrippyRow();
|
||||
let sel = this.getTree().view.selection;
|
||||
if (grippyRow === 0) {
|
||||
is(sel.getRangeCount(), 0,
|
||||
"Grippy row is 0, so no tree selection should exist");
|
||||
}
|
||||
else {
|
||||
is(sel.getRangeCount(), 1,
|
||||
"Grippy row > 0, so only one tree selection range should exist");
|
||||
let min = {};
|
||||
let max = {};
|
||||
sel.getRangeAt(0, min, max);
|
||||
is(min.value, 0, "Tree selection should start at first row");
|
||||
is(max.value, grippyRow - 1,
|
||||
"Tree selection should end at row before grippy");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The grippy should always be visible when it's moved directly. This method
|
||||
* ensures that.
|
||||
*/
|
||||
ensureGrippyIsVisible: function () {
|
||||
let tbo = this.getTree().treeBoxObject;
|
||||
let firstVis = tbo.getFirstVisibleRow();
|
||||
let lastVis = tbo.getLastVisibleRow();
|
||||
let grippyRow = this.getGrippyRow();
|
||||
ok(firstVis <= grippyRow && grippyRow <= lastVis,
|
||||
"Grippy row should be visible; this inequality should be true: " +
|
||||
firstVis + " <= " + grippyRow + " <= " + lastVis);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return The dialog's duration dropdown
|
||||
*/
|
||||
getDurationDropdown: function () {
|
||||
return this.win.document.getElementById("sanitizeDurationChoice");
|
||||
},
|
||||
|
||||
/**
|
||||
* @return The grippy row index
|
||||
*/
|
||||
getGrippyRow: function () {
|
||||
return this.win.gContiguousSelectionTreeHelper.getGrippyRow();
|
||||
},
|
||||
|
||||
/**
|
||||
* @return The tree's row count (includes the grippy row)
|
||||
*/
|
||||
getRowCount: function () {
|
||||
return this.getTree().view.rowCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return The tree
|
||||
*/
|
||||
getTree: function () {
|
||||
return this.win.gContiguousSelectionTreeHelper.tree;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return True if the "Everything" warning panel is visible (as opposed to
|
||||
* the tree)
|
||||
*/
|
||||
isWarningPanelVisible: function () {
|
||||
return this.win.document.getElementById("durationDeck").selectedIndex == 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return True if the tree is visible (as opposed to the warning panel)
|
||||
*/
|
||||
isTreeVisible: function () {
|
||||
return this.win.document.getElementById("durationDeck").selectedIndex == 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves the grippy one row at a time in the direction and magnitude specified.
|
||||
* If aDelta < 0, moves the grippy up; if aDelta > 0, moves it down.
|
||||
*
|
||||
* @param aDelta
|
||||
* The amount and direction to move
|
||||
*/
|
||||
moveGrippyBy: function (aDelta) {
|
||||
if (aDelta === 0)
|
||||
return;
|
||||
let key = aDelta < 0 ? "UP" : "DOWN";
|
||||
let abs = Math.abs(aDelta);
|
||||
let treechildren = this.getTree().treeBoxObject.treeBody;
|
||||
treechildren.focus();
|
||||
for (let i = 0; i < abs; i++) {
|
||||
EventUtils.sendKey(key);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects a duration in the duration dropdown.
|
||||
*
|
||||
* @param aDurVal
|
||||
* One of the Sanitizer.TIMESPAN_* values
|
||||
*/
|
||||
selectDuration: function (aDurVal) {
|
||||
this.getDurationDropdown().value = aDurVal;
|
||||
if (aDurVal === Sanitizer.TIMESPAN_EVERYTHING) {
|
||||
is(this.isTreeVisible(), false,
|
||||
"Tree should not be visible for TIMESPAN_EVERYTHING");
|
||||
is(this.isWarningPanelVisible(), true,
|
||||
"Warning panel should be visible for TIMESPAN_EVERYTHING");
|
||||
}
|
||||
else {
|
||||
is(this.isTreeVisible(), true,
|
||||
"Tree should be visible for non-TIMESPAN_EVERYTHING");
|
||||
is(this.isWarningPanelVisible(), false,
|
||||
"Warning panel should not be visible for non-TIMESPAN_EVERYTHING");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a download to history.
|
||||
*
|
||||
* @param aMinutesAgo
|
||||
* The download will be downloaded this many minutes ago
|
||||
*/
|
||||
function addDownloadWithMinutesAgo(aMinutesAgo) {
|
||||
let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
|
||||
let data = {
|
||||
id: gDownloadId,
|
||||
name: name,
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: name,
|
||||
startTime: now_uSec - (aMinutesAgo * 60 * 1000000),
|
||||
endTime: now_uSec - ((aMinutesAgo + 1) *60 * 1000000),
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "a1bcD23eF4g5"
|
||||
};
|
||||
|
||||
let db = dm.DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
|
||||
"state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
|
||||
"VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
|
||||
":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
|
||||
try {
|
||||
for (let prop in data) {
|
||||
stmt.params[prop] = data[prop];
|
||||
}
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
is(downloadExists(gDownloadId), true,
|
||||
"Sanity check: download " + gDownloadId +
|
||||
" should exist after creating it");
|
||||
|
||||
return gDownloadId++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a form entry to history.
|
||||
*
|
||||
* @param aMinutesAgo
|
||||
* The entry will be added this many minutes ago
|
||||
*/
|
||||
function addFormEntryWithMinutesAgo(aMinutesAgo) {
|
||||
let name = aMinutesAgo + "-minutes-ago";
|
||||
formhist.addEntry(name, "dummy");
|
||||
|
||||
// Artifically age the entry to the proper vintage.
|
||||
let db = formhist.DBConnection;
|
||||
let timestamp = now_uSec - (aMinutesAgo * 60 * 1000000);
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '" + name + "'");
|
||||
|
||||
is(formhist.nameExists(name), true,
|
||||
"Sanity check: form entry " + name + " should exist after creating it");
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all history visits, downloads, and form entries.
|
||||
*/
|
||||
function blankSlate() {
|
||||
PlacesUtils.bhistory.removeAllPages();
|
||||
dm.cleanUp();
|
||||
formhist.removeAllEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the download with the specified ID exists.
|
||||
*
|
||||
* @param aID
|
||||
* The ID of the download to check
|
||||
* @return True if the download exists, false otherwise
|
||||
*/
|
||||
function downloadExists(aID)
|
||||
{
|
||||
let db = dm.DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"SELECT * " +
|
||||
"FROM moz_downloads " +
|
||||
"WHERE id = :id"
|
||||
);
|
||||
stmt.params.id = aID;
|
||||
let rows = stmt.executeStep();
|
||||
stmt.finalize();
|
||||
return !!rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the next test in the gAllTests array. If all tests have been run,
|
||||
* finishes the entire suite.
|
||||
*/
|
||||
function doNextTest() {
|
||||
if (gAllTests.length <= gCurrTest) {
|
||||
blankSlate();
|
||||
waitForAsyncUpdates(finish);
|
||||
}
|
||||
else {
|
||||
let ct = gCurrTest;
|
||||
gCurrTest++;
|
||||
gAllTests[ct]();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the specified downloads are either cleared or not.
|
||||
*
|
||||
* @param aDownloadIDs
|
||||
* Array of download database IDs
|
||||
* @param aShouldBeCleared
|
||||
* True if each download should be cleared, false otherwise
|
||||
*/
|
||||
function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
|
||||
let niceStr = aShouldBeCleared ? "no longer" : "still";
|
||||
aDownloadIDs.forEach(function (id) {
|
||||
is(downloadExists(id), !aShouldBeCleared,
|
||||
"download " + id + " should " + niceStr + " exist");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the specified form entries are either cleared or not.
|
||||
*
|
||||
* @param aFormEntries
|
||||
* Array of form entry names
|
||||
* @param aShouldBeCleared
|
||||
* True if each form entry should be cleared, false otherwise
|
||||
*/
|
||||
function ensureFormEntriesClearedState(aFormEntries, aShouldBeCleared) {
|
||||
let niceStr = aShouldBeCleared ? "no longer" : "still";
|
||||
aFormEntries.forEach(function (entry) {
|
||||
is(formhist.nameExists(entry), !aShouldBeCleared,
|
||||
"form entry " + entry + " should " + niceStr + " exist");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the sanitize dialog and runs a callback once it's finished loading.
|
||||
*
|
||||
* @param aOnloadCallback
|
||||
* A function that will be called once the dialog has loaded
|
||||
*/
|
||||
function openWindow(aOnloadCallback) {
|
||||
function windowObserver(aSubject, aTopic, aData) {
|
||||
if (aTopic != "domwindowopened")
|
||||
return;
|
||||
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
win.addEventListener("load", function onload(event) {
|
||||
win.removeEventListener("load", onload, false);
|
||||
executeSoon(function () {
|
||||
// Some exceptions that reach here don't reach the test harness, but
|
||||
// ok()/is() do...
|
||||
try {
|
||||
Task.spawn(function() {
|
||||
aOnloadCallback(win);
|
||||
}).then(function() {
|
||||
waitForAsyncUpdates(doNextTest);
|
||||
});
|
||||
}
|
||||
catch (exc) {
|
||||
win.close();
|
||||
ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
Services.ww.registerNotification(windowObserver);
|
||||
Services.ww.openWindow(null,
|
||||
"chrome://browser/content/sanitize.xul",
|
||||
"Sanitize",
|
||||
"chrome,titlebar,dialog,centerscreen,modal",
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a visit time.
|
||||
*
|
||||
* @param aMinutesAgo
|
||||
* The visit will be visited this many minutes ago
|
||||
*/
|
||||
function visitTimeForMinutesAgo(aMinutesAgo) {
|
||||
return now_uSec - (aMinutesAgo * 60 * 1000000);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function test() {
|
||||
blankSlate();
|
||||
waitForExplicitFinish();
|
||||
// Kick off all the tests in the gAllTests array.
|
||||
waitForAsyncUpdates(doNextTest);
|
||||
}
|
@ -69,7 +69,7 @@ var gMainPane = {
|
||||
let syncListener = gMainPane.onGetStarted.bind(gMainPane);
|
||||
getStartedLink.addEventListener("click", syncListener);
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
let uAppData = OS.Constants.Path.userApplicationDataDir;
|
||||
let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
|
||||
|
||||
@ -87,12 +87,13 @@ var gMainPane = {
|
||||
separateProfileModeChange: function ()
|
||||
{
|
||||
function quitApp() {
|
||||
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestartNotSameProfile);
|
||||
Services.startup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
|
||||
Components.interfaces.nsIAppStartup.eRestartNotSameProfile);
|
||||
}
|
||||
function revertCheckbox(error) {
|
||||
separateProfileModeCheckbox.checked = !separateProfileModeCheckbox.checked;
|
||||
if (error) {
|
||||
Cu.reportError("Failed to toggle separate profile mode: " + error);
|
||||
Components.utils.reportError("Failed to toggle separate profile mode: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,13 +107,13 @@ var gMainPane = {
|
||||
let shouldProceed = Services.prompt.confirm(window, title, msg)
|
||||
if (shouldProceed) {
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Ci.nsISupportsPRBool);
|
||||
.createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
|
||||
"restart");
|
||||
shouldProceed = !cancelQuit.data;
|
||||
|
||||
if (shouldProceed) {
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
let uAppData = OS.Constants.Path.userApplicationDataDir;
|
||||
let ignoreSeparateProfile = OS.Path.join(uAppData, "ignore-dev-edition-profile");
|
||||
|
||||
@ -131,7 +132,7 @@ var gMainPane = {
|
||||
onGetStarted: function (aEvent) {
|
||||
const Cc = Components.classes, Ci = Components.interfaces;
|
||||
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
let win = wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
if (win) {
|
||||
@ -254,7 +255,7 @@ var gMainPane = {
|
||||
const Cc = Components.classes, Ci = Components.interfaces;
|
||||
// If we're in instant-apply mode, use the most recent browser window
|
||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
win = wm.getMostRecentWindow("navigator:browser");
|
||||
}
|
||||
else {
|
||||
|
@ -89,8 +89,6 @@ public:
|
||||
mStream = aStream;
|
||||
mAudioSource = aAudioSource;
|
||||
mVideoSource = aVideoSource;
|
||||
mLastEndTimeAudio = 0;
|
||||
mLastEndTimeVideo = 0;
|
||||
|
||||
mStream->AddListener(this);
|
||||
}
|
||||
@ -187,10 +185,10 @@ public:
|
||||
// Currently audio sources ignore NotifyPull, but they could
|
||||
// watch it especially for fake audio.
|
||||
if (mAudioSource) {
|
||||
mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime, mLastEndTimeAudio);
|
||||
mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime);
|
||||
}
|
||||
if (mVideoSource) {
|
||||
mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime, mLastEndTimeVideo);
|
||||
mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,8 +237,6 @@ private:
|
||||
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe refcnt
|
||||
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe refcnt
|
||||
nsRefPtr<SourceMediaStream> mStream; // threadsafe refcnt
|
||||
StreamTime mLastEndTimeAudio;
|
||||
StreamTime mLastEndTimeVideo;
|
||||
bool mFinished;
|
||||
|
||||
// Accessed from MainThread and MSG thread
|
||||
|
@ -206,6 +206,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
|
||||
aStream, data->mID, int64_t(data->mStart),
|
||||
int64_t(segment->GetDuration())));
|
||||
|
||||
data->mEndOfFlushedData += segment->GetDuration();
|
||||
aStream->mBuffer.AddTrack(data->mID, data->mStart, segment);
|
||||
// The track has taken ownership of data->mData, so let's replace
|
||||
// data->mData with an empty clone.
|
||||
@ -217,6 +218,7 @@ MediaStreamGraphImpl::ExtractPendingInput(SourceMediaStream* aStream,
|
||||
aStream, data->mID,
|
||||
int64_t(dest->GetDuration()),
|
||||
int64_t(dest->GetDuration() + data->mData->GetDuration())));
|
||||
data->mEndOfFlushedData += data->mData->GetDuration();
|
||||
dest->AppendFrom(data->mData);
|
||||
}
|
||||
if (data->mCommands & SourceMediaStream::TRACK_END) {
|
||||
@ -2279,6 +2281,7 @@ SourceMediaStream::AddTrackInternal(TrackID aID, TrackRate aRate, StreamTime aSt
|
||||
data->mID = aID;
|
||||
data->mInputRate = aRate;
|
||||
data->mStart = aStart;
|
||||
data->mEndOfFlushedData = aStart;
|
||||
data->mCommands = TRACK_CREATE;
|
||||
data->mData = aSegment;
|
||||
data->mHaveEnough = false;
|
||||
@ -2437,6 +2440,18 @@ SourceMediaStream::HaveEnoughBuffered(TrackID aID)
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamTime
|
||||
SourceMediaStream::GetEndOfAppendedData(TrackID aID)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
TrackData *track = FindDataForTrack(aID);
|
||||
if (track) {
|
||||
return track->mEndOfFlushedData + track->mData->GetDuration();
|
||||
}
|
||||
NS_ERROR("Track not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SourceMediaStream::DispatchWhenNotEnoughBuffered(TrackID aID,
|
||||
nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable)
|
||||
|
@ -750,6 +750,13 @@ public:
|
||||
* Returns false if there isn't enough data or if no such track exists.
|
||||
*/
|
||||
bool HaveEnoughBuffered(TrackID aID);
|
||||
/**
|
||||
* Get the stream time of the end of the data that has been appended so far.
|
||||
* Can be called from any thread but won't be useful if it can race with
|
||||
* an AppendToTrack call, so should probably just be called from the thread
|
||||
* that also calls AppendToTrack.
|
||||
*/
|
||||
StreamTime GetEndOfAppendedData(TrackID aID);
|
||||
/**
|
||||
* Ensures that aSignalRunnable will be dispatched to aSignalThread
|
||||
* when we don't have enough buffered data in the track (which could be
|
||||
@ -848,13 +855,15 @@ protected:
|
||||
int mResamplerChannelCount;
|
||||
#endif
|
||||
StreamTime mStart;
|
||||
// Each time the track updates are flushed to the media graph thread,
|
||||
// this is cleared.
|
||||
uint32_t mCommands;
|
||||
// End-time of data already flushed to the track (excluding mData)
|
||||
StreamTime mEndOfFlushedData;
|
||||
// Each time the track updates are flushed to the media graph thread,
|
||||
// the segment buffer is emptied.
|
||||
nsAutoPtr<MediaSegment> mData;
|
||||
nsTArray<ThreadAndRunnable> mDispatchWhenNotEnough;
|
||||
// Each time the track updates are flushed to the media graph thread,
|
||||
// this is cleared.
|
||||
uint32_t mCommands;
|
||||
bool mHaveEnough;
|
||||
};
|
||||
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
layers::SurfaceTextureImage::Data data;
|
||||
data.mSurfTex = mSurfaceTexture.get();
|
||||
data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
|
||||
data.mInverted = true;
|
||||
data.mOriginPos = gl::OriginPos::BottomLeft;
|
||||
|
||||
layers::SurfaceTextureImage* stImg = static_cast<layers::SurfaceTextureImage*>(img.get());
|
||||
stImg->SetData(data);
|
||||
@ -138,7 +138,7 @@ public:
|
||||
data.mSync = eglSync;
|
||||
data.mOwns = true;
|
||||
data.mSize = gfx::IntSize(mConfig.display_width, mConfig.display_height);
|
||||
data.mInverted = false;
|
||||
data.mOriginPos = gl::OriginPos::TopLeft;
|
||||
|
||||
layers::EGLImageImage* typedImg = static_cast<layers::EGLImageImage*>(img.get());
|
||||
typedImg->SetData(data);
|
||||
|
@ -149,7 +149,7 @@ GMPAudioDecoderParent::Close()
|
||||
|
||||
// In case this is the last reference
|
||||
nsRefPtr<GMPAudioDecoderParent> kungfudeathgrip(this);
|
||||
NS_RELEASE(kungfudeathgrip);
|
||||
Release();
|
||||
Shutdown();
|
||||
|
||||
return NS_OK;
|
||||
|
@ -76,7 +76,7 @@ GMPVideoDecoderParent::Close()
|
||||
|
||||
// In case this is the last reference
|
||||
nsRefPtr<GMPVideoDecoderParent> kungfudeathgrip(this);
|
||||
NS_RELEASE(kungfudeathgrip);
|
||||
Release();
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ GMPVideoEncoderParent::Close()
|
||||
|
||||
// In case this is the last reference
|
||||
nsRefPtr<GMPVideoEncoderParent> kungfudeathgrip(this);
|
||||
NS_RELEASE(kungfudeathgrip);
|
||||
Release();
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,10 @@ SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (aMode == SourceBufferAppendMode::Sequence) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
|
||||
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
|
||||
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
|
||||
|
@ -19,6 +19,7 @@ skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet o
|
||||
[test_SeekableAfterEndOfStreamSplit.html]
|
||||
[test_SeekableBeforeEndOfStream.html]
|
||||
[test_SeekableBeforeEndOfStreamSplit.html]
|
||||
[test_SetModeThrows.html]
|
||||
[test_SplitAppendDelay.html]
|
||||
[test_SplitAppend.html]
|
||||
[test_WaitingOnMissingData.html]
|
||||
|
34
dom/media/mediasource/test/test_SetModeThrows.html
Normal file
34
dom/media/mediasource/test/test_SetModeThrows.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>MSE: append initialization only</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="mediasource.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// This test should be removed when we implement sequence mode in bug 1116353
|
||||
runWithMSE(function (ms, v) {
|
||||
ms.addEventListener("sourceopen", function () {
|
||||
var sb = ms.addSourceBuffer("video/webm");
|
||||
|
||||
sb.mode = "segments";
|
||||
ok("true", "Setting to segments does not throw");
|
||||
try {
|
||||
sb.mode = "sequence";
|
||||
ok(false, "Should have thrown");
|
||||
} catch (e) { ok(/supported/.test(e), "Correctly threw not supported: " + e); }
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -110,8 +110,7 @@ public:
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime) = 0;
|
||||
StreamTime aDesiredTime) = 0;
|
||||
|
||||
/* Stop the device and release the corresponding MediaStream */
|
||||
virtual nsresult Stop(SourceMediaStream *aSource, TrackID aID) = 0;
|
||||
|
@ -244,8 +244,7 @@ void
|
||||
MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aID,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime)
|
||||
StreamTime aDesiredTime)
|
||||
{
|
||||
// AddTrack takes ownership of segment
|
||||
VideoSegment segment;
|
||||
@ -256,7 +255,7 @@ MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
|
||||
// Note: we're not giving up mImage here
|
||||
nsRefPtr<layers::Image> image = mImage;
|
||||
StreamTime delta = aDesiredTime - aLastEndTime;
|
||||
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
|
||||
|
||||
if (delta > 0) {
|
||||
// nullptr images are allowed
|
||||
@ -264,9 +263,7 @@ MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
segment.AppendFrame(image.forget(), delta, size);
|
||||
// This can fail if either a) we haven't added the track yet, or b)
|
||||
// we've removed or finished the track.
|
||||
if (aSource->AppendToTrack(aID, &segment)) {
|
||||
aLastEndTime = aDesiredTime;
|
||||
}
|
||||
aSource->AppendToTrack(aID, &segment);
|
||||
// Generate null data for fake tracks.
|
||||
if (mHasFakeTracks) {
|
||||
for (int i = 0; i < kFakeVideoTrackCount; ++i) {
|
||||
|
@ -53,8 +53,7 @@ public:
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime);
|
||||
StreamTime aDesiredTime) MOZ_OVERRIDE;
|
||||
virtual bool SatisfiesConstraintSets(
|
||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets)
|
||||
{
|
||||
@ -122,8 +121,7 @@ public:
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime) {}
|
||||
StreamTime aDesiredTime) MOZ_OVERRIDE {}
|
||||
|
||||
virtual bool IsFake() {
|
||||
return true;
|
||||
|
@ -70,8 +70,7 @@ void
|
||||
MediaEngineGonkVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aID,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime& aLastEndTime)
|
||||
StreamTime aDesiredTime)
|
||||
{
|
||||
VideoSegment segment;
|
||||
|
||||
@ -82,7 +81,7 @@ MediaEngineGonkVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
|
||||
// Note: we're not giving up mImage here
|
||||
nsRefPtr<layers::Image> image = mImage;
|
||||
StreamTime delta = aDesiredTime - aLastEndTime;
|
||||
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
|
||||
LOGFRAME(("NotifyPull, desired = %ld, delta = %ld %s", (int64_t) aDesiredTime,
|
||||
(int64_t) delta, image ? "" : "<null>"));
|
||||
|
||||
@ -102,9 +101,7 @@ MediaEngineGonkVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
segment.AppendFrame(image.forget(), delta, size);
|
||||
// This can fail if either a) we haven't added the track yet, or b)
|
||||
// we've removed or finished the track.
|
||||
if (aSource->AppendToTrack(aID, &(segment))) {
|
||||
aLastEndTime = aDesiredTime;
|
||||
}
|
||||
aSource->AppendToTrack(aID, &(segment));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,7 @@ public:
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime) MOZ_OVERRIDE;
|
||||
StreamTime aDesiredTime) MOZ_OVERRIDE;
|
||||
virtual bool SatisfiesConstraintSets(
|
||||
const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets)
|
||||
{
|
||||
|
@ -192,24 +192,21 @@ MediaEngineTabVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
|
||||
void
|
||||
MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aID, StreamTime aDesiredTime,
|
||||
StreamTime& aLastEndTime)
|
||||
TrackID aID, StreamTime aDesiredTime)
|
||||
{
|
||||
VideoSegment segment;
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
|
||||
// Note: we're not giving up mImage here
|
||||
nsRefPtr<layers::CairoImage> image = mImage;
|
||||
StreamTime delta = aDesiredTime - aLastEndTime;
|
||||
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
|
||||
if (delta > 0) {
|
||||
// nullptr images are allowed
|
||||
gfx::IntSize size = image ? image->GetSize() : IntSize(0, 0);
|
||||
segment.AppendFrame(image.forget().downcast<layers::Image>(), delta, size);
|
||||
// This can fail if either a) we haven't added the track yet, or b)
|
||||
// we've removed or finished the track.
|
||||
if (aSource->AppendToTrack(aID, &(segment))) {
|
||||
aLastEndTime = aDesiredTime;
|
||||
}
|
||||
aSource->AppendToTrack(aID, &(segment));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventList
|
||||
virtual nsresult Deallocate();
|
||||
virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID);
|
||||
virtual void SetDirectListeners(bool aHasDirectListeners) {};
|
||||
virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, mozilla::StreamTime&);
|
||||
virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) MOZ_OVERRIDE;
|
||||
virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
|
||||
virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t);
|
||||
virtual bool IsFake();
|
||||
|
@ -97,8 +97,7 @@ public:
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime);
|
||||
StreamTime aDesiredTime) MOZ_OVERRIDE;
|
||||
|
||||
virtual const MediaSourceType GetMediaSource() {
|
||||
return mMediaSource;
|
||||
@ -180,8 +179,7 @@ public:
|
||||
virtual void NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aId,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime);
|
||||
StreamTime aDesiredTime) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool IsFake() {
|
||||
return false;
|
||||
|
@ -398,16 +398,10 @@ void
|
||||
MediaEngineWebRTCAudioSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream *aSource,
|
||||
TrackID aID,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime)
|
||||
StreamTime aDesiredTime)
|
||||
{
|
||||
// Ignore - we push audio data
|
||||
#ifdef DEBUG
|
||||
StreamTime delta = aDesiredTime - aLastEndTime;
|
||||
LOG(("Audio: NotifyPull: aDesiredTime %ld, delta %ld",(int64_t) aDesiredTime,
|
||||
(int64_t) delta));
|
||||
aLastEndTime = aDesiredTime;
|
||||
#endif
|
||||
LOG_FRAMES(("NotifyPull, desired = %ld", (int64_t) aDesiredTime));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -124,8 +124,7 @@ void
|
||||
MediaEngineWebRTCVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
SourceMediaStream* aSource,
|
||||
TrackID aID,
|
||||
StreamTime aDesiredTime,
|
||||
StreamTime &aLastEndTime)
|
||||
StreamTime aDesiredTime)
|
||||
{
|
||||
VideoSegment segment;
|
||||
|
||||
@ -134,7 +133,7 @@ MediaEngineWebRTCVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
// So mState could be kReleased here. We really don't care about the state,
|
||||
// though.
|
||||
|
||||
StreamTime delta = aDesiredTime - aLastEndTime;
|
||||
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
|
||||
LOGFRAME(("NotifyPull, desired = %ld, delta = %ld %s", (int64_t) aDesiredTime,
|
||||
(int64_t) delta, mImage.get() ? "" : "<null>"));
|
||||
|
||||
@ -150,9 +149,7 @@ MediaEngineWebRTCVideoSource::NotifyPull(MediaStreamGraph* aGraph,
|
||||
// Doing so means a negative delta and thus messes up handling of the graph
|
||||
if (delta > 0) {
|
||||
// nullptr images are allowed
|
||||
if (AppendToTrack(aSource, mImage, aID, delta)) {
|
||||
aLastEndTime = aDesiredTime;
|
||||
}
|
||||
AppendToTrack(aSource, mImage, aID, delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "nsNPAPIPluginInstance.h"
|
||||
#include "gfxRect.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla;
|
||||
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
|
||||
@ -24,8 +23,13 @@ static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
|
||||
}
|
||||
|
||||
static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) {
|
||||
// NativeWindow is TopLeft if uninverted.
|
||||
gl::OriginPos newOriginPos = gl::OriginPos::TopLeft;
|
||||
if (isContentInverted)
|
||||
newOriginPos = gl::OriginPos::BottomLeft;
|
||||
|
||||
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
|
||||
pinst->SetInverted(isContentInverted);
|
||||
pinst->SetOriginPos(newOriginPos);
|
||||
pinst->RedrawPlugin();
|
||||
}
|
||||
|
||||
|
@ -60,10 +60,14 @@ static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info)
|
||||
}
|
||||
|
||||
static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) {
|
||||
// OpenGL is BottomLeft if uninverted.
|
||||
gl::OriginPos newOriginPos = gl::OriginPos::BottomLeft;
|
||||
if (isContentInverted)
|
||||
newOriginPos = gl::OriginPos::TopLeft;
|
||||
|
||||
nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
|
||||
|
||||
// Our definition of inverted is the opposite of the plugin's
|
||||
pinst->SetInverted(!isContentInverted);
|
||||
pinst->SetOriginPos(newOriginPos);
|
||||
pinst->RedrawPlugin();
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ nsNPAPIPluginInstance::nsNPAPIPluginInstance()
|
||||
, mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary)
|
||||
, mWakeLocked(false)
|
||||
, mFullScreen(false)
|
||||
, mInverted(false)
|
||||
, mOriginPos(gl::OriginPos::TopLeft)
|
||||
#endif
|
||||
, mRunning(NOT_STARTED)
|
||||
, mWindowless(false)
|
||||
@ -1056,14 +1056,6 @@ void nsNPAPIPluginInstance::GetVideos(nsTArray<VideoInfo*>& aVideos)
|
||||
aVideos.AppendElement(it->second);
|
||||
}
|
||||
|
||||
void nsNPAPIPluginInstance::SetInverted(bool aInverted)
|
||||
{
|
||||
if (aInverted == mInverted)
|
||||
return;
|
||||
|
||||
mInverted = aInverted;
|
||||
}
|
||||
|
||||
nsNPAPIPluginInstance* nsNPAPIPluginInstance::GetFromNPP(NPP npp)
|
||||
{
|
||||
std::map<NPP, nsNPAPIPluginInstance*>::iterator it;
|
||||
|
@ -220,8 +220,10 @@ public:
|
||||
|
||||
void GetVideos(nsTArray<VideoInfo*>& aVideos);
|
||||
|
||||
void SetInverted(bool aInverted);
|
||||
bool Inverted() { return mInverted; }
|
||||
void SetOriginPos(mozilla::gl::OriginPos aOriginPos) {
|
||||
mOriginPos = aOriginPos;
|
||||
}
|
||||
mozilla::gl::OriginPos OriginPos() const { return mOriginPos; }
|
||||
|
||||
static nsNPAPIPluginInstance* GetFromNPP(NPP npp);
|
||||
#endif
|
||||
@ -328,7 +330,7 @@ protected:
|
||||
uint32_t mFullScreenOrientation;
|
||||
bool mWakeLocked;
|
||||
bool mFullScreen;
|
||||
bool mInverted;
|
||||
mozilla::gl::OriginPos mOriginPos;
|
||||
|
||||
mozilla::RefPtr<SharedPluginTexture> mContentTexture;
|
||||
mozilla::RefPtr<mozilla::gl::AndroidSurfaceTexture> mContentSurface;
|
||||
|
@ -176,7 +176,7 @@ AttachToContainerAsEGLImage(ImageContainer* container,
|
||||
EGLImageImage::Data data;
|
||||
data.mImage = image;
|
||||
data.mSize = gfx::IntSize(rect.width, rect.height);
|
||||
data.mInverted = instance->Inverted();
|
||||
data.mOriginPos = instance->OriginPos();
|
||||
|
||||
EGLImageImage* typedImg = static_cast<EGLImageImage*>(img.get());
|
||||
typedImg->SetData(data);
|
||||
@ -203,7 +203,7 @@ AttachToContainerAsSurfaceTexture(ImageContainer* container,
|
||||
SurfaceTextureImage::Data data;
|
||||
data.mSurfTex = surfTex;
|
||||
data.mSize = gfx::IntSize(rect.width, rect.height);
|
||||
data.mInverted = instance->Inverted();
|
||||
data.mOriginPos = instance->OriginPos();
|
||||
|
||||
SurfaceTextureImage* typedImg = static_cast<SurfaceTextureImage*>(img.get());
|
||||
typedImg->SetData(data);
|
||||
@ -1386,7 +1386,8 @@ nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInf
|
||||
|
||||
// The logic below for Honeycomb is just a guess, but seems to work. We don't have a separate
|
||||
// inverted flag for video.
|
||||
data.mInverted = AndroidBridge::Bridge()->IsHoneycomb() ? true : mInstance->Inverted();
|
||||
data.mOriginPos = AndroidBridge::Bridge()->IsHoneycomb() ? gl::OriginPos::BottomLeft
|
||||
: mInstance->OriginPos();
|
||||
data.mSize = gfx::IntSize(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height);
|
||||
|
||||
SurfaceTextureImage* typedImg = static_cast<SurfaceTextureImage*>(img.get());
|
||||
|
@ -690,7 +690,7 @@ GLBlitHelper::BindAndUploadEGLImage(EGLImage image, GLuint target)
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
||||
bool
|
||||
GLBlitHelper::BlitGrallocImage(layers::GrallocImage* grallocImage, bool yFlip)
|
||||
GLBlitHelper::BlitGrallocImage(layers::GrallocImage* grallocImage, bool yflip)
|
||||
{
|
||||
ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
|
||||
mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
|
||||
@ -711,7 +711,7 @@ GLBlitHelper::BlitGrallocImage(layers::GrallocImage* grallocImage, bool yFlip)
|
||||
|
||||
BindAndUploadEGLImage(image, LOCAL_GL_TEXTURE_EXTERNAL_OES);
|
||||
|
||||
mGL->fUniform1f(mYFlipLoc, yFlip ? (float)1.0f : (float)0.0f);
|
||||
mGL->fUniform1f(mYFlipLoc, yflip ? (float)1.0f : (float)0.0f);
|
||||
|
||||
mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
@ -724,18 +724,14 @@ GLBlitHelper::BlitGrallocImage(layers::GrallocImage* grallocImage, bool yFlip)
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
|
||||
bool
|
||||
GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool yFlip)
|
||||
GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool yflip)
|
||||
{
|
||||
AndroidSurfaceTexture* surfaceTexture = stImage->GetData()->mSurfTex;
|
||||
if (stImage->GetData()->mInverted) {
|
||||
yFlip = !yFlip;
|
||||
}
|
||||
|
||||
ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
|
||||
|
||||
if (NS_FAILED(surfaceTexture->Attach(mGL))) {
|
||||
if (NS_FAILED(surfaceTexture->Attach(mGL)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// UpdateTexImage() changes the EXTERNAL binding, so save it here
|
||||
// so we can restore it after.
|
||||
@ -748,7 +744,7 @@ GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool
|
||||
surfaceTexture->GetTransformMatrix(transform);
|
||||
|
||||
mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &transform._11);
|
||||
mGL->fUniform1f(mYFlipLoc, yFlip ? 1.0f : 0.0f);
|
||||
mGL->fUniform1f(mYFlipLoc, yflip ? 1.0f : 0.0f);
|
||||
mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
surfaceTexture->Detach();
|
||||
@ -758,15 +754,11 @@ GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool
|
||||
}
|
||||
|
||||
bool
|
||||
GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image, bool yFlip)
|
||||
GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image, bool yflip)
|
||||
{
|
||||
EGLImage eglImage = image->GetData()->mImage;
|
||||
EGLSync eglSync = image->GetData()->mSync;
|
||||
|
||||
if (image->GetData()->mInverted) {
|
||||
yFlip = !yFlip;
|
||||
}
|
||||
|
||||
if (eglSync) {
|
||||
EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), eglSync, 0, LOCAL_EGL_FOREVER);
|
||||
if (status != LOCAL_EGL_CONDITION_SATISFIED) {
|
||||
@ -781,7 +773,7 @@ GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image, bool yFlip)
|
||||
|
||||
BindAndUploadEGLImage(eglImage, LOCAL_GL_TEXTURE_2D);
|
||||
|
||||
mGL->fUniform1f(mYFlipLoc, yFlip ? 1.0f : 0.0f);
|
||||
mGL->fUniform1f(mYFlipLoc, yflip ? 1.0f : 0.0f);
|
||||
|
||||
mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
@ -792,7 +784,7 @@ GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image, bool yFlip)
|
||||
#endif
|
||||
|
||||
bool
|
||||
GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage, bool yFlip)
|
||||
GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage, bool yflip)
|
||||
{
|
||||
ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
|
||||
const PlanarYCbCrData* yuvData = yuvImage->GetData();
|
||||
@ -813,7 +805,7 @@ GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage, bool yFli
|
||||
BindAndUploadYUVTexture(Channel_Cb, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCbChannel, needsAllocation);
|
||||
BindAndUploadYUVTexture(Channel_Cr, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCrChannel, needsAllocation);
|
||||
|
||||
mGL->fUniform1f(mYFlipLoc, yFlip ? (float)1.0 : (float)0.0);
|
||||
mGL->fUniform1f(mYFlipLoc, yflip ? (float)1.0 : (float)0.0);
|
||||
|
||||
if (needsAllocation) {
|
||||
mGL->fUniform2f(mYTexScaleLoc, (float)yuvData->mYSize.width/yuvData->mYStride, 1.0f);
|
||||
@ -832,7 +824,7 @@ bool
|
||||
GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
|
||||
const gfx::IntSize& destSize,
|
||||
GLuint destFB,
|
||||
bool yFlip,
|
||||
bool yflip,
|
||||
GLuint xoffset,
|
||||
GLuint yoffset,
|
||||
GLuint cropWidth,
|
||||
@ -878,22 +870,22 @@ GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (type == ConvertGralloc) {
|
||||
layers::GrallocImage* grallocImage = static_cast<layers::GrallocImage*>(srcImage);
|
||||
return BlitGrallocImage(grallocImage, yFlip);
|
||||
return BlitGrallocImage(grallocImage, yflip);
|
||||
}
|
||||
#endif
|
||||
if (type == ConvertPlanarYCbCr) {
|
||||
mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
|
||||
PlanarYCbCrImage* yuvImage = static_cast<PlanarYCbCrImage*>(srcImage);
|
||||
return BlitPlanarYCbCrImage(yuvImage, yFlip);
|
||||
return BlitPlanarYCbCrImage(yuvImage, yflip);
|
||||
}
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
if (type == ConvertSurfaceTexture) {
|
||||
layers::SurfaceTextureImage* stImage = static_cast<layers::SurfaceTextureImage*>(srcImage);
|
||||
return BlitSurfaceTextureImage(stImage, yFlip);
|
||||
return BlitSurfaceTextureImage(stImage, yflip);
|
||||
}
|
||||
if (type == ConvertEGLImage) {
|
||||
layers::EGLImageImage* eglImage = static_cast<layers::EGLImageImage*>(srcImage);
|
||||
return BlitEGLImageImage(eglImage, yFlip);
|
||||
return BlitEGLImageImage(eglImage, yflip);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -905,7 +897,7 @@ GLBlitHelper::BlitImageToTexture(layers::Image* srcImage,
|
||||
const gfx::IntSize& destSize,
|
||||
GLuint destTex,
|
||||
GLenum destTarget,
|
||||
bool yFlip,
|
||||
bool yflip,
|
||||
GLuint xoffset,
|
||||
GLuint yoffset,
|
||||
GLuint cropWidth,
|
||||
@ -913,13 +905,13 @@ GLBlitHelper::BlitImageToTexture(layers::Image* srcImage,
|
||||
{
|
||||
ScopedGLDrawState autoStates(mGL);
|
||||
|
||||
if (!mFBO) {
|
||||
if (!mFBO)
|
||||
mGL->fGenFramebuffers(1, &mFBO);
|
||||
}
|
||||
|
||||
ScopedBindFramebuffer boundFB(mGL, mFBO);
|
||||
mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, destTarget, destTex, 0);
|
||||
return BlitImageToFramebuffer(srcImage, destSize, mFBO, yFlip, xoffset, yoffset,
|
||||
mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
|
||||
destTarget, destTex, 0);
|
||||
return BlitImageToFramebuffer(srcImage, destSize, mFBO, yflip, xoffset, yoffset,
|
||||
cropWidth, cropHeight);
|
||||
}
|
||||
|
||||
@ -1027,5 +1019,5 @@ GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
|
||||
srcSize, destSize, destTarget);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
||||
|
@ -145,12 +145,13 @@ class GLBlitHelper MOZ_FINAL
|
||||
void BindAndUploadEGLImage(EGLImage image, GLuint target);
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
bool BlitGrallocImage(layers::GrallocImage* grallocImage, bool yFlip = false);
|
||||
bool BlitGrallocImage(layers::GrallocImage* grallocImage, bool yflip);
|
||||
#endif
|
||||
bool BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage, bool yFlip = false);
|
||||
bool BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage, bool yflip);
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
bool BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool yFlip = false);
|
||||
bool BlitEGLImageImage(layers::EGLImageImage* eglImage, bool yFlip = false);
|
||||
// Blit onto the current FB.
|
||||
bool BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage, bool yflip);
|
||||
bool BlitEGLImageImage(layers::EGLImageImage* eglImage, bool yflip);
|
||||
#endif
|
||||
|
||||
public:
|
||||
@ -186,14 +187,15 @@ public:
|
||||
GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
|
||||
GLenum destTarget = LOCAL_GL_TEXTURE_2D);
|
||||
bool BlitImageToFramebuffer(layers::Image* srcImage, const gfx::IntSize& destSize,
|
||||
GLuint destFB, bool yFlip = false, GLuint xoffset = 0,
|
||||
GLuint destFB, bool yflip = false, GLuint xoffset = 0,
|
||||
GLuint yoffset = 0, GLuint width = 0, GLuint height = 0);
|
||||
bool BlitImageToTexture(layers::Image* srcImage, const gfx::IntSize& destSize,
|
||||
GLuint destTex, GLenum destTarget, bool yFlip = false, GLuint xoffset = 0,
|
||||
GLuint yoffset = 0, GLuint width = 0, GLuint height = 0);
|
||||
GLuint destTex, GLenum destTarget, bool yflip = false,
|
||||
GLuint xoffset = 0, GLuint yoffset = 0, GLuint width = 0,
|
||||
GLuint height = 0);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GLBLITHELPER_H_
|
||||
|
@ -22,6 +22,11 @@ MOZ_BEGIN_ENUM_CLASS(GLContextType)
|
||||
EGL
|
||||
MOZ_END_ENUM_CLASS(GLContextType)
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(OriginPos, uint8_t)
|
||||
TopLeft,
|
||||
BottomLeft
|
||||
MOZ_END_ENUM_CLASS(OriginPos)
|
||||
|
||||
struct GLFormats
|
||||
{
|
||||
// Constructs a zeroed object:
|
||||
@ -39,7 +44,6 @@ struct GLFormats
|
||||
GLsizei samples;
|
||||
};
|
||||
|
||||
|
||||
struct PixelBufferFormat
|
||||
{
|
||||
// Constructs a zeroed object:
|
||||
@ -53,7 +57,6 @@ struct PixelBufferFormat
|
||||
int ColorBits() const { return red + green + blue; }
|
||||
};
|
||||
|
||||
|
||||
} /* namespace gl */
|
||||
} /* namespace mozilla */
|
||||
|
||||
|
@ -49,7 +49,7 @@ GLScreenBuffer::Create(GLContext* gl,
|
||||
XRE_GetProcessType() != GeckoProcessType_Default)
|
||||
{
|
||||
layers::TextureFlags flags = layers::TextureFlags::DEALLOCATE_CLIENT |
|
||||
layers::TextureFlags::NEEDS_Y_FLIP;
|
||||
layers::TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
if (!caps.premultAlpha) {
|
||||
flags |= layers::TextureFlags::NON_PREMULTIPLIED;
|
||||
}
|
||||
|
@ -597,9 +597,9 @@ gfx::IntRect TiledTextureImage::GetTileRect()
|
||||
gfx::IntRect TiledTextureImage::GetSrcTileRect()
|
||||
{
|
||||
gfx::IntRect rect = GetTileRect();
|
||||
unsigned int srcY = mFlags & NeedsYFlip
|
||||
? mSize.height - rect.height - rect.y
|
||||
: rect.y;
|
||||
const bool needsYFlip = mFlags & OriginBottomLeft;
|
||||
unsigned int srcY = needsYFlip ? mSize.height - rect.height - rect.y
|
||||
: rect.y;
|
||||
return gfx::IntRect(rect.x, srcY, rect.width, rect.height);
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
enum Flags {
|
||||
NoFlags = 0x0,
|
||||
UseNearestFilter = 0x1,
|
||||
NeedsYFlip = 0x2,
|
||||
OriginBottomLeft = 0x2,
|
||||
DisallowBigImage = 0x4
|
||||
};
|
||||
|
||||
@ -381,7 +381,8 @@ CreateBasicTextureImage(GLContext* aGL,
|
||||
* |aWrapMode| (usually GL_CLAMP_TO_EDGE or GL_REPEAT) and by
|
||||
* default, GL_LINEAR filtering. Specify
|
||||
* |aFlags=UseNearestFilter| for GL_NEAREST filtering. Specify
|
||||
* |aFlags=NeedsYFlip| if the image is flipped. Return
|
||||
* |aFlags=OriginBottomLeft| if the image is origin-bottom-left, instead of the
|
||||
* default origin-top-left. Return
|
||||
* nullptr if creating the TextureImage fails.
|
||||
*
|
||||
* The returned TextureImage may only be used with this GLContext.
|
||||
|
@ -29,8 +29,8 @@ MOZ_BEGIN_ENUM_CLASS(TextureFlags, uint32_t)
|
||||
NO_FLAGS = 0,
|
||||
// Use nearest-neighbour texture filtering (as opposed to linear filtering).
|
||||
USE_NEAREST_FILTER = 1 << 0,
|
||||
// The texture should be flipped along the y-axis when composited.
|
||||
NEEDS_Y_FLIP = 1 << 1,
|
||||
// The compositor assumes everything is origin-top-left by default.
|
||||
ORIGIN_BOTTOM_LEFT = 1 << 1,
|
||||
// Force the texture to be represented using a single tile (note that this means
|
||||
// tiled textures, not tiled layers).
|
||||
DISALLOW_BIGIMAGE = 1 << 2,
|
||||
|
@ -32,6 +32,7 @@ CopyableCanvasLayer::CopyableCanvasLayer(LayerManager* aLayerManager, void *aImp
|
||||
CanvasLayer(aLayerManager, aImplData)
|
||||
, mGLFrontbuffer(nullptr)
|
||||
, mIsAlphaPremultiplied(true)
|
||||
, mOriginPos(gl::OriginPos::TopLeft)
|
||||
{
|
||||
MOZ_COUNT_CTOR(CopyableCanvasLayer);
|
||||
}
|
||||
@ -49,7 +50,8 @@ CopyableCanvasLayer::Initialize(const Data& aData)
|
||||
if (aData.mGLContext) {
|
||||
mGLContext = aData.mGLContext;
|
||||
mIsAlphaPremultiplied = aData.mIsGLAlphaPremult;
|
||||
mNeedsYFlip = true;
|
||||
mOriginPos = gl::OriginPos::BottomLeft;
|
||||
|
||||
MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");
|
||||
|
||||
if (aData.mFrontbufferGLTex) {
|
||||
@ -63,9 +65,8 @@ CopyableCanvasLayer::Initialize(const Data& aData)
|
||||
} else if (aData.mDrawTarget) {
|
||||
mDrawTarget = aData.mDrawTarget;
|
||||
mSurface = mDrawTarget->Snapshot();
|
||||
mNeedsYFlip = false;
|
||||
} else {
|
||||
NS_ERROR("CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
|
||||
MOZ_CRASH("CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
|
||||
}
|
||||
|
||||
mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
|
||||
|
@ -55,7 +55,7 @@ protected:
|
||||
UniquePtr<gl::SharedSurface> mGLFrontbuffer;
|
||||
|
||||
bool mIsAlphaPremultiplied;
|
||||
bool mNeedsYFlip;
|
||||
gl::OriginPos mOriginPos;
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> mCachedTempSurface;
|
||||
|
||||
|
@ -40,9 +40,9 @@ public:
|
||||
|
||||
FrameMetrics()
|
||||
: mCompositionBounds(0, 0, 0, 0)
|
||||
, mCriticalDisplayPort(0, 0, 0, 0)
|
||||
, mPresShellResolution(1)
|
||||
, mDisplayPort(0, 0, 0, 0)
|
||||
, mCriticalDisplayPort(0, 0, 0, 0)
|
||||
, mScrollableRect(0, 0, 0, 0)
|
||||
, mCumulativeResolution(1)
|
||||
, mDevPixelsPerCSSPixel(1)
|
||||
@ -251,19 +251,6 @@ public:
|
||||
// layout/paint time.
|
||||
ParentLayerRect mCompositionBounds;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The following metrics are all in CSS pixels. They are not in any uniform
|
||||
// space, so each is explained separately.
|
||||
//
|
||||
|
||||
// If non-empty, the area of a frame's contents that is considered critical
|
||||
// to paint. Area outside of this area (i.e. area inside mDisplayPort, but
|
||||
// outside of mCriticalDisplayPort) is considered low-priority, and may be
|
||||
// painted with lower precision, or not painted at all.
|
||||
//
|
||||
// The same restrictions for mDisplayPort apply here.
|
||||
CSSRect mCriticalDisplayPort;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The following metrics are dimensionless.
|
||||
//
|
||||
@ -288,6 +275,16 @@ public:
|
||||
return mDisplayPort;
|
||||
}
|
||||
|
||||
void SetCriticalDisplayPort(const CSSRect& aCriticalDisplayPort)
|
||||
{
|
||||
mCriticalDisplayPort = aCriticalDisplayPort;
|
||||
}
|
||||
|
||||
CSSRect GetCriticalDisplayPort() const
|
||||
{
|
||||
return mCriticalDisplayPort;
|
||||
}
|
||||
|
||||
void SetCumulativeResolution(const LayoutDeviceToLayerScale& aCumulativeResolution)
|
||||
{
|
||||
mCumulativeResolution = aCumulativeResolution;
|
||||
@ -539,6 +536,14 @@ private:
|
||||
// width = window.innerWidth + 200, height = window.innerHeight + 200 }
|
||||
CSSRect mDisplayPort;
|
||||
|
||||
// If non-empty, the area of a frame's contents that is considered critical
|
||||
// to paint. Area outside of this area (i.e. area inside mDisplayPort, but
|
||||
// outside of mCriticalDisplayPort) is considered low-priority, and may be
|
||||
// painted with lower precision, or not painted at all.
|
||||
//
|
||||
// The same restrictions for mDisplayPort apply here.
|
||||
CSSRect mCriticalDisplayPort;
|
||||
|
||||
// The scrollable bounds of a frame. This is determined by reflow.
|
||||
// Ordinarily the x and y will be 0 and the width and height will be the
|
||||
// size of the element being scrolled. However for RTL pages or elements
|
||||
|
@ -40,7 +40,6 @@ GLImage::GetAsSourceSurface()
|
||||
|
||||
if (!sSnapshotContext) {
|
||||
sSnapshotContext = GLContextProvider::CreateHeadless();
|
||||
|
||||
if (!sSnapshotContext) {
|
||||
NS_WARNING("Failed to create snapshot GLContext");
|
||||
return nullptr;
|
||||
@ -63,6 +62,7 @@ GLImage::GetAsSourceSurface()
|
||||
GLBlitHelper helper(sSnapshotContext);
|
||||
|
||||
helper.BlitImageToFramebuffer(this, size, fb.FB(), false);
|
||||
|
||||
ScopedBindFramebuffer bind(sSnapshotContext, fb.FB());
|
||||
|
||||
RefPtr<gfx::DataSourceSurface> source =
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifndef GFX_GLIMAGES_H
|
||||
#define GFX_GLIMAGES_H
|
||||
|
||||
#include "GLContextTypes.h"
|
||||
#include "GLTypes.h"
|
||||
#include "ImageContainer.h" // for Image
|
||||
#include "ImageTypes.h" // for ImageFormat::SHARED_GLTEXTURE
|
||||
@ -31,10 +32,11 @@ public:
|
||||
EGLImage mImage;
|
||||
EGLSync mSync;
|
||||
gfx::IntSize mSize;
|
||||
bool mInverted;
|
||||
gl::OriginPos mOriginPos;
|
||||
bool mOwns;
|
||||
|
||||
Data() : mImage(nullptr), mSync(nullptr), mSize(0, 0), mInverted(false), mOwns(false)
|
||||
Data() : mImage(nullptr), mSync(nullptr), mSize(0, 0),
|
||||
mOriginPos(gl::OriginPos::TopLeft), mOwns(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
@ -60,7 +62,7 @@ public:
|
||||
struct Data {
|
||||
mozilla::gl::AndroidSurfaceTexture* mSurfTex;
|
||||
gfx::IntSize mSize;
|
||||
bool mInverted;
|
||||
gl::OriginPos mOriginPos;
|
||||
};
|
||||
|
||||
void SetData(const Data& aData) { mData = aData; }
|
||||
|
@ -176,7 +176,7 @@ AppendToString(std::stringstream& aStream, const FrameMetrics& m,
|
||||
AppendToString(aStream, m.GetSmoothScrollOffset(), "] [ss=");
|
||||
}
|
||||
AppendToString(aStream, m.GetDisplayPort(), "] [dp=");
|
||||
AppendToString(aStream, m.mCriticalDisplayPort, "] [cdp=");
|
||||
AppendToString(aStream, m.GetCriticalDisplayPort(), "] [cdp=");
|
||||
AppendToString(aStream, m.GetBackgroundColor(), "] [color=");
|
||||
if (!detailed) {
|
||||
AppendToString(aStream, m.GetScrollId(), "] [scrollId=");
|
||||
@ -289,7 +289,7 @@ AppendToString(std::stringstream& aStream, TextureFlags flags,
|
||||
}
|
||||
bool previous = false;
|
||||
AppendFlag(TextureFlags::USE_NEAREST_FILTER);
|
||||
AppendFlag(TextureFlags::NEEDS_Y_FLIP);
|
||||
AppendFlag(TextureFlags::ORIGIN_BOTTOM_LEFT);
|
||||
AppendFlag(TextureFlags::DISALLOW_BIGIMAGE);
|
||||
|
||||
#undef AppendFlag
|
||||
|
@ -79,7 +79,7 @@ MOZ_END_ENUM_CLASS(SurfaceMode)
|
||||
// by other surfaces we will need a more generic LayerRenderState.
|
||||
MOZ_BEGIN_ENUM_CLASS(LayerRenderStateFlags, int8_t)
|
||||
LAYER_RENDER_STATE_DEFAULT = 0,
|
||||
Y_FLIPPED = 1 << 0,
|
||||
ORIGIN_BOTTOM_LEFT = 1 << 0,
|
||||
BUFFER_ROTATION = 1 << 1,
|
||||
// Notify Composer2D to swap the RB pixels of gralloc buffer
|
||||
FORMAT_RB_SWAP = 1 << 2,
|
||||
@ -116,8 +116,8 @@ struct LayerRenderState {
|
||||
, mTexture(aTexture)
|
||||
{}
|
||||
|
||||
bool YFlipped() const
|
||||
{ return bool(mFlags & LayerRenderStateFlags::Y_FLIPPED); }
|
||||
bool OriginBottomLeft() const
|
||||
{ return bool(mFlags & LayerRenderStateFlags::ORIGIN_BOTTOM_LEFT); }
|
||||
|
||||
bool BufferRotated() const
|
||||
{ return bool(mFlags & LayerRenderStateFlags::BUFFER_ROTATION); }
|
||||
|
@ -2707,9 +2707,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.GetScrollableRect());
|
||||
LogRendertraceRect(GetGuid(), "painted displayport", "lightgreen",
|
||||
aLayerMetrics.GetDisplayPort() + aLayerMetrics.GetScrollOffset());
|
||||
if (!aLayerMetrics.mCriticalDisplayPort.IsEmpty()) {
|
||||
if (!aLayerMetrics.GetCriticalDisplayPort().IsEmpty()) {
|
||||
LogRendertraceRect(GetGuid(), "painted critical displayport", "darkgreen",
|
||||
aLayerMetrics.mCriticalDisplayPort + aLayerMetrics.GetScrollOffset());
|
||||
aLayerMetrics.GetCriticalDisplayPort() + aLayerMetrics.GetScrollOffset());
|
||||
}
|
||||
|
||||
mPaintThrottler.TaskComplete(GetFrameTime());
|
||||
|
@ -40,8 +40,10 @@ BasicCanvasLayer::Paint(DrawTarget* aDT,
|
||||
return;
|
||||
}
|
||||
|
||||
const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
|
||||
|
||||
Matrix oldTM;
|
||||
if (mNeedsYFlip) {
|
||||
if (needsYFlip) {
|
||||
oldTM = aDT->GetTransform();
|
||||
aDT->SetTransform(Matrix(oldTM).
|
||||
PreTranslate(0.0f, mBounds.height).
|
||||
@ -54,7 +56,7 @@ BasicCanvasLayer::Paint(DrawTarget* aDT,
|
||||
DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)),
|
||||
aMaskLayer);
|
||||
|
||||
if (mNeedsYFlip) {
|
||||
if (needsYFlip) {
|
||||
aDT->SetTransform(oldTM);
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +72,8 @@ CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
|
||||
gfxImageFormat format
|
||||
= gfxPlatform::GetPlatform()->OptimalFormatForContent(contentType);
|
||||
TextureFlags flags = TextureFlags::DEFAULT;
|
||||
if (mTextureFlags & TextureFlags::NEEDS_Y_FLIP) {
|
||||
flags |= TextureFlags::NEEDS_Y_FLIP;
|
||||
if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
|
||||
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat surfaceFormat = gfx::ImageFormatToSurfaceFormat(format);
|
||||
|
@ -78,7 +78,7 @@ ClientCanvasLayer::Initialize(const Data& aData)
|
||||
if (mGLContext->GetContextType() == GLContextType::EGL) {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
TextureFlags flags = TextureFlags::DEALLOCATE_CLIENT |
|
||||
TextureFlags::NEEDS_Y_FLIP;
|
||||
TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
if (!aData.mIsGLAlphaPremult) {
|
||||
flags |= TextureFlags::NON_PREMULTIPLIED;
|
||||
}
|
||||
@ -153,8 +153,8 @@ ClientCanvasLayer::RenderLayer()
|
||||
|
||||
if (!mCanvasClient) {
|
||||
TextureFlags flags = TextureFlags::IMMEDIATE_UPLOAD;
|
||||
if (mNeedsYFlip) {
|
||||
flags |= TextureFlags::NEEDS_Y_FLIP;
|
||||
if (mOriginPos == gl::OriginPos::BottomLeft) {
|
||||
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
}
|
||||
|
||||
if (!mGLContext) {
|
||||
|
@ -752,8 +752,8 @@ ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
|
||||
// gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree.
|
||||
CSSToLayerScale paintScale = aMetrics.LayersPixelsPerCSSPixel();
|
||||
const CSSRect& metricsDisplayPort =
|
||||
(aDrawingCritical && !aMetrics.mCriticalDisplayPort.IsEmpty()) ?
|
||||
aMetrics.mCriticalDisplayPort : aMetrics.GetDisplayPort();
|
||||
(aDrawingCritical && !aMetrics.GetCriticalDisplayPort().IsEmpty()) ?
|
||||
aMetrics.GetCriticalDisplayPort() : aMetrics.GetDisplayPort();
|
||||
LayerRect displayPort = (metricsDisplayPort + aMetrics.GetScrollOffset()) * paintScale;
|
||||
|
||||
ParentLayerPoint scrollOffset;
|
||||
|
@ -152,7 +152,7 @@ ClientTiledPaintedLayer::BeginPaint()
|
||||
// Compute the critical display port that applies to this layer in the
|
||||
// LayoutDevice space of this layer.
|
||||
ParentLayerRect criticalDisplayPort =
|
||||
(displayportMetrics.mCriticalDisplayPort * displayportMetrics.GetZoom())
|
||||
(displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
|
||||
+ displayportMetrics.mCompositionBounds.TopLeft();
|
||||
mPaintData.mCriticalDisplayPort = RoundedOut(
|
||||
ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort));
|
||||
@ -189,7 +189,7 @@ ClientTiledPaintedLayer::UseFastPath()
|
||||
|
||||
bool multipleTransactionsNeeded = gfxPlatform::GetPlatform()->UseProgressivePaint()
|
||||
|| gfxPrefs::UseLowPrecisionBuffer()
|
||||
|| !parentMetrics.mCriticalDisplayPort.IsEmpty();
|
||||
|| !parentMetrics.GetCriticalDisplayPort().IsEmpty();
|
||||
bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition();
|
||||
return !multipleTransactionsNeeded || isFixed || parentMetrics.GetDisplayPort().IsEmpty();
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag
|
||||
const SurfaceTextureImage::Data* data = typedImage->GetData();
|
||||
texture = new SurfaceTextureClient(GetForwarder(), mTextureFlags,
|
||||
data->mSurfTex, size,
|
||||
data->mInverted);
|
||||
data->mOriginPos);
|
||||
#endif
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Bad ImageFormat.");
|
||||
|
@ -257,9 +257,9 @@ SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetric
|
||||
// converted to app units and then back to CSS pixels before being put in the FrameMetrics.
|
||||
// This process can introduce some rounding error, so we inflate the rect by one app unit
|
||||
// to account for that.
|
||||
CSSRect painted = (aContentMetrics.mCriticalDisplayPort.IsEmpty()
|
||||
CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
|
||||
? aContentMetrics.GetDisplayPort()
|
||||
: aContentMetrics.mCriticalDisplayPort)
|
||||
: aContentMetrics.GetCriticalDisplayPort())
|
||||
+ aContentMetrics.GetScrollOffset();
|
||||
painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
|
||||
|
||||
|
@ -604,8 +604,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
|
||||
|
||||
const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
|
||||
CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
|
||||
CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ?
|
||||
metrics.GetDisplayPort() : metrics.mCriticalDisplayPort);
|
||||
CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
|
||||
metrics.GetDisplayPort() : metrics.GetCriticalDisplayPort());
|
||||
ScreenPoint offset(0, 0);
|
||||
// XXX this call to SyncFrameMetrics is not currently being used. It will be cleaned
|
||||
// up as part of bug 776030 or one of its dependencies.
|
||||
@ -859,9 +859,9 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
|
||||
// notifications, so that Java can take these into account in its response.
|
||||
// Calculate the absolute display port to send to Java
|
||||
LayerIntRect displayPort = RoundedToInt(
|
||||
(metrics.mCriticalDisplayPort.IsEmpty()
|
||||
(metrics.GetCriticalDisplayPort().IsEmpty()
|
||||
? metrics.GetDisplayPort()
|
||||
: metrics.mCriticalDisplayPort
|
||||
: metrics.GetCriticalDisplayPort()
|
||||
) * geckoZoom);
|
||||
displayPort += scrollOffsetLayerPixels;
|
||||
|
||||
|
@ -151,7 +151,7 @@ ImageHost::Composite(EffectChain& aEffectChain,
|
||||
} else {
|
||||
effect->mTextureCoords = Rect(0, 0, 1, 1);
|
||||
}
|
||||
if (mFrontBuffer->GetFlags() & TextureFlags::NEEDS_Y_FLIP) {
|
||||
if (mFrontBuffer->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
|
||||
effect->mTextureCoords.y = effect->mTextureCoords.YMost();
|
||||
effect->mTextureCoords.height = -effect->mTextureCoords.height;
|
||||
}
|
||||
@ -179,7 +179,7 @@ ImageHost::Composite(EffectChain& aEffectChain,
|
||||
rect = gfx::Rect(0, 0, textureSize.width, textureSize.height);
|
||||
}
|
||||
|
||||
if (mFrontBuffer->GetFlags() & TextureFlags::NEEDS_Y_FLIP) {
|
||||
if (mFrontBuffer->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
|
||||
effect->mTextureCoords.y = effect->mTextureCoords.YMost();
|
||||
effect->mTextureCoords.height = -effect->mTextureCoords.height;
|
||||
}
|
||||
|
@ -928,10 +928,10 @@ LayerManagerComposite::ComputeRenderIntegrity()
|
||||
|
||||
// Work out how much of the critical display-port covers the screen
|
||||
bool hasLowPrecision = false;
|
||||
if (!metrics.mCriticalDisplayPort.IsEmpty()) {
|
||||
if (!metrics.GetCriticalDisplayPort().IsEmpty()) {
|
||||
hasLowPrecision = true;
|
||||
highPrecisionMultiplier =
|
||||
GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect);
|
||||
GetDisplayportCoverage(metrics.GetCriticalDisplayPort(), transform, screenRect);
|
||||
}
|
||||
|
||||
// Work out how much of the display-port covers the screen
|
||||
|
@ -23,8 +23,8 @@ using namespace mozilla::gfx;
|
||||
CanvasLayerD3D10::CanvasLayerD3D10(LayerManagerD3D10 *aManager)
|
||||
: CanvasLayer(aManager, nullptr)
|
||||
, LayerD3D10(aManager)
|
||||
, mDataIsPremultiplied(false)
|
||||
, mNeedsYFlip(false)
|
||||
, mDataIsPremultiplied(true)
|
||||
, mOriginPos(gl::OriginPos::TopLeft)
|
||||
, mHasAlpha(true)
|
||||
{
|
||||
mImplData = static_cast<LayerD3D10*>(this);
|
||||
@ -43,7 +43,7 @@ CanvasLayerD3D10::Initialize(const Data& aData)
|
||||
mGLContext = aData.mGLContext;
|
||||
NS_ASSERTION(mGLContext->IsOffscreen(), "Canvas GLContext must be offscreen.");
|
||||
mDataIsPremultiplied = aData.mIsGLAlphaPremult;
|
||||
mNeedsYFlip = true;
|
||||
mOriginPos = gl::OriginPos::TopLeft;
|
||||
|
||||
GLScreenBuffer* screen = mGLContext->Screen();
|
||||
|
||||
@ -60,8 +60,6 @@ CanvasLayerD3D10::Initialize(const Data& aData)
|
||||
}
|
||||
} else if (aData.mDrawTarget) {
|
||||
mDrawTarget = aData.mDrawTarget;
|
||||
mNeedsYFlip = false;
|
||||
mDataIsPremultiplied = true;
|
||||
void *texture = mDrawTarget->GetNativeSurface(NativeSurfaceType::D3D10_TEXTURE);
|
||||
|
||||
if (texture) {
|
||||
@ -79,7 +77,7 @@ CanvasLayerD3D10::Initialize(const Data& aData)
|
||||
// bypassing Thebes
|
||||
mSurface = mDrawTarget->Snapshot();
|
||||
} else {
|
||||
NS_ERROR("CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
|
||||
MOZ_CRASH("CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
|
||||
}
|
||||
|
||||
mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
|
||||
@ -213,7 +211,9 @@ CanvasLayerD3D10::RenderLayer()
|
||||
(float)mBounds.height)
|
||||
);
|
||||
|
||||
if (mNeedsYFlip) {
|
||||
const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
|
||||
|
||||
if (needsYFlip) {
|
||||
effect()->GetVariableByName("vTextureCoords")->AsVector()->SetFloatVector(
|
||||
ShaderConstantRectD3D10(
|
||||
0,
|
||||
@ -226,7 +226,7 @@ CanvasLayerD3D10::RenderLayer()
|
||||
technique->GetPassByIndex(0)->Apply(0);
|
||||
device()->Draw(4, 0);
|
||||
|
||||
if (mNeedsYFlip) {
|
||||
if (needsYFlip) {
|
||||
effect()->GetVariableByName("vTextureCoords")->AsVector()->
|
||||
SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
#ifndef GFX_CANVASLAYERD3D10_H
|
||||
#define GFX_CANVASLAYERD3D10_H
|
||||
|
||||
#include "GLContextTypes.h"
|
||||
#include "LayerManagerD3D10.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -45,7 +45,7 @@ private:
|
||||
nsRefPtr<ID3D10ShaderResourceView> mSRView;
|
||||
|
||||
bool mDataIsPremultiplied;
|
||||
bool mNeedsYFlip;
|
||||
gl::OriginPos mOriginPos;
|
||||
bool mIsD2DTexture;
|
||||
bool mHasAlpha;
|
||||
|
||||
|
@ -22,8 +22,8 @@ namespace layers {
|
||||
CanvasLayerD3D9::CanvasLayerD3D9(LayerManagerD3D9 *aManager)
|
||||
: CanvasLayer(aManager, nullptr)
|
||||
, LayerD3D9(aManager)
|
||||
, mDataIsPremultiplied(false)
|
||||
, mNeedsYFlip(false)
|
||||
, mDataIsPremultiplied(true)
|
||||
, mOriginPos(gl::OriginPos::TopLeft)
|
||||
, mHasAlpha(true)
|
||||
{
|
||||
mImplData = static_cast<LayerD3D9*>(this);
|
||||
@ -44,13 +44,11 @@ CanvasLayerD3D9::Initialize(const Data& aData)
|
||||
|
||||
if (aData.mDrawTarget) {
|
||||
mDrawTarget = aData.mDrawTarget;
|
||||
mNeedsYFlip = false;
|
||||
mDataIsPremultiplied = true;
|
||||
} else if (aData.mGLContext) {
|
||||
mGLContext = aData.mGLContext;
|
||||
NS_ASSERTION(mGLContext->IsOffscreen(), "Canvas GLContext must be offscreen.");
|
||||
mDataIsPremultiplied = aData.mIsGLAlphaPremult;
|
||||
mNeedsYFlip = true;
|
||||
mOriginPos = gl::OriginPos::BottomLeft;
|
||||
} else {
|
||||
NS_ERROR("CanvasLayer created without mGLContext or mDrawTarget?");
|
||||
}
|
||||
@ -137,9 +135,10 @@ CanvasLayerD3D9::RenderLayer()
|
||||
* We flip the Y axis here, note we can only do this because we are in
|
||||
* CULL_NONE mode!
|
||||
*/
|
||||
|
||||
ShaderConstantRect quad(0, 0, mBounds.width, mBounds.height);
|
||||
if (mNeedsYFlip) {
|
||||
|
||||
const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
|
||||
if (needsYFlip) {
|
||||
quad.mHeight = (float)-mBounds.height;
|
||||
quad.mY = (float)mBounds.height;
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
#ifndef GFX_CANVASLAYERD3D9_H
|
||||
#define GFX_CANVASLAYERD3D9_H
|
||||
|
||||
#include "LayerManagerD3D9.h"
|
||||
#include "GLContextTypes.h"
|
||||
#include "LayerManagerD3D9.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
@ -42,7 +42,7 @@ protected:
|
||||
RefPtr<gfx::DrawTarget> mDrawTarget;
|
||||
|
||||
bool mDataIsPremultiplied;
|
||||
bool mNeedsYFlip;
|
||||
gl::OriginPos mOriginPos;
|
||||
bool mHasAlpha;
|
||||
|
||||
nsAutoArrayPtr<uint8_t> mCachedTempBlob;
|
||||
|
@ -348,7 +348,7 @@ GrallocTextureClientOGL::FromSharedSurface(gl::SharedSurface* abstractSurf,
|
||||
|
||||
RefPtr<TextureClient> ret = surf->GetTextureClient();
|
||||
|
||||
TextureFlags mask = TextureFlags::NEEDS_Y_FLIP |
|
||||
TextureFlags mask = TextureFlags::ORIGIN_BOTTOM_LEFT |
|
||||
TextureFlags::RB_SWAPPED |
|
||||
TextureFlags::NON_PREMULTIPLIED;
|
||||
TextureFlags required = flags & mask;
|
||||
|
@ -212,8 +212,8 @@ GrallocTextureHostOGL::GetRenderState()
|
||||
if (mIsOpaque) {
|
||||
flags |= LayerRenderStateFlags::OPAQUE;
|
||||
}
|
||||
if (mFlags & TextureFlags::NEEDS_Y_FLIP) {
|
||||
flags |= LayerRenderStateFlags::Y_FLIPPED;
|
||||
if (mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
|
||||
flags |= LayerRenderStateFlags::ORIGIN_BOTTOM_LEFT;
|
||||
}
|
||||
if (mFlags & TextureFlags::RB_SWAPPED) {
|
||||
flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
|
||||
|
@ -34,8 +34,8 @@ EGLImageTextureClient::EGLImageTextureClient(ISurfaceAllocator* aAllocator,
|
||||
|
||||
AddFlags(TextureFlags::DEALLOCATE_CLIENT);
|
||||
|
||||
if (aImage->GetData()->mInverted) {
|
||||
AddFlags(TextureFlags::NEEDS_Y_FLIP);
|
||||
if (aImage->GetData()->mOriginPos == gl::OriginPos::BottomLeft) {
|
||||
AddFlags(TextureFlags::ORIGIN_BOTTOM_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ SurfaceTextureClient::SurfaceTextureClient(ISurfaceAllocator* aAllocator,
|
||||
TextureFlags aFlags,
|
||||
AndroidSurfaceTexture* aSurfTex,
|
||||
gfx::IntSize aSize,
|
||||
bool aInverted)
|
||||
gl::OriginPos aOriginPos)
|
||||
: TextureClient(aAllocator, aFlags)
|
||||
, mSurfTex(aSurfTex)
|
||||
, mSize(aSize)
|
||||
@ -89,8 +89,8 @@ SurfaceTextureClient::SurfaceTextureClient(ISurfaceAllocator* aAllocator,
|
||||
// Our data is always owned externally.
|
||||
AddFlags(TextureFlags::DEALLOCATE_CLIENT);
|
||||
|
||||
if (aInverted) {
|
||||
AddFlags(TextureFlags::NEEDS_Y_FLIP);
|
||||
if (aOriginPos == gl::OriginPos::BottomLeft) {
|
||||
AddFlags(TextureFlags::ORIGIN_BOTTOM_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
TextureFlags aFlags,
|
||||
gl::AndroidSurfaceTexture* aSurfTex,
|
||||
gfx::IntSize aSize,
|
||||
bool aInverted);
|
||||
gl::OriginPos aOriginPos);
|
||||
|
||||
~SurfaceTextureClient();
|
||||
|
||||
|
@ -103,8 +103,8 @@ FlagsToGLFlags(TextureFlags aFlags)
|
||||
|
||||
if (aFlags & TextureFlags::USE_NEAREST_FILTER)
|
||||
result |= TextureImage::UseNearestFilter;
|
||||
if (aFlags & TextureFlags::NEEDS_Y_FLIP)
|
||||
result |= TextureImage::NeedsYFlip;
|
||||
if (aFlags & TextureFlags::ORIGIN_BOTTOM_LEFT)
|
||||
result |= TextureImage::OriginBottomLeft;
|
||||
if (aFlags & TextureFlags::DISALLOW_BIGIMAGE)
|
||||
result |= TextureImage::DisallowBigImage;
|
||||
|
||||
|
@ -225,7 +225,7 @@ TestFrameMetrics()
|
||||
|
||||
fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
|
||||
fm.mCompositionBounds = ParentLayerRect(0, 0, 10, 10);
|
||||
fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10);
|
||||
fm.SetCriticalDisplayPort(CSSRect(0, 0, 10, 10));
|
||||
fm.SetScrollableRect(CSSRect(0, 0, 100, 100));
|
||||
|
||||
return fm;
|
||||
|
@ -429,7 +429,7 @@ class GCRuntime
|
||||
void disallowIncrementalGC() { incrementalAllowed = false; }
|
||||
|
||||
bool isIncrementalGCEnabled() { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
|
||||
bool isIncrementalGCInProgress() { return state() != gc::NO_INCREMENTAL && !verifyPreData; }
|
||||
bool isIncrementalGCInProgress() { return state() != gc::NO_INCREMENTAL; }
|
||||
|
||||
bool isGenerationalGCEnabled() { return generationalDisabled == 0; }
|
||||
void disableGenerationalGC();
|
||||
|
@ -773,7 +773,7 @@ Statistics::endGC()
|
||||
runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gc.isIncrementalGCAllowed());
|
||||
runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
|
||||
runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
|
||||
|
||||
|
||||
double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
|
||||
runtime->addTelemetry(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
|
||||
|
||||
@ -792,7 +792,7 @@ Statistics::beginSlice(const ZoneGCStats &zoneStats, JSGCInvocationKind gckind,
|
||||
{
|
||||
this->zoneStats = zoneStats;
|
||||
|
||||
bool first = runtime->gc.state() == gc::NO_INCREMENTAL;
|
||||
bool first = !runtime->gc.isIncrementalGCInProgress();
|
||||
if (first)
|
||||
beginGC(gckind);
|
||||
|
||||
@ -820,7 +820,7 @@ Statistics::endSlice()
|
||||
runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
|
||||
runtime->addTelemetry(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
|
||||
|
||||
bool last = runtime->gc.state() == gc::NO_INCREMENTAL;
|
||||
bool last = !runtime->gc.isIncrementalGCInProgress();
|
||||
if (last)
|
||||
endGC();
|
||||
|
||||
|
@ -170,7 +170,7 @@ NextNode(VerifyNode *node)
|
||||
void
|
||||
gc::GCRuntime::startVerifyPreBarriers()
|
||||
{
|
||||
if (verifyPreData || incrementalState != NO_INCREMENTAL)
|
||||
if (verifyPreData || isIncrementalGCInProgress())
|
||||
return;
|
||||
|
||||
/*
|
||||
@ -403,11 +403,8 @@ struct VerifyPostTracer : JSTracer
|
||||
void
|
||||
gc::GCRuntime::startVerifyPostBarriers()
|
||||
{
|
||||
if (verifyPostData ||
|
||||
incrementalState != NO_INCREMENTAL)
|
||||
{
|
||||
if (verifyPostData || isIncrementalGCInProgress())
|
||||
return;
|
||||
}
|
||||
|
||||
evictNursery();
|
||||
|
||||
|
@ -107,13 +107,6 @@ Zone::onTooMuchMalloc()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Zone::isCloseToAllocTrigger(bool highFrequencyGC) const
|
||||
{
|
||||
double factor = highFrequencyGC ? 0.85 : 0.9;
|
||||
return usage.gcBytes() >= factor * threshold.gcTriggerBytes();
|
||||
}
|
||||
|
||||
void
|
||||
Zone::beginSweepTypes(FreeOp *fop, bool releaseTypes)
|
||||
{
|
||||
|
@ -62,6 +62,7 @@ class ZoneHeapThreshold
|
||||
|
||||
double gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; }
|
||||
size_t gcTriggerBytes() const { return gcTriggerBytes_; }
|
||||
bool isCloseToAllocTrigger(const js::gc::HeapUsage& usage, bool highFrequencyGC) const;
|
||||
|
||||
void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind,
|
||||
const GCSchedulingTunables &tunables, const GCSchedulingState &state);
|
||||
@ -153,8 +154,6 @@ struct Zone : public JS::shadow::Zone,
|
||||
bool isTooMuchMalloc() const { return gcMallocBytes <= 0; }
|
||||
void onTooMuchMalloc();
|
||||
|
||||
bool isCloseToAllocTrigger(bool highFrequencyGC) const;
|
||||
|
||||
void *onOutOfMemory(void *p, size_t nbytes) {
|
||||
return runtimeFromMainThread()->onOutOfMemory(p, nbytes);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ BEGIN_TEST(testGCFinalizeCallback)
|
||||
FinalizeCalls = 0;
|
||||
JS::PrepareForFullGC(rt);
|
||||
JS::IncrementalGC(rt, JS::gcreason::API, 1000000);
|
||||
CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL);
|
||||
CHECK(!rt->gc.isIncrementalGCInProgress());
|
||||
CHECK(rt->gc.isFullGc());
|
||||
CHECK(checkMultipleGroups());
|
||||
CHECK(checkFinalizeStatus());
|
||||
@ -62,7 +62,7 @@ BEGIN_TEST(testGCFinalizeCallback)
|
||||
FinalizeCalls = 0;
|
||||
JS::PrepareZoneForGC(global1->zone());
|
||||
JS::IncrementalGC(rt, JS::gcreason::API, 1000000);
|
||||
CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL);
|
||||
CHECK(!rt->gc.isIncrementalGCInProgress());
|
||||
CHECK(!rt->gc.isFullGc());
|
||||
CHECK(checkSingleGroup());
|
||||
CHECK(checkFinalizeStatus());
|
||||
@ -74,7 +74,7 @@ BEGIN_TEST(testGCFinalizeCallback)
|
||||
JS::PrepareZoneForGC(global2->zone());
|
||||
JS::PrepareZoneForGC(global3->zone());
|
||||
JS::IncrementalGC(rt, JS::gcreason::API, 1000000);
|
||||
CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL);
|
||||
CHECK(!rt->gc.isIncrementalGCInProgress());
|
||||
CHECK(!rt->gc.isFullGc());
|
||||
CHECK(checkMultipleGroups());
|
||||
CHECK(checkFinalizeStatus());
|
||||
@ -95,7 +95,7 @@ BEGIN_TEST(testGCFinalizeCallback)
|
||||
JS::RootedObject global4(cx, createTestGlobal());
|
||||
budget = js::SliceBudget(js::WorkBudget(1));
|
||||
rt->gc.gcDebugSlice(budget);
|
||||
CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL);
|
||||
CHECK(!rt->gc.isIncrementalGCInProgress());
|
||||
CHECK(!rt->gc.isFullGc());
|
||||
CHECK(checkMultipleGroups());
|
||||
CHECK(checkFinalizeStatus());
|
||||
|
@ -1114,7 +1114,7 @@ JS::IsIncrementalGCEnabled(JSRuntime *rt)
|
||||
JS_FRIEND_API(bool)
|
||||
JS::IsIncrementalGCInProgress(JSRuntime *rt)
|
||||
{
|
||||
return rt->gc.isIncrementalGCInProgress();
|
||||
return rt->gc.isIncrementalGCInProgress() && !rt->gc.isVerifyPreBarriersEnabled();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
|
@ -1627,10 +1627,10 @@ GCRuntime::addRoot(T *rp, const char *name, JSGCRootType rootType)
|
||||
* or ModifyBusyCount in workers). We need a read barrier to cover these
|
||||
* cases.
|
||||
*/
|
||||
if (rt->gc.incrementalState != NO_INCREMENTAL)
|
||||
if (isIncrementalGCInProgress())
|
||||
BarrierOwner<T>::result::writeBarrierPre(*rp);
|
||||
|
||||
return rt->gc.rootsHash.put((void *)rp, RootInfo(name, rootType));
|
||||
return rootsHash.put((void *)rp, RootInfo(name, rootType));
|
||||
}
|
||||
|
||||
void
|
||||
@ -1748,6 +1748,13 @@ GCRuntime::onTooMuchMalloc()
|
||||
mallocGCTriggered = triggerGC(JS::gcreason::TOO_MUCH_MALLOC);
|
||||
}
|
||||
|
||||
bool
|
||||
ZoneHeapThreshold::isCloseToAllocTrigger(const js::gc::HeapUsage& usage, bool highFrequencyGC) const
|
||||
{
|
||||
double factor = highFrequencyGC ? 0.85 : 0.9;
|
||||
return usage.gcBytes() >= factor * gcTriggerBytes();
|
||||
}
|
||||
|
||||
/* static */ double
|
||||
ZoneHeapThreshold::computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes,
|
||||
const GCSchedulingTunables &tunables,
|
||||
@ -3009,8 +3016,9 @@ template <AllowGC allowGC>
|
||||
/* static */ void *
|
||||
GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
|
||||
{
|
||||
MOZ_ASSERT(!cx->runtime()->isHeapBusy(), "allocating while under GC");
|
||||
MOZ_ASSERT_IF(allowGC, !cx->runtime()->currentThreadHasExclusiveAccess());
|
||||
JSRuntime *rt = cx->runtime();
|
||||
MOZ_ASSERT(!rt->isHeapBusy(), "allocating while under GC");
|
||||
MOZ_ASSERT_IF(allowGC, !rt->currentThreadHasExclusiveAccess());
|
||||
|
||||
Allocator *allocator = cx->allocator();
|
||||
Zone *zone = allocator->zone_;
|
||||
@ -3018,8 +3026,7 @@ GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
|
||||
// If we have grown past our GC heap threshold while in the middle of an
|
||||
// incremental GC, we're growing faster than we're GCing, so stop the world
|
||||
// and do a full, non-incremental GC right now, if possible.
|
||||
const bool mustCollectNow = allowGC &&
|
||||
cx->runtime()->gc.incrementalState != NO_INCREMENTAL &&
|
||||
const bool mustCollectNow = allowGC && rt->gc.isIncrementalGCInProgress() &&
|
||||
zone->usage.gcBytes() > zone->threshold.gcTriggerBytes();
|
||||
|
||||
bool outOfMemory = false; // Set true if we fail to allocate.
|
||||
@ -3047,7 +3054,7 @@ GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
|
||||
// finalization task may be running (freeing more memory); wait for it
|
||||
// to finish, then try to allocate again in case it freed up the memory
|
||||
// we need.
|
||||
cx->runtime()->gc.waitBackgroundSweepEnd();
|
||||
rt->gc.waitBackgroundSweepEnd();
|
||||
|
||||
thing = allocator->arenas.allocateFromArena(zone, thingKind, maybeStartBGAlloc);
|
||||
if (MOZ_LIKELY(thing))
|
||||
@ -3309,8 +3316,8 @@ GCRuntime::maybeGC(Zone *zone)
|
||||
return true;
|
||||
|
||||
if (zone->usage.gcBytes() > 1024 * 1024 &&
|
||||
zone->isCloseToAllocTrigger(schedulingState.inHighFrequencyGCMode()) &&
|
||||
incrementalState == NO_INCREMENTAL &&
|
||||
zone->threshold.isCloseToAllocTrigger(zone->usage, schedulingState.inHighFrequencyGCMode()) &&
|
||||
!isIncrementalGCInProgress() &&
|
||||
!isBackgroundSweeping())
|
||||
{
|
||||
PrepareZoneForGC(zone);
|
||||
@ -3666,7 +3673,7 @@ GCHelperState::waitBackgroundSweepEnd()
|
||||
AutoLockGC lock(rt);
|
||||
while (state() == SWEEPING)
|
||||
waitForBackgroundThread();
|
||||
if (rt->gc.incrementalState == NO_INCREMENTAL)
|
||||
if (!rt->gc.isIncrementalGCInProgress())
|
||||
rt->gc.assertBackgroundSweepingFinished();
|
||||
}
|
||||
|
||||
@ -5880,7 +5887,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason rea
|
||||
}
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT_IF(incrementalState != NO_INCREMENTAL, isIncremental);
|
||||
MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
|
||||
isIncremental = !budget.isUnlimited();
|
||||
|
||||
if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
|
||||
@ -6038,11 +6045,8 @@ GCRuntime::budgetIncrementalGC(SliceBudget &budget)
|
||||
stats.nonincremental("allocation trigger");
|
||||
}
|
||||
|
||||
if (incrementalState != NO_INCREMENTAL &&
|
||||
zone->isGCScheduled() != zone->wasGCStarted())
|
||||
{
|
||||
if (isIncrementalGCInProgress() && zone->isGCScheduled() != zone->wasGCStarted())
|
||||
reset = true;
|
||||
}
|
||||
|
||||
if (zone->isTooMuchMalloc()) {
|
||||
budget.makeUnlimited();
|
||||
@ -6101,7 +6105,7 @@ GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
interFrameGC = true;
|
||||
|
||||
number++;
|
||||
if (incrementalState == NO_INCREMENTAL)
|
||||
if (!isIncrementalGCInProgress())
|
||||
majorGCNumber++;
|
||||
|
||||
// It's ok if threads other than the main thread have suppressGC set, as
|
||||
@ -6116,7 +6120,7 @@ GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
|
||||
// As we are about to clear the mark bits, wait for background
|
||||
// finalization to finish. We only need to wait on the first slice.
|
||||
if (incrementalState == NO_INCREMENTAL)
|
||||
if (!isIncrementalGCInProgress())
|
||||
waitBackgroundSweepEnd();
|
||||
|
||||
// We must also wait for background allocation to finish so we can
|
||||
@ -6143,13 +6147,13 @@ GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
}
|
||||
|
||||
/* The GC was reset, so we need a do-over. */
|
||||
if (prevState != NO_INCREMENTAL && incrementalState == NO_INCREMENTAL)
|
||||
if (prevState != NO_INCREMENTAL && !isIncrementalGCInProgress())
|
||||
return true;
|
||||
|
||||
TraceMajorGCStart();
|
||||
|
||||
/* Set the invocation kind in the first slice. */
|
||||
if (incrementalState == NO_INCREMENTAL)
|
||||
if (!isIncrementalGCInProgress())
|
||||
invocationKind = gckind;
|
||||
|
||||
incrementalCollectSlice(budget, reason);
|
||||
@ -6215,11 +6219,11 @@ GCRuntime::scanZonesBeforeGC()
|
||||
zone->scheduleGC();
|
||||
|
||||
/* This is a heuristic to avoid resets. */
|
||||
if (incrementalState != NO_INCREMENTAL && zone->needsIncrementalBarrier())
|
||||
if (isIncrementalGCInProgress() && zone->needsIncrementalBarrier())
|
||||
zone->scheduleGC();
|
||||
|
||||
/* This is a heuristic to reduce the total number of collections. */
|
||||
if (zone->isCloseToAllocTrigger(schedulingState.inHighFrequencyGCMode()))
|
||||
if (zone->threshold.isCloseToAllocTrigger(zone->usage, schedulingState.inHighFrequencyGCMode()))
|
||||
zone->scheduleGC();
|
||||
|
||||
zoneStats.zoneCount++;
|
||||
@ -6274,7 +6278,7 @@ GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
* Let the API user decide to defer a GC if it wants to (unless this
|
||||
* is the last context). Invoke the callback regardless.
|
||||
*/
|
||||
if (incrementalState == NO_INCREMENTAL) {
|
||||
if (!isIncrementalGCInProgress()) {
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_BEGIN);
|
||||
if (gcCallback.op)
|
||||
gcCallback.op(rt, JSGC_BEGIN, gcCallback.data);
|
||||
@ -6283,7 +6287,7 @@ GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
poked = false;
|
||||
bool wasReset = gcCycle(incremental, budget, gckind, reason);
|
||||
|
||||
if (incrementalState == NO_INCREMENTAL) {
|
||||
if (!isIncrementalGCInProgress()) {
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_GC_END);
|
||||
if (gcCallback.op)
|
||||
gcCallback.op(rt, JSGC_END, gcCallback.data);
|
||||
@ -6299,7 +6303,7 @@ GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
* beginMarkPhase.
|
||||
*/
|
||||
bool repeatForDeadZone = false;
|
||||
if (incremental && incrementalState == NO_INCREMENTAL) {
|
||||
if (incremental && !isIncrementalGCInProgress()) {
|
||||
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
|
||||
if (c->scheduledForDestruction) {
|
||||
incremental = false;
|
||||
@ -6319,7 +6323,7 @@ GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gck
|
||||
repeat = (poked && cleanUpEverything) || wasReset || repeatForDeadZone;
|
||||
} while (repeat);
|
||||
|
||||
if (incrementalState == NO_INCREMENTAL)
|
||||
if (!isIncrementalGCInProgress())
|
||||
EnqueuePendingParseTasksAfterGC(rt);
|
||||
}
|
||||
|
||||
@ -6677,7 +6681,7 @@ GCRuntime::runDebugGC()
|
||||
* ensure that we get multiple slices, and collection runs to
|
||||
* completion.
|
||||
*/
|
||||
if (initialState == NO_INCREMENTAL)
|
||||
if (!isIncrementalGCInProgress())
|
||||
incrementalLimit = zealFrequency / 2;
|
||||
else
|
||||
incrementalLimit *= 2;
|
||||
|
@ -2902,10 +2902,10 @@ ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf,
|
||||
return eRestyleResult_Continue;
|
||||
}
|
||||
|
||||
if (oldContext->IsDirectlyInsideRuby() !=
|
||||
aNewContext->IsDirectlyInsideRuby()) {
|
||||
LOG_RESTYLE_CONTINUE("NS_STYLE_IS_DIRECTLY_INSIDE_RUBY differes between old"
|
||||
" and new style contexts");
|
||||
if (oldContext->IsInlineDescendantOfRuby() !=
|
||||
aNewContext->IsInlineDescendantOfRuby()) {
|
||||
LOG_RESTYLE_CONTINUE("NS_STYLE_IS_INLINE_DESCENDANT_OF_RUBY differes"
|
||||
"between old and new style contexts");
|
||||
return eRestyleResult_Continue;
|
||||
}
|
||||
|
||||
|
15
layout/base/crashtests/1116104.html
Normal file
15
layout/base/crashtests/1116104.html
Normal file
@ -0,0 +1,15 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>colgroup::after { content:"after"; }</style>
|
||||
|
||||
<table>
|
||||
<colgroup><col style="display: inline;">t</col></colgroup>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -454,4 +454,5 @@ load 973390-1.html
|
||||
load 1001237.html
|
||||
load 1043163-1.html
|
||||
load 1061028.html
|
||||
load 1116104.html
|
||||
load 1107508-1.html
|
||||
|
@ -6070,8 +6070,11 @@ AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager,
|
||||
if (child && child->IsPseudoFrame(aContainer) &&
|
||||
!child->IsGeneratedContentFrame()) {
|
||||
// Drill down into non-generated pseudo frames of aContainer.
|
||||
parent = nsLayoutUtils::LastContinuationWithChild(do_QueryFrame(child));
|
||||
continue;
|
||||
nsContainerFrame* childAsContainer = do_QueryFrame(child);
|
||||
if (childAsContainer) {
|
||||
parent = nsLayoutUtils::LastContinuationWithChild(childAsContainer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (; child; child = child->GetPrevSibling()) {
|
||||
|
@ -721,7 +721,7 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
|
||||
metrics.GetDisplayPort());
|
||||
}
|
||||
if (nsLayoutUtils::GetCriticalDisplayPort(content, &dp)) {
|
||||
metrics.mCriticalDisplayPort = CSSRect::FromAppUnits(dp);
|
||||
metrics.SetCriticalDisplayPort(CSSRect::FromAppUnits(dp));
|
||||
}
|
||||
DisplayPortMarginsPropertyData* marginsData =
|
||||
static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
|
||||
|
@ -97,7 +97,7 @@ BRFrame::Reflow(nsPresContext* aPresContext,
|
||||
// Only when the BR is operating in a line-layout situation will it
|
||||
// behave like a BR. BR is suppressed when it is inside ruby frames.
|
||||
nsLineLayout* ll = aReflowState.mLineLayout;
|
||||
if (ll && !StyleContext()->IsDirectlyInsideRuby()) {
|
||||
if (ll && !StyleContext()->IsInlineDescendantOfRuby()) {
|
||||
// Note that the compatibility mode check excludes AlmostStandards
|
||||
// mode, since this is the inline box model. See bug 161691.
|
||||
if ( ll->LineIsEmpty() ||
|
||||
|
@ -442,7 +442,7 @@ nsLineLayout::BeginSpan(nsIFrame* aFrame,
|
||||
|
||||
nsIFrame* frame = aSpanReflowState->frame;
|
||||
psd->mNoWrap = !frame->StyleText()->WhiteSpaceCanWrap(frame) ||
|
||||
frame->StyleContext()->IsDirectlyInsideRuby();
|
||||
frame->StyleContext()->IsInlineDescendantOfRuby();
|
||||
psd->mWritingMode = aSpanReflowState->GetWritingMode();
|
||||
|
||||
// Switch to new span
|
||||
@ -2854,13 +2854,13 @@ nsLineLayout::TextAlignLine(nsLineBox* aLine,
|
||||
ComputeFrameJustification(psd, computeState);
|
||||
if (mHasRuby && computeState.mFirstParticipant) {
|
||||
PerFrameData* firstFrame = computeState.mFirstParticipant;
|
||||
if (firstFrame->mFrame->StyleContext()->IsDirectlyInsideRuby()) {
|
||||
if (firstFrame->mFrame->StyleContext()->IsInlineDescendantOfRuby()) {
|
||||
MOZ_ASSERT(!firstFrame->mJustificationAssignment.mGapsAtStart);
|
||||
firstFrame->mJustificationAssignment.mGapsAtStart = 1;
|
||||
additionalGaps++;
|
||||
}
|
||||
PerFrameData* lastFrame = computeState.mLastParticipant;
|
||||
if (lastFrame->mFrame->StyleContext()->IsDirectlyInsideRuby()) {
|
||||
if (lastFrame->mFrame->StyleContext()->IsInlineDescendantOfRuby()) {
|
||||
MOZ_ASSERT(!lastFrame->mJustificationAssignment.mGapsAtEnd);
|
||||
lastFrame->mJustificationAssignment.mGapsAtEnd = 1;
|
||||
additionalGaps++;
|
||||
|
@ -344,7 +344,7 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
startEdge, endEdge, &mBaseline);
|
||||
|
||||
nsIFrame* parent = GetParent();
|
||||
bool inNestedRuby = parent->StyleContext()->IsDirectlyInsideRuby();
|
||||
bool inNestedRuby = parent->StyleContext()->IsInlineDescendantOfRuby();
|
||||
// Allow line break between ruby bases when white-space allows,
|
||||
// we are not inside a nested ruby, and there is no span.
|
||||
bool allowLineBreak = !inNestedRuby && StyleText()->WhiteSpaceCanWrap(this);
|
||||
|
@ -1896,7 +1896,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||
textFlags |= GetSpacingFlags(WordSpacing(f));
|
||||
nsTextFrameUtils::CompressionMode compression =
|
||||
CSSWhitespaceToCompressionMode[textStyle->mWhiteSpace];
|
||||
if ((enabledJustification || f->StyleContext()->IsDirectlyInsideRuby()) &&
|
||||
if ((enabledJustification || f->StyleContext()->IsInlineDescendantOfRuby()) &&
|
||||
!textStyle->WhiteSpaceIsSignificant() && !isSVG) {
|
||||
textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
|
||||
}
|
||||
@ -2761,7 +2761,7 @@ static int32_t FindChar(const nsTextFragment* frag,
|
||||
|
||||
static bool IsChineseOrJapanese(nsIFrame* aFrame)
|
||||
{
|
||||
if (aFrame->StyleContext()->IsDirectlyInsideRuby()) {
|
||||
if (aFrame->StyleContext()->IsInlineDescendantOfRuby()) {
|
||||
// Always treat ruby as CJ language so that those characters can
|
||||
// be expanded properly even when surrounded by other language.
|
||||
return true;
|
||||
@ -8241,7 +8241,7 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
gfxBreakPriority breakPriority;
|
||||
aLineLayout.GetLastOptionalBreakPosition(&unusedOffset, &breakPriority);
|
||||
gfxTextRun::SuppressBreak suppressBreak = gfxTextRun::eNoSuppressBreak;
|
||||
if (StyleContext()->IsDirectlyInsideRuby()) {
|
||||
if (StyleContext()->IsInlineDescendantOfRuby()) {
|
||||
suppressBreak = gfxTextRun::eSuppressAllBreaks;
|
||||
} else if (!aLineLayout.LineIsBreakable()) {
|
||||
suppressBreak = gfxTextRun::eSuppressInitialBreak;
|
||||
@ -8510,7 +8510,7 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
if (!textStyle->WhiteSpaceIsSignificant() &&
|
||||
(lineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
|
||||
lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
|
||||
StyleContext()->IsDirectlyInsideRuby()) &&
|
||||
StyleContext()->IsInlineDescendantOfRuby()) &&
|
||||
!lineContainer->IsSVGText()) {
|
||||
AddStateBits(TEXT_JUSTIFICATION_ENABLED);
|
||||
provider.ComputeJustification(offset, charsFit);
|
||||
|
@ -563,9 +563,9 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup)
|
||||
// The display change should only occur for "in-flow" children
|
||||
if (!disp->IsOutOfFlowStyle() &&
|
||||
((containerDisp->mDisplay == NS_STYLE_DISPLAY_INLINE &&
|
||||
containerContext->IsDirectlyInsideRuby()) ||
|
||||
containerContext->IsInlineDescendantOfRuby()) ||
|
||||
containerDisp->IsRubyDisplayType())) {
|
||||
mBits |= NS_STYLE_IS_DIRECTLY_INSIDE_RUBY;
|
||||
mBits |= NS_STYLE_IS_INLINE_DESCENDANT_OF_RUBY;
|
||||
uint8_t displayVal = disp->mDisplay;
|
||||
nsRuleNode::EnsureInlineDisplay(displayVal);
|
||||
if (displayVal != disp->mDisplay) {
|
||||
|
@ -133,8 +133,8 @@ public:
|
||||
// Whether this style context or any of its inline-level ancestors
|
||||
// is directly contained by a ruby box? It is used to inlinize
|
||||
// block-level descendants and suppress line breaks inside ruby.
|
||||
bool IsDirectlyInsideRuby() const
|
||||
{ return !!(mBits & NS_STYLE_IS_DIRECTLY_INSIDE_RUBY); }
|
||||
bool IsInlineDescendantOfRuby() const
|
||||
{ return !!(mBits & NS_STYLE_IS_INLINE_DESCENDANT_OF_RUBY); }
|
||||
|
||||
// Does this style context represent the style for a pseudo-element or
|
||||
// inherit data from such a style context? Whether this returns true
|
||||
|
@ -56,8 +56,8 @@ class imgIContainer;
|
||||
// See nsStyleContext::AssertStructsNotUsedElsewhere
|
||||
// (This bit is currently only used in #ifdef DEBUG code.)
|
||||
#define NS_STYLE_IS_GOING_AWAY 0x040000000
|
||||
// See nsStyleContext::IsDirectlyInsideRuby
|
||||
#define NS_STYLE_IS_DIRECTLY_INSIDE_RUBY 0x080000000
|
||||
// See nsStyleContext::IsInlineDescendantOfRuby
|
||||
#define NS_STYLE_IS_INLINE_DESCENDANT_OF_RUBY 0x080000000
|
||||
// See nsStyleContext::GetPseudoEnum
|
||||
#define NS_STYLE_CONTEXT_TYPE_SHIFT 32
|
||||
|
||||
|
@ -12,16 +12,19 @@ import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.IllegalArgumentException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class DownloadsIntegration implements NativeEventListener
|
||||
{
|
||||
@ -88,6 +91,23 @@ public class DownloadsIntegration implements NativeEventListener
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean useSystemDownloadManager() {
|
||||
if (!AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int state = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
|
||||
try {
|
||||
state = GeckoAppShell.getContext().getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Download Manager package does not exist
|
||||
return false;
|
||||
}
|
||||
|
||||
return (PackageManager.COMPONENT_ENABLED_STATE_ENABLED == state ||
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT == state);
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void scanMedia(final String aFile, String aMimeType) {
|
||||
String mimeType = aMimeType;
|
||||
@ -115,7 +135,7 @@ public class DownloadsIntegration implements NativeEventListener
|
||||
}
|
||||
}
|
||||
|
||||
if (AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
|
||||
if (useSystemDownloadManager()) {
|
||||
final File f = new File(aFile);
|
||||
final DownloadManager dm = (DownloadManager) GeckoAppShell.getContext().getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
dm.addCompletedDownload(f.getName(),
|
||||
@ -133,7 +153,7 @@ public class DownloadsIntegration implements NativeEventListener
|
||||
}
|
||||
|
||||
public static void removeDownload(final Download download) {
|
||||
if (!AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
|
||||
if (!useSystemDownloadManager()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1586,9 +1586,9 @@ public abstract class GeckoApp
|
||||
|
||||
mPromptService = new PromptService(this);
|
||||
|
||||
mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.start_handle),
|
||||
(TextSelectionHandle) findViewById(R.id.middle_handle),
|
||||
(TextSelectionHandle) findViewById(R.id.end_handle),
|
||||
mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.anchor_handle),
|
||||
(TextSelectionHandle) findViewById(R.id.caret_handle),
|
||||
(TextSelectionHandle) findViewById(R.id.focus_handle),
|
||||
EventDispatcher.getInstance(),
|
||||
this);
|
||||
|
||||
|
@ -36,10 +36,10 @@ import android.view.View;
|
||||
class TextSelection extends Layer implements GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoTextSelection";
|
||||
|
||||
private final TextSelectionHandle mStartHandle;
|
||||
private final TextSelectionHandle mMiddleHandle;
|
||||
private final TextSelectionHandle mEndHandle;
|
||||
private final EventDispatcher mEventDispatcher;
|
||||
private final TextSelectionHandle anchorHandle;
|
||||
private final TextSelectionHandle caretHandle;
|
||||
private final TextSelectionHandle focusHandle;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
|
||||
private final DrawListener mDrawListener;
|
||||
private boolean mDraggingHandles;
|
||||
@ -68,15 +68,15 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
};
|
||||
private ActionModeTimerTask mActionModeTimerTask;
|
||||
|
||||
TextSelection(TextSelectionHandle startHandle,
|
||||
TextSelectionHandle middleHandle,
|
||||
TextSelectionHandle endHandle,
|
||||
TextSelection(TextSelectionHandle anchorHandle,
|
||||
TextSelectionHandle caretHandle,
|
||||
TextSelectionHandle focusHandle,
|
||||
EventDispatcher eventDispatcher,
|
||||
GeckoApp activity) {
|
||||
mStartHandle = startHandle;
|
||||
mMiddleHandle = middleHandle;
|
||||
mEndHandle = endHandle;
|
||||
mEventDispatcher = eventDispatcher;
|
||||
this.anchorHandle = anchorHandle;
|
||||
this.caretHandle = caretHandle;
|
||||
this.focusHandle = focusHandle;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
|
||||
mDrawListener = new DrawListener() {
|
||||
@Override
|
||||
@ -88,7 +88,7 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
};
|
||||
|
||||
// Only register listeners if we have valid start/middle/end handles
|
||||
if (mStartHandle == null || mMiddleHandle == null || mEndHandle == null) {
|
||||
if (anchorHandle == null || caretHandle == null || focusHandle == null) {
|
||||
Log.e(LOGTAG, "Failed to initialize text selection because at least one handle is null");
|
||||
} else {
|
||||
EventDispatcher.getInstance().registerGeckoThreadListener(this,
|
||||
@ -110,12 +110,16 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
}
|
||||
|
||||
private TextSelectionHandle getHandle(String name) {
|
||||
if (name.equals("START")) {
|
||||
return mStartHandle;
|
||||
} else if (name.equals("MIDDLE")) {
|
||||
return mMiddleHandle;
|
||||
} else {
|
||||
return mEndHandle;
|
||||
switch (TextSelectionHandle.HandleType.valueOf(name)) {
|
||||
case ANCHOR:
|
||||
return anchorHandle;
|
||||
case CARET:
|
||||
return caretHandle;
|
||||
case FOCUS:
|
||||
return focusHandle;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("TextSelectionHandle is invalid type.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,9 +169,9 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
mActionModeTimerTask = new ActionModeTimerTask();
|
||||
mActionModeTimer.schedule(mActionModeTimerTask, 250);
|
||||
|
||||
mStartHandle.setVisibility(View.GONE);
|
||||
mMiddleHandle.setVisibility(View.GONE);
|
||||
mEndHandle.setVisibility(View.GONE);
|
||||
anchorHandle.setVisibility(View.GONE);
|
||||
caretHandle.setVisibility(View.GONE);
|
||||
focusHandle.setVisibility(View.GONE);
|
||||
} else if (event.equals("TextSelection:PositionHandles")) {
|
||||
final boolean rtl = message.getBoolean("rtl");
|
||||
final JSONArray positions = message.getJSONArray("positions");
|
||||
@ -200,7 +204,7 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
return;
|
||||
}
|
||||
|
||||
final Context context = mStartHandle.getContext();
|
||||
final Context context = anchorHandle.getContext();
|
||||
if (context instanceof ActionModeCompat.Presenter) {
|
||||
final ActionModeCompat.Presenter presenter = (ActionModeCompat.Presenter) context;
|
||||
mCallback = new TextSelectionActionModeCallback(items);
|
||||
@ -209,7 +213,7 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
}
|
||||
|
||||
private void endActionMode() {
|
||||
Context context = mStartHandle.getContext();
|
||||
Context context = anchorHandle.getContext();
|
||||
if (context instanceof ActionModeCompat.Presenter) {
|
||||
final ActionModeCompat.Presenter presenter = (ActionModeCompat.Presenter) context;
|
||||
presenter.endActionModeCompat();
|
||||
@ -238,9 +242,9 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mStartHandle.repositionWithViewport(viewLeft, viewTop, viewZoom);
|
||||
mMiddleHandle.repositionWithViewport(viewLeft, viewTop, viewZoom);
|
||||
mEndHandle.repositionWithViewport(viewLeft, viewTop, viewZoom);
|
||||
anchorHandle.repositionWithViewport(viewLeft, viewTop, viewZoom);
|
||||
caretHandle.repositionWithViewport(viewLeft, viewTop, viewZoom);
|
||||
focusHandle.repositionWithViewport(viewLeft, viewTop, viewZoom);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -276,7 +280,7 @@ class TextSelection extends Layer implements GeckoEventListener {
|
||||
final int actionEnum = obj.optBoolean("showAsAction") ? GeckoMenuItem.SHOW_AS_ACTION_ALWAYS : GeckoMenuItem.SHOW_AS_ACTION_NEVER;
|
||||
menuitem.setShowAsAction(actionEnum, R.attr.menuItemActionModeStyle);
|
||||
|
||||
BitmapUtils.getDrawable(mStartHandle.getContext(), obj.optString("icon"), new BitmapLoader() {
|
||||
BitmapUtils.getDrawable(anchorHandle.getContext(), obj.optString("icon"), new BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
if (d != null) {
|
||||
|
@ -19,10 +19,30 @@ import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
/**
|
||||
* Text selection handles enable a user to change position of selected text in
|
||||
* Gecko's DOM structure.
|
||||
*
|
||||
* A text "Selection" or nsISelection object, has start and end positions,
|
||||
* referred to as Anchor and Focus objects.
|
||||
*
|
||||
* If the Anchor and Focus objects are at the same point, it represents a text
|
||||
* selection Caret, commonly diplayed as a blinking, vertical |.
|
||||
*
|
||||
* Anchor and Focus objects each represent a DOM node, and character offset
|
||||
* from the start of the node. The Anchor always refers to the start of the
|
||||
* Selection, and the Focus refers to its end.
|
||||
*
|
||||
* In LTR languages such as English, the Anchor is to the left of the Focus.
|
||||
* In RTL languages such as Hebrew, the Anchor is to the right of the Focus.
|
||||
*
|
||||
* For multi-line Selections, in both LTR and RTL languages, the Anchor starts
|
||||
* above the Focus.
|
||||
*/
|
||||
class TextSelectionHandle extends ImageView implements View.OnTouchListener {
|
||||
private static final String LOGTAG = "GeckoTextSelectionHandle";
|
||||
|
||||
private enum HandleType { START, MIDDLE, END };
|
||||
public enum HandleType { ANCHOR, CARET, FOCUS };
|
||||
|
||||
private final HandleType mHandleType;
|
||||
private final int mWidth;
|
||||
@ -51,11 +71,11 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener {
|
||||
int handleType = a.getInt(R.styleable.TextSelectionHandle_handleType, 0x01);
|
||||
|
||||
if (handleType == 0x01)
|
||||
mHandleType = HandleType.START;
|
||||
mHandleType = HandleType.ANCHOR;
|
||||
else if (handleType == 0x02)
|
||||
mHandleType = HandleType.MIDDLE;
|
||||
mHandleType = HandleType.CARET;
|
||||
else
|
||||
mHandleType = HandleType.END;
|
||||
mHandleType = HandleType.FOCUS;
|
||||
|
||||
mGeckoPoint = new PointF(0.0f, 0.0f);
|
||||
|
||||
@ -132,7 +152,7 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener {
|
||||
// will tell us the position of the caret, so we set the handle
|
||||
// position then. This allows us to lock the handle to wherever the
|
||||
// caret appears.
|
||||
if (mHandleType != HandleType.MIDDLE) {
|
||||
if (mHandleType != HandleType.CARET) {
|
||||
setLayoutPosition();
|
||||
}
|
||||
}
|
||||
@ -166,9 +186,9 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener {
|
||||
}
|
||||
|
||||
private float adjustLeftForHandle() {
|
||||
if (mHandleType == HandleType.START) {
|
||||
if (mHandleType == HandleType.ANCHOR) {
|
||||
return mIsRTL ? mShadow : mWidth - mShadow;
|
||||
} else if (mHandleType == HandleType.MIDDLE) {
|
||||
} else if (mHandleType == HandleType.CARET) {
|
||||
return mWidth / 2;
|
||||
} else {
|
||||
return mIsRTL ? mWidth - mShadow : mShadow;
|
||||
|
@ -529,12 +529,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY updater_apply_title2 "Update available for &brandShortName;">
|
||||
<!ENTITY updater_apply_select2 "Touch to update">
|
||||
|
||||
<!-- New tablet UI -->
|
||||
|
||||
<!-- LOCALIZATION NOTE (new_tablet_restart): Notification displayed after the user toggles the new tablet UI in the settings screen.-->
|
||||
<!ENTITY new_tablet_restart "Restart the browser for the changes to take effect">
|
||||
<!ENTITY new_tablet_pref "Enable new tablet UI">
|
||||
|
||||
<!-- Guest mode -->
|
||||
<!ENTITY new_guest_session "New Guest Session">
|
||||
<!ENTITY exit_guest_session "Exit Guest Session">
|
||||
|
@ -517,24 +517,16 @@ public class GeckoMenu extends ListView
|
||||
}
|
||||
|
||||
if (actionView != null) {
|
||||
// The update could be coming from the background thread.
|
||||
// Post a runnable on the UI thread of the view for it to update.
|
||||
final GeckoMenuItem menuItem = item;
|
||||
actionView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (menuItem.isVisible()) {
|
||||
actionView.setVisibility(View.VISIBLE);
|
||||
if (actionView instanceof MenuItemActionBar) {
|
||||
((MenuItemActionBar) actionView).initialize(menuItem);
|
||||
} else {
|
||||
((MenuItemActionView) actionView).initialize(menuItem);
|
||||
}
|
||||
} else {
|
||||
actionView.setVisibility(View.GONE);
|
||||
}
|
||||
if (item.isVisible()) {
|
||||
actionView.setVisibility(View.VISIBLE);
|
||||
if (actionView instanceof MenuItemActionBar) {
|
||||
((MenuItemActionBar) actionView).initialize(item);
|
||||
} else {
|
||||
((MenuItemActionView) actionView).initialize(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
actionView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user