Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2014-12-31 13:12:51 -08:00
commit ac8b2c0ce1
118 changed files with 1045 additions and 1370 deletions

View File

@ -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

View File

@ -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*

View File

@ -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;

View File

@ -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);
}

View File

@ -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 {

View File

@ -359,6 +359,7 @@ HTMLComboboxAccessible::
HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
AccessibleWrap(aContent, aDoc)
{
mType = eHTMLComboboxType;
mGenericTypes |= eCombobox;
}

View File

@ -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>

View File

@ -86,7 +86,7 @@
]
},
]
},
},
{
role: ROLE_COMBOBOX_OPTION,
children: [

View File

@ -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()

View File

@ -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()

View File

@ -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]

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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;
};

View File

@ -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);

View File

@ -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;

View File

@ -76,7 +76,7 @@ GMPVideoDecoderParent::Close()
// In case this is the last reference
nsRefPtr<GMPVideoDecoderParent> kungfudeathgrip(this);
NS_RELEASE(kungfudeathgrip);
Release();
Shutdown();
}

View File

@ -91,7 +91,7 @@ GMPVideoEncoderParent::Close()
// In case this is the last reference
nsRefPtr<GMPVideoEncoderParent> kungfudeathgrip(this);
NS_RELEASE(kungfudeathgrip);
Release();
Shutdown();
}

View File

@ -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);

View File

@ -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]

View 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>

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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));
}
}

View File

@ -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)
{

View File

@ -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));
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -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

View File

@ -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_

View File

@ -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 */

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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,

View File

@ -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);

View File

@ -55,7 +55,7 @@ protected:
UniquePtr<gl::SharedSurface> mGLFrontbuffer;
bool mIsAlphaPremultiplied;
bool mNeedsYFlip;
gl::OriginPos mOriginPos;
RefPtr<gfx::DataSourceSurface> mCachedTempSurface;

View File

@ -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

View File

@ -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 =

View File

@ -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; }

View File

@ -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

View File

@ -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); }

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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;

View File

@ -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();
}

View File

@ -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.");

View File

@ -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)));

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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));
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -77,7 +77,7 @@ public:
TextureFlags aFlags,
gl::AndroidSurfaceTexture* aSurfTex,
gfx::IntSize aSize,
bool aInverted);
gl::OriginPos aOriginPos);
~SurfaceTextureClient();

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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());

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View 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>

View File

@ -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

View File

@ -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()) {

View File

@ -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));

View File

@ -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() ||

View File

@ -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++;

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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) {

View File

@ -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;

View File

@ -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">

View File

@ -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