mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-08 22:08:16 +00:00
Merge bug 486146
This commit is contained in:
commit
b724f896c3
@ -72,6 +72,7 @@ XPIDLSRCS = \
|
||||
nsIAccessibleText.idl \
|
||||
nsIAccessibleValue.idl \
|
||||
nsIAccessibleImage.idl \
|
||||
nsIXBLAccessible.idl \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_XUL
|
||||
|
53
accessible/public/nsIXBLAccessible.idl
Normal file
53
accessible/public/nsIXBLAccessible.idl
Normal file
@ -0,0 +1,53 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is the Mozilla browser.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Alexander Surkov <surkov.alexander@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* XBL controls can implement this interface to provide own implementation of
|
||||
* accessible properties.
|
||||
*/
|
||||
[scriptable, uuid(3716eb86-166b-445b-a94a-9b522fee96e6)]
|
||||
interface nsIXBLAccessible : nsISupports
|
||||
{
|
||||
/**
|
||||
* Return accessible name.
|
||||
*/
|
||||
readonly attribute DOMString accessibleName;
|
||||
};
|
@ -45,6 +45,7 @@
|
||||
|
||||
#include "nsAccessibleEventData.h"
|
||||
#include "nsHyperTextAccessible.h"
|
||||
#include "nsHTMLTableAccessible.h"
|
||||
#include "nsAccessibilityAtoms.h"
|
||||
#include "nsAccessibleTreeWalker.h"
|
||||
#include "nsAccessible.h"
|
||||
@ -725,6 +726,16 @@ nsAccUtils::QueryAccessible(nsIAccessible *aAccessible)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
already_AddRefed<nsHTMLTableAccessible>
|
||||
nsAccUtils::QueryAccessibleTable(nsIAccessibleTable *aAccessibleTable)
|
||||
{
|
||||
nsHTMLTableAccessible* accessible = nsnull;
|
||||
if (aAccessibleTable)
|
||||
CallQueryInterface(aAccessibleTable, &accessible);
|
||||
|
||||
return accessible;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_A11Y
|
||||
|
||||
PRBool
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "nsIAccessibleDocument.h"
|
||||
#include "nsIAccessibleRole.h"
|
||||
#include "nsIAccessibleText.h"
|
||||
#include "nsIAccessibleTable.h"
|
||||
#include "nsARIAMap.h"
|
||||
|
||||
#include "nsIDOMNode.h"
|
||||
@ -53,6 +54,7 @@
|
||||
|
||||
class nsAccessNode;
|
||||
class nsAccessible;
|
||||
class nsHTMLTableAccessible;
|
||||
|
||||
class nsAccUtils
|
||||
{
|
||||
@ -327,6 +329,12 @@ public:
|
||||
static already_AddRefed<nsAccessible>
|
||||
QueryAccessible(nsIAccessible *aAccessible);
|
||||
|
||||
/**
|
||||
* Query nsHTMLTableAccessible from the given nsIAccessibleTable.
|
||||
*/
|
||||
static already_AddRefed<nsHTMLTableAccessible>
|
||||
QueryAccessibleTable(nsIAccessibleTable *aAccessibleTable);
|
||||
|
||||
#ifdef DEBUG_A11Y
|
||||
/**
|
||||
* Detect whether the given accessible object implements nsIAccessibleText,
|
||||
|
@ -56,12 +56,14 @@
|
||||
|
||||
|
||||
// Alphabetical list of generic atoms
|
||||
ACCESSIBILITY_ATOM(_empty, "")
|
||||
ACCESSIBILITY_ATOM(button, "button")
|
||||
ACCESSIBILITY_ATOM(col, "col")
|
||||
ACCESSIBILITY_ATOM(_empty, "")
|
||||
ACCESSIBILITY_ATOM(_false, "false")
|
||||
ACCESSIBILITY_ATOM(image, "image")
|
||||
ACCESSIBILITY_ATOM(password, "password")
|
||||
ACCESSIBILITY_ATOM(reset, "reset")
|
||||
ACCESSIBILITY_ATOM(row, "row")
|
||||
ACCESSIBILITY_ATOM(submit, "submit")
|
||||
ACCESSIBILITY_ATOM(_true, "true")
|
||||
ACCESSIBILITY_ATOM(_undefined, "undefined")
|
||||
@ -165,6 +167,7 @@ ACCESSIBILITY_ATOM(data, "data")
|
||||
ACCESSIBILITY_ATOM(droppable, "droppable") // XUL combo box
|
||||
ACCESSIBILITY_ATOM(editable, "editable")
|
||||
ACCESSIBILITY_ATOM(_for, "for")
|
||||
ACCESSIBILITY_ATOM(headers, "headers") // HTML table
|
||||
ACCESSIBILITY_ATOM(hidden, "hidden") // XUL tree columns
|
||||
ACCESSIBILITY_ATOM(href, "href") // XUL, XLink
|
||||
ACCESSIBILITY_ATOM(increment, "increment") // XUL
|
||||
@ -178,6 +181,7 @@ ACCESSIBILITY_ATOM(multiline, "multiline") // XUL
|
||||
ACCESSIBILITY_ATOM(name, "name")
|
||||
ACCESSIBILITY_ATOM(onclick, "onclick")
|
||||
ACCESSIBILITY_ATOM(readonly, "readonly")
|
||||
ACCESSIBILITY_ATOM(scope, "scope") // HTML table
|
||||
ACCESSIBILITY_ATOM(simple, "simple") // XLink
|
||||
ACCESSIBILITY_ATOM(src, "src")
|
||||
ACCESSIBILITY_ATOM(selected, "selected")
|
||||
|
@ -557,6 +557,10 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame *aFrame,
|
||||
tag == nsAccessibilityAtoms::q) {
|
||||
return CreateHyperTextAccessible(aFrame, aAccessible);
|
||||
}
|
||||
else if (nsCoreUtils::IsHTMLTableHeader(content)) {
|
||||
*aAccessible = new nsHTMLTableHeaderAccessible(aNode, aWeakShell);
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aAccessible);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#include "nsIAccessibleDocument.h"
|
||||
#include "nsIAccessibleHyperText.h"
|
||||
#include "nsIXBLAccessible.h"
|
||||
#include "nsAccessibleTreeWalker.h"
|
||||
|
||||
#include "nsIDOMElement.h"
|
||||
@ -283,6 +284,13 @@ nsAccessible::GetName(nsAString& aName)
|
||||
if (!aName.IsEmpty())
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mDOMNode));
|
||||
if (xblAccessible) {
|
||||
nsresult rv = xblAccessible->GetAccessibleName(aName);
|
||||
if (!aName.IsEmpty())
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = GetNameInternal(aName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -71,7 +71,6 @@
|
||||
#include "nsContentCID.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIMutableArray.h"
|
||||
|
||||
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
|
||||
|
||||
@ -805,6 +804,56 @@ nsCoreUtils::GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::GetElementsHavingIDRefsAttr(nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIArray **aElements)
|
||||
{
|
||||
*aElements = nsnull;
|
||||
|
||||
nsAutoString id;
|
||||
if (!GetID(aContent, id))
|
||||
return;
|
||||
|
||||
nsCAutoString idWithSpaces(' ');
|
||||
LossyAppendUTF16toASCII(id, idWithSpaces);
|
||||
idWithSpaces += ' ';
|
||||
|
||||
nsCOMPtr<nsIMutableArray> elms = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!elms)
|
||||
return;
|
||||
|
||||
GetElementsHavingIDRefsAttrImpl(aRootContent, idWithSpaces, aIDRefsAttr,
|
||||
elms);
|
||||
NS_ADDREF(*aElements = elms);
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::GetElementsHavingIDRefsAttrImpl(nsIContent *aRootContent,
|
||||
nsCString& aIdWithSpaces,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIMutableArray *aElements)
|
||||
{
|
||||
PRUint32 childCount = aRootContent->GetChildCount();
|
||||
for (PRUint32 index = 0; index < childCount; index++) {
|
||||
nsIContent* child = aRootContent->GetChildAt(index);
|
||||
nsAutoString idList;
|
||||
if (child->GetAttr(kNameSpaceID_None, aIDRefsAttr, idList)) {
|
||||
idList.Insert(' ', 0); // Surround idlist with spaces for search
|
||||
idList.Append(' ');
|
||||
// idList is now a set of id's with spaces around each, and id also has
|
||||
// spaces around it. If id is a substring of idList then we have a match.
|
||||
if (idList.Find(aIdWithSpaces) != -1) {
|
||||
aElements->AppendElement(child, PR_FALSE);
|
||||
continue; // Do not search inside children.
|
||||
}
|
||||
}
|
||||
GetElementsHavingIDRefsAttrImpl(child, aIdWithSpaces,
|
||||
aIDRefsAttr, aElements);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
|
||||
nsIDOMNode *aNode,
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsIArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
class nsCoreUtils
|
||||
@ -248,6 +249,27 @@ public:
|
||||
static void GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
|
||||
nsIArray **aRefElements);
|
||||
|
||||
/**
|
||||
* Return the array of elements having IDRefs that points to the given node.
|
||||
*
|
||||
* @param aRootContent [in] root element to search inside
|
||||
* @param aContent [in] an element having ID attribute
|
||||
* @param aIDRefsAttr [in] IDRefs attribute
|
||||
* @param aElements [out] result array of elements
|
||||
*/
|
||||
static void GetElementsHavingIDRefsAttr(nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIArray **aElements);
|
||||
|
||||
/**
|
||||
* Helper method for GetElementsHavingIDRefsAttr.
|
||||
*/
|
||||
static void GetElementsHavingIDRefsAttrImpl(nsIContent *aRootContent,
|
||||
nsCString& aIdWithSpaces,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIMutableArray *aElements);
|
||||
|
||||
/**
|
||||
* Return computed styles declaration for the given node.
|
||||
*/
|
||||
@ -339,6 +361,15 @@ public:
|
||||
*/
|
||||
static already_AddRefed<nsIBoxObject>
|
||||
GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj);
|
||||
|
||||
/**
|
||||
* Return true if the given node is table header element.
|
||||
*/
|
||||
static PRBool IsHTMLTableHeader(nsIContent *aContent)
|
||||
{
|
||||
return aContent->NodeInfo()->Equals(nsAccessibilityAtoms::th) ||
|
||||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::scope);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1140,7 +1140,9 @@ nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID
|
||||
}
|
||||
|
||||
if (aAttribute == nsAccessibilityAtoms::alt ||
|
||||
aAttribute == nsAccessibilityAtoms::title) {
|
||||
aAttribute == nsAccessibilityAtoms::title ||
|
||||
aAttribute == nsAccessibilityAtoms::aria_label ||
|
||||
aAttribute == nsAccessibilityAtoms::aria_labelledby) {
|
||||
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
|
||||
targetNode);
|
||||
return;
|
||||
|
@ -158,3 +158,33 @@ nsRelUtils::AddTargetFromNeighbour(PRUint32 aRelationType,
|
||||
nsCoreUtils::FindNeighbourPointingToNode(aContent, aNeighboutAttr,
|
||||
aNeighboutTagName));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRelUtils::AddTargetFromChildrenHavingIDRefsAttr(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation,
|
||||
nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr)
|
||||
{
|
||||
nsCOMPtr<nsIArray> elms;
|
||||
nsCoreUtils::GetElementsHavingIDRefsAttr(aRootContent, aContent, aIDRefsAttr,
|
||||
getter_AddRefs(elms));
|
||||
if (!elms)
|
||||
return NS_OK_NO_RELATION_TARGET;
|
||||
|
||||
PRUint32 count = 0;
|
||||
nsresult rv = elms->GetLength(&count);
|
||||
if (NS_FAILED(rv) || count == 0)
|
||||
return NS_OK_NO_RELATION_TARGET;
|
||||
|
||||
nsCOMPtr<nsIContent> content;
|
||||
for (PRUint32 idx = 0; idx < count; idx++) {
|
||||
content = do_QueryElementAt(elms, idx, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = AddTargetFromContent(aRelationType, aRelation, content);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -134,6 +134,23 @@ public:
|
||||
nsIAtom *aNeighboutAttr,
|
||||
nsIAtom *aNeighboutTagName = nsnull);
|
||||
|
||||
/**
|
||||
* Create the relation if the given relation is null and add the targets to it
|
||||
* that have IDRefs attribute pointing to the given node.
|
||||
*
|
||||
* @param aRelationType [in] relation type
|
||||
* @param aRelation [in, out] relation object
|
||||
* @param aRootContent [in] root node we search inside of
|
||||
* @param aContent [in] node having ID
|
||||
* @param aIDRefsAttr [in] IDRefs attribute
|
||||
*/
|
||||
static nsresult
|
||||
AddTargetFromChildrenHavingIDRefsAttr(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation,
|
||||
nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr);
|
||||
|
||||
/**
|
||||
* Query nsAccessibleRelation from the given nsIAccessibleRelation.
|
||||
*/
|
||||
|
@ -61,7 +61,9 @@
|
||||
#include "nsLayoutErrors.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTableCellAccessible
|
||||
// nsHTMLTableCellAccessible implementation
|
||||
|
||||
// nsISupports
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLTableCellAccessible, nsHyperTextAccessible)
|
||||
|
||||
@ -70,6 +72,8 @@ nsHyperTextAccessibleWrap(aDomNode, aShell)
|
||||
{
|
||||
}
|
||||
|
||||
// nsAccessible
|
||||
|
||||
/* unsigned long getRole (); */
|
||||
nsresult
|
||||
nsHTMLTableCellAccessible::GetRoleInternal(PRUint32 *aResult)
|
||||
@ -87,11 +91,94 @@ nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttri
|
||||
nsresult rv = nsHyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
|
||||
if (!tableAcc)
|
||||
return NS_OK;
|
||||
|
||||
PRInt32 rowIdx = -1, colIdx = -1;
|
||||
rv = GetCellIndexes(rowIdx, colIdx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt32 idx = -1;
|
||||
rv = tableAcc->GetIndexAt(rowIdx, colIdx, &idx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString stringIdx;
|
||||
stringIdx.AppendInt(idx);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::tableCellIndex,
|
||||
stringIdx);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAccessible
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableCellAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation)
|
||||
{
|
||||
nsresult rv = nsHyperTextAccessibleWrap::GetRelationByType(aRelationType,
|
||||
aRelation);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aRelationType != nsIAccessibleRelation::RELATION_DESCRIBED_BY)
|
||||
return NS_OK;
|
||||
|
||||
// 'described_by' relation from @headers attribute.
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
rv = nsRelUtils::AddTargetFromIDRefsAttr(aRelationType, aRelation,
|
||||
content, nsAccessibilityAtoms::headers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return rv; // Do not calculate more relations.
|
||||
|
||||
// 'described_by' relation from hierarchy (see 11.4.3 "Algorithm to find
|
||||
// heading information" of w3c HTML 4.01)
|
||||
return FindCellsForRelation(eHeadersForCell, aRelationType, aRelation);
|
||||
}
|
||||
|
||||
// nsHTMLTableCellAccessible
|
||||
|
||||
already_AddRefed<nsIAccessibleTable>
|
||||
nsHTMLTableCellAccessible::GetTableAccessible()
|
||||
{
|
||||
nsCOMPtr<nsIAccessible> childAcc(this);
|
||||
|
||||
nsCOMPtr<nsIAccessible> parentAcc;
|
||||
nsresult rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
if (NS_FAILED(rv))
|
||||
return nsnull;
|
||||
|
||||
while (parentAcc) {
|
||||
if (nsAccUtils::Role(parentAcc) == nsIAccessibleRole::ROLE_TABLE) {
|
||||
// Table accessible must implement nsIAccessibleTable interface but if
|
||||
// it isn't happen (for example because of ARIA usage).
|
||||
if (!parentAcc)
|
||||
return nsnull;
|
||||
|
||||
nsIAccessibleTable* tableAcc = nsnull;
|
||||
CallQueryInterface(parentAcc, &tableAcc);
|
||||
return tableAcc;
|
||||
}
|
||||
|
||||
parentAcc.swap(childAcc);
|
||||
rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
if (NS_FAILED(rv))
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableCellAccessible::GetCellIndexes(PRInt32& aRowIndex,
|
||||
PRInt32& aColIndex)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = GetPresShell();
|
||||
NS_ENSURE_STATE(shell);
|
||||
|
||||
|
||||
nsIFrame *frame = shell->GetPrimaryFrameFor(content);
|
||||
NS_ASSERTION(frame, "The frame cannot be obtaied for HTML table cell.");
|
||||
NS_ENSURE_STATE(frame);
|
||||
@ -99,48 +186,211 @@ nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttri
|
||||
nsITableCellLayout *cellLayout = do_QueryFrame(frame);
|
||||
NS_ENSURE_STATE(cellLayout);
|
||||
|
||||
PRInt32 rowIdx = -1, cellIdx = -1;
|
||||
rv = cellLayout->GetCellIndexes(rowIdx, cellIdx);
|
||||
return cellLayout->GetCellIndexes(aRowIndex, aColIndex);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableCellAccessible::FindCellsForRelation(PRInt32 aSearchHint,
|
||||
PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation)
|
||||
{
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
|
||||
nsRefPtr<nsHTMLTableAccessible> nsTableAcc =
|
||||
nsAccUtils::QueryAccessibleTable(tableAcc);
|
||||
if (!nsTableAcc)
|
||||
return NS_OK; // Do not fail because of wrong markup.
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
PRInt32 rowIdx = -1, colIdx = -1;
|
||||
nsresult rv = GetCellIndexes(rowIdx, colIdx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAccessible> childAcc(this);
|
||||
PRBool moveToTopLeft = aSearchHint == eHeadersForCell;
|
||||
PRInt32 dir = (moveToTopLeft) ? -1 : 1;
|
||||
PRInt32 bound = 0;
|
||||
|
||||
nsCOMPtr<nsIAccessible> parentAcc;
|
||||
rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (parentAcc) {
|
||||
if (nsAccUtils::Role(parentAcc) == nsIAccessibleRole::ROLE_TABLE) {
|
||||
// Table accessible must implement nsIAccessibleTable interface but if
|
||||
// it isn't happen (for example because of ARIA usage) we shouldn't fail
|
||||
// on getting other attributes.
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(do_QueryInterface(parentAcc));
|
||||
if (!tableAcc)
|
||||
return NS_OK;
|
||||
|
||||
PRInt32 idx = -1;
|
||||
rv = tableAcc->GetIndexAt(rowIdx, cellIdx, &idx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString stringIdx;
|
||||
stringIdx.AppendInt(idx);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::tableCellIndex,
|
||||
stringIdx);
|
||||
return NS_OK;
|
||||
// left/right direction
|
||||
if (aSearchHint != eCellsForColumnHeader) {
|
||||
if (!moveToTopLeft) {
|
||||
tableAcc->GetColumns(&bound);
|
||||
bound--;
|
||||
}
|
||||
|
||||
parentAcc.swap(childAcc);
|
||||
rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
for (PRInt32 index = colIdx + dir; dir * index <= bound; index += dir) {
|
||||
// Left direction means we look for the first columnheader. Right direction
|
||||
// means we look for all cells underneath of columnheader.
|
||||
nsIContent *cellContent = FindCell(nsTableAcc, content, rowIdx, index,
|
||||
moveToTopLeft);
|
||||
|
||||
if (cellContent) {
|
||||
nsRelUtils::AddTargetFromContent(aRelationType, aRelation, cellContent);
|
||||
if (moveToTopLeft)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// up/down direction
|
||||
if (aSearchHint != eCellsForRowHeader) {
|
||||
if (!moveToTopLeft) {
|
||||
tableAcc->GetRows(&bound);
|
||||
bound--;
|
||||
}
|
||||
|
||||
for (PRInt32 index = rowIdx + dir; dir * index <= bound; index += dir) {
|
||||
// Left direction means we look for the first rowheader. Right direction
|
||||
// means we look for all cells underneath of rowheader.
|
||||
nsIContent *cellContent = FindCell(nsTableAcc, content, index, colIdx,
|
||||
moveToTopLeft);
|
||||
|
||||
if (cellContent) {
|
||||
nsRelUtils::AddTargetFromContent(aRelationType, aRelation, cellContent);
|
||||
if (moveToTopLeft)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsHTMLTableCellAccessible::FindCell(nsHTMLTableAccessible *aTableAcc,
|
||||
nsIContent *aAnchorCell,
|
||||
PRInt32 aRowIdx, PRInt32 aColIdx,
|
||||
PRInt32 aLookForHeader)
|
||||
{
|
||||
nsCOMPtr<nsIDOMElement> cellElm;
|
||||
aTableAcc->GetCellAt(aRowIdx, aColIdx, *getter_AddRefs(cellElm));
|
||||
if (!cellElm)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm));
|
||||
if (aAnchorCell == cellContent) // colspan or rowspan case
|
||||
return nsnull;
|
||||
|
||||
if (aLookForHeader) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(cellContent))
|
||||
return cellContent;
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return cellContent;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTableHeaderAccessible
|
||||
|
||||
nsHTMLTableHeaderAccessible::
|
||||
nsHTMLTableHeaderAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell):
|
||||
nsHTMLTableCellAccessible(aDomNode, aShell)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableHeaderAccessible::GetRoleInternal(PRUint32 *aRole)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
// Check value of @scope attribute.
|
||||
static nsIContent::AttrValuesArray scopeValues[] =
|
||||
{&nsAccessibilityAtoms::col, &nsAccessibilityAtoms::row, nsnull};
|
||||
PRInt32 valueIdx =
|
||||
content->FindAttrValueIn(kNameSpaceID_None, nsAccessibilityAtoms::scope,
|
||||
scopeValues, eCaseMatters);
|
||||
|
||||
switch (valueIdx) {
|
||||
case 0:
|
||||
*aRole = nsIAccessibleRole::ROLE_COLUMNHEADER;
|
||||
return NS_OK;
|
||||
case 1:
|
||||
*aRole = nsIAccessibleRole::ROLE_ROWHEADER;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Assume it's columnheader if there are headers in siblings, oterwise
|
||||
// rowheader.
|
||||
nsIContent* parent = content->GetParent();
|
||||
PRInt32 indexInParent = parent->IndexOf(content);
|
||||
|
||||
for (PRInt32 idx = indexInParent - 1; idx >= 0; idx--) {
|
||||
nsIContent* sibling = parent->GetChildAt(idx);
|
||||
if (sibling && sibling->IsNodeOfType(nsINode::eELEMENT)) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(sibling))
|
||||
*aRole = nsIAccessibleRole::ROLE_COLUMNHEADER;
|
||||
else
|
||||
*aRole = nsIAccessibleRole::ROLE_ROWHEADER;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32 childCount = parent->GetChildCount();
|
||||
for (PRInt32 idx = indexInParent + 1; idx < childCount; idx++) {
|
||||
nsIContent* sibling = parent->GetChildAt(idx);
|
||||
if (sibling && sibling->IsNodeOfType(nsINode::eELEMENT)) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(sibling))
|
||||
*aRole = nsIAccessibleRole::ROLE_COLUMNHEADER;
|
||||
else
|
||||
*aRole = nsIAccessibleRole::ROLE_ROWHEADER;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableHeaderAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation)
|
||||
{
|
||||
nsresult rv = nsHyperTextAccessibleWrap::
|
||||
GetRelationByType(aRelationType, aRelation);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aRelationType != nsIAccessibleRelation::RELATION_DESCRIPTION_FOR)
|
||||
return rv;
|
||||
|
||||
// 'description_for' relation from @headers attribute placed on table cells.
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
|
||||
if (!tableAcc)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIAccessNode> tableAccNode(do_QueryInterface(tableAcc));
|
||||
nsCOMPtr<nsIDOMNode> tableNode;
|
||||
tableAccNode->GetDOMNode(getter_AddRefs(tableNode));
|
||||
nsCOMPtr<nsIContent> tableContent(do_QueryInterface(tableNode));
|
||||
if (!tableContent)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
rv = nsRelUtils::
|
||||
AddTargetFromChildrenHavingIDRefsAttr(aRelationType, aRelation,
|
||||
tableContent, content,
|
||||
nsAccessibilityAtoms::headers);
|
||||
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return rv; // Do not calculate more relations.
|
||||
|
||||
// 'description_for' relation from hierarchy.
|
||||
PRUint32 role;
|
||||
rv = GetRoleInternal(&role);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (role == nsIAccessibleRole::ROLE_COLUMNHEADER)
|
||||
return FindCellsForRelation(eCellsForColumnHeader, aRelationType, aRelation);
|
||||
|
||||
return FindCellsForRelation(eCellsForRowHeader, aRelationType, aRelation);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTableAccessible
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLTableAccessible, nsAccessible, nsIAccessibleTable)
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsHTMLTableAccessible, nsAccessible,
|
||||
nsHTMLTableAccessible, nsIAccessibleTable)
|
||||
|
||||
nsHTMLTableAccessible::nsHTMLTableAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell):
|
||||
nsAccessibleWrap(aDomNode, aShell)
|
||||
|
@ -42,25 +42,105 @@
|
||||
#include "nsBaseWidgetAccessible.h"
|
||||
#include "nsIAccessibleTable.h"
|
||||
|
||||
class nsITableLayout;
|
||||
|
||||
/**
|
||||
* HTML table cell accessible (html:td).
|
||||
*/
|
||||
class nsHTMLTableCellAccessible : public nsHyperTextAccessibleWrap
|
||||
{
|
||||
public:
|
||||
nsHTMLTableCellAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell);
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetRoleInternal(PRUint32 *aRole);
|
||||
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
|
||||
|
||||
protected:
|
||||
already_AddRefed<nsIAccessibleTable> GetTableAccessible();
|
||||
nsresult GetCellIndexes(PRInt32& aRowIdx, PRInt32& aColIdx);
|
||||
|
||||
/**
|
||||
* Search hint enum constants. Used by FindCellsForRelation method.
|
||||
*/
|
||||
enum {
|
||||
// search for header cells, up-left direction search
|
||||
eHeadersForCell,
|
||||
// search for row header cell, right direction search
|
||||
eCellsForRowHeader,
|
||||
// search for column header cell, down direction search
|
||||
eCellsForColumnHeader
|
||||
};
|
||||
|
||||
/**
|
||||
* Add found cells as relation targets.
|
||||
*
|
||||
* @param aSearchHint [in] enum constan defined above, defines an
|
||||
* algorithm to search cells
|
||||
* @param aRelationType [in] relation type
|
||||
* @param aRelation [in, out] relation object
|
||||
*/
|
||||
nsresult FindCellsForRelation(PRInt32 aSearchHint, PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
|
||||
/**
|
||||
* Return the cell or header cell at the given row and column.
|
||||
*
|
||||
* @param aTableAcc [in] table accessible the search is prepared in
|
||||
* @param aAnchorCell [in] anchor cell, found cell should be different
|
||||
* from it
|
||||
* @param aRowIdx [in] row index
|
||||
* @param aColIdx [in] column index
|
||||
* @param aLookForHeader [in] flag specifies if found cell must be a header
|
||||
* @return found cell content
|
||||
*/
|
||||
nsIContent* FindCell(nsHTMLTableAccessible *aTableAcc, nsIContent *aAnchorCell,
|
||||
PRInt32 aRowIdx, PRInt32 aColIdx,
|
||||
PRInt32 aLookForHeader);
|
||||
};
|
||||
|
||||
class nsITableLayout;
|
||||
|
||||
/**
|
||||
* HTML table row/column header accessible (html:th or html:td@scope).
|
||||
*/
|
||||
class nsHTMLTableHeaderAccessible : public nsHTMLTableCellAccessible
|
||||
{
|
||||
public:
|
||||
nsHTMLTableHeaderAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell);
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetRoleInternal(PRUint32 *aRole);
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* HTML table accessible.
|
||||
*/
|
||||
|
||||
// To turn on table debugging descriptions define SHOW_LAYOUT_HEURISTIC
|
||||
// This allow release trunk builds to be used by testers to refine the
|
||||
// data vs. layout heuristic
|
||||
// #define SHOW_LAYOUT_HEURISTIC
|
||||
|
||||
#define NS_TABLEACCESSIBLE_IMPL_CID \
|
||||
{ /* 8d6d9c40-74bd-47ac-88dc-4a23516aa23d */ \
|
||||
0x8d6d9c40, \
|
||||
0x74bd, \
|
||||
0x47ac, \
|
||||
{ 0x88, 0xdc, 0x4a, 0x23, 0x51, 0x6a, 0xa2, 0x3d } \
|
||||
}
|
||||
|
||||
class nsHTMLTableAccessible : public nsAccessibleWrap,
|
||||
public nsIAccessibleTable
|
||||
{
|
||||
@ -69,6 +149,7 @@ public:
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIACCESSIBLETABLE
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TABLEACCESSIBLE_IMPL_CID)
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetDescription(nsAString& aDescription);
|
||||
@ -97,6 +178,11 @@ public:
|
||||
*/
|
||||
PRBool IsValidRow(PRInt32 aRow);
|
||||
|
||||
/**
|
||||
* Retun cell element at the given row and column index.
|
||||
*/
|
||||
nsresult GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex,
|
||||
nsIDOMElement* &aCell);
|
||||
protected:
|
||||
|
||||
/**
|
||||
@ -124,15 +210,16 @@ protected:
|
||||
virtual void CacheChildren();
|
||||
nsresult GetTableNode(nsIDOMNode **_retval);
|
||||
nsresult GetTableLayout(nsITableLayout **aLayoutObject);
|
||||
nsresult GetCellAt(PRInt32 aRowIndex,
|
||||
PRInt32 aColIndex,
|
||||
nsIDOMElement* &aCell);
|
||||
PRBool HasDescendant(char *aTagName, PRBool aAllowEmpty = PR_TRUE);
|
||||
#ifdef SHOW_LAYOUT_HEURISTIC
|
||||
nsAutoString mLayoutHeuristic;
|
||||
#endif
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsHTMLTableAccessible,
|
||||
NS_TABLEACCESSIBLE_IMPL_CID)
|
||||
|
||||
|
||||
class nsHTMLTableHeadAccessible : public nsHTMLTableAccessible
|
||||
{
|
||||
public:
|
||||
|
@ -132,17 +132,6 @@ nsXULSliderAccessible::SetCurrentValue(double aValue)
|
||||
return SetSliderAttr(nsAccessibilityAtoms::curpos, aValue);
|
||||
}
|
||||
|
||||
// nsPIAccessible
|
||||
NS_IMETHODIMP
|
||||
nsXULSliderAccessible::GetAllowsAnonChildAccessibles(PRBool *aAllowsAnonChildren)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aAllowsAnonChildren);
|
||||
|
||||
// Allow anonymous xul:thumb inside xul:slider.
|
||||
*aAllowsAnonChildren = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Utils
|
||||
|
||||
already_AddRefed<nsIContent>
|
||||
|
@ -57,9 +57,6 @@ public:
|
||||
// nsIAccessibleValue
|
||||
NS_DECL_NSIACCESSIBLEVALUE
|
||||
|
||||
// nsPIAccessible
|
||||
NS_IMETHOD GetAllowsAnonChildAccessibles(PRBool *aAllowsAnonChildren);
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetRoleInternal(PRUint32 *aRole);
|
||||
|
||||
|
@ -269,20 +269,27 @@ NS_IMETHODIMP nsXULTreeAccessible::GetFirstChild(nsIAccessible **aFirstChild)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsXULTreeAccessible::GetLastChild(nsIAccessible **aLastChild)
|
||||
NS_IMETHODIMP
|
||||
nsXULTreeAccessible::GetLastChild(nsIAccessible **aLastChild)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aLastChild);
|
||||
*aLastChild = nsnull;
|
||||
|
||||
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
|
||||
|
||||
PRInt32 rowCount;
|
||||
PRInt32 rowCount = 0;
|
||||
mTreeView->GetRowCount(&rowCount);
|
||||
if (rowCount > 0) {
|
||||
nsCOMPtr<nsITreeColumn> column = GetLastVisibleColumn(mTree);
|
||||
return GetCachedTreeitemAccessible(rowCount - 1, column, aLastChild);
|
||||
nsresult rv = GetCachedTreeitemAccessible(rowCount - 1, column, aLastChild);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else // if there is not any rows, use treecols as tree's last child
|
||||
nsAccessible::GetLastChild(aLastChild);
|
||||
|
||||
return NS_OK;
|
||||
if (*aLastChild)
|
||||
return NS_OK;
|
||||
|
||||
// If there is not any rows, use treecols as tree's last child.
|
||||
return nsAccessible::GetLastChild(aLastChild);
|
||||
}
|
||||
|
||||
// tree's children count is row count + treecols count
|
||||
|
@ -84,6 +84,7 @@ _TEST_FILES =\
|
||||
test_elm_media.html \
|
||||
test_elm_txtcntnr.html \
|
||||
test_events_caretmove.html \
|
||||
test_events_focus.xul \
|
||||
test_events_mutation.html \
|
||||
test_events_tree.xul \
|
||||
test_groupattrs.xul \
|
||||
@ -110,6 +111,7 @@ _TEST_FILES =\
|
||||
test_objectattrs.html \
|
||||
test_relations.html \
|
||||
test_relations.xul \
|
||||
test_relations_table.html \
|
||||
test_role_nsHyperTextAcc.html \
|
||||
test_role_table_cells.html \
|
||||
test_states.html \
|
||||
|
@ -347,8 +347,14 @@ function prettyName(aIdentifier)
|
||||
{
|
||||
if (aIdentifier instanceof nsIAccessible) {
|
||||
var acc = getAccessible(aIdentifier, [nsIAccessNode]);
|
||||
return getNodePrettyName(acc.DOMNode) + ", role: " +
|
||||
roleToString(acc.role);
|
||||
var msg = "[" + getNodePrettyName(acc.DOMNode) +
|
||||
", role: " + roleToString(acc.role);
|
||||
|
||||
if (acc.name)
|
||||
msg += ", name: '" + acc.name + "'"
|
||||
msg += "]";
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (aIdentifier instanceof nsIDOMNode)
|
||||
|
@ -23,23 +23,28 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function focusChecker(aAcc, aName)
|
||||
function focusChecker(aAcc)
|
||||
{
|
||||
this.type = EVENT_FOCUS;
|
||||
this.target = aAcc;
|
||||
this.getID = function focusChecker_getID()
|
||||
{
|
||||
return "focus handling";
|
||||
}
|
||||
}
|
||||
|
||||
function nameChecker(aAcc, aName)
|
||||
{
|
||||
this.type = EVENT_NAME_CHANGE;
|
||||
this.target = aAcc;
|
||||
this.getID = function nameChecker_getID()
|
||||
{
|
||||
return "name change handling";
|
||||
},
|
||||
this.check = function focusChecker_check(aEvent)
|
||||
{
|
||||
SimpleTest.executeSoon(
|
||||
function()
|
||||
{
|
||||
is(aEvent.accessible.name, aName,
|
||||
"Wrong name of " + prettyName(aEvent.accessible) + " on focus");
|
||||
}
|
||||
);
|
||||
is(aEvent.accessible.name, aName,
|
||||
"Wrong name of " + prettyName(aEvent.accessible) + " on focus");
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,10 +67,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
|
||||
role: ROLE_PROGRESSBAR
|
||||
},
|
||||
{ // slider of progress bar
|
||||
role: ROLE_SLIDER
|
||||
role: ROLE_SLIDER,
|
||||
name: "0:00 of 0:01 elapsed"
|
||||
},
|
||||
{ // duration label
|
||||
role: ROLE_LABEL
|
||||
{ // duration label, role="presentation"
|
||||
role: ROLE_NOTHING
|
||||
},
|
||||
{ // mute button
|
||||
role: ROLE_PUSHBUTTON,
|
||||
@ -83,17 +89,23 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=483573
|
||||
var muteBtn = audioElm.lastChild;
|
||||
|
||||
var actions = [
|
||||
{
|
||||
ID: playBtn,
|
||||
actionName: "press",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [ new focusChecker(playBtn, "Pause") ],
|
||||
},
|
||||
{
|
||||
ID: muteBtn,
|
||||
actionName: "press",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [ new focusChecker(muteBtn, "Unmute") ],
|
||||
eventSeq: [
|
||||
new focusChecker(muteBtn),
|
||||
new nameChecker(muteBtn, "Unmute"),
|
||||
]
|
||||
},
|
||||
{
|
||||
ID: playBtn,
|
||||
actionName: "press",
|
||||
events: CLICK_EVENTS,
|
||||
eventSeq: [
|
||||
new focusChecker(playBtn),
|
||||
new nameChecker(playBtn, "Pause"),
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
73
accessible/tests/mochitest/test_events_focus.xul
Normal file
73
accessible/tests/mochitest/test_events_focus.xul
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="Accessible focus event testing">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/common.js" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/events.js" />
|
||||
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Focus invoker.
|
||||
*/
|
||||
function synthFocus(aNodeOrID)
|
||||
{
|
||||
this.DOMNode = getNode(aNodeOrID);
|
||||
|
||||
this.invoke = function synthFocus_invoke()
|
||||
{
|
||||
this.DOMNode.focus();
|
||||
}
|
||||
|
||||
this.getID = function synthFocus_getID() { return aNodeOrID + " focus"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
var gQueue = null;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
// Test focus events.
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
|
||||
|
||||
gQueue.push(new synthFocus("textbox"));
|
||||
gQueue.push(new synthFocus("scale"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTests);
|
||||
</script>
|
||||
|
||||
<hbox flex="1" style="overflow: auto;">
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=492518"
|
||||
title="xul:slider accessible of xul:scale is accessible illegally">
|
||||
Mozilla Bug 492518
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<vbox flex="1">
|
||||
<textbox id="textbox" value="hello"/>
|
||||
<scale id="scale" min="0" max="9" value="5"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
112
accessible/tests/mochitest/test_relations_table.html
Normal file
112
accessible/tests/mochitest/test_relations_table.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/relations.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function doTest()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// relations from markup
|
||||
|
||||
// 'description_for' relations for column headers
|
||||
testRelation("table1_0", RELATION_DESCRIPTION_FOR,
|
||||
["table1_3", "table1_6"]);
|
||||
testRelation("table1_1", RELATION_DESCRIPTION_FOR,
|
||||
["table1_4", "table1_7"]);
|
||||
testRelation("table1_2", RELATION_DESCRIPTION_FOR,
|
||||
["table1_5", "table1_8"]);
|
||||
|
||||
// 'description_for' relations for row headers
|
||||
testRelation("table1_3", RELATION_DESCRIPTION_FOR,
|
||||
["table1_4", "table1_5"]);
|
||||
testRelation("table1_6", RELATION_DESCRIPTION_FOR,
|
||||
["table1_7", "table1_8"]);
|
||||
|
||||
// 'described_by' relations for cells
|
||||
testRelation("table1_4", RELATION_DESCRIBED_BY,
|
||||
["table1_1", "table1_3"]);
|
||||
testRelation("table1_5", RELATION_DESCRIBED_BY,
|
||||
["table1_2", "table1_3"]);
|
||||
testRelation("table1_7", RELATION_DESCRIBED_BY,
|
||||
["table1_1", "table1_6"]);
|
||||
testRelation("table1_8", RELATION_DESCRIBED_BY,
|
||||
["table1_2", "table1_6"]);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// relations from @headers
|
||||
|
||||
// 'description_for' relations for column headers
|
||||
testRelation("table2_3", RELATION_DESCRIPTION_FOR,
|
||||
["table2_0"]);
|
||||
testRelation("table2_4", RELATION_DESCRIPTION_FOR,
|
||||
["table2_1"]);
|
||||
|
||||
// 'description_by' relations for cells
|
||||
testRelation("table2_0", RELATION_DESCRIBED_BY,
|
||||
["table2_3"]);
|
||||
testRelation("table2_1", RELATION_DESCRIBED_BY,
|
||||
["table2_4"]);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id="
|
||||
title="">Mozilla Bug </a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<table id="table1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="table1_0">col1</th>
|
||||
<th id="table1_1">col2</th>
|
||||
<td id="table1_2" scope="col">col3</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th id="table1_3">row1</th>
|
||||
<td id="table1_4">cell1</td>
|
||||
<td id="table1_5">cell2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="table1_6" scope="row">row2</td>
|
||||
<td id="table1_7">cell3</td>
|
||||
<td id="table1_8">cell4</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="table2">
|
||||
<tr>
|
||||
<td id="table2_0" headers="table2_3">cell1</td>
|
||||
<td id="table2_1" headers="table2_4">cell2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="table2_3" scope="col">col1</td>
|
||||
<td id="table2_4" scope="col">col2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -65,7 +65,7 @@ var StarUI = {
|
||||
// to avoid impacting startup / new window performance
|
||||
element.hidden = false;
|
||||
element.addEventListener("popuphidden", this, false);
|
||||
element.addEventListener("keypress", this, true);
|
||||
element.addEventListener("keypress", this, false);
|
||||
return this.panel = element;
|
||||
},
|
||||
|
||||
@ -112,25 +112,25 @@ var StarUI = {
|
||||
}
|
||||
break;
|
||||
case "keypress":
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) {
|
||||
// If the panel is visible the ESC key is mapped to the cancel button
|
||||
// unless we are editing a folder in the folderTree, or an
|
||||
// autocomplete popup is open.
|
||||
if (!this._element("editBookmarkPanelContent").hidden) {
|
||||
var elt = aEvent.target;
|
||||
if ((elt.localName != "tree" || !elt.hasAttribute("editing")) &&
|
||||
!elt.popupOpen)
|
||||
this.cancelButtonOnCommand();
|
||||
}
|
||||
if (aEvent.getPreventDefault()) {
|
||||
// The event has already been consumed inside of the panel.
|
||||
break;
|
||||
}
|
||||
else if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) {
|
||||
// hide the panel unless the folder tree or an expander are focused
|
||||
// or an autocomplete popup is open.
|
||||
if (aEvent.target.localName != "tree" &&
|
||||
aEvent.target.className != "expander-up" &&
|
||||
aEvent.target.className != "expander-down" &&
|
||||
!aEvent.target.popupOpen)
|
||||
switch (aEvent.keyCode) {
|
||||
case KeyEvent.DOM_VK_ESCAPE:
|
||||
if (!this._element("editBookmarkPanelContent").hidden)
|
||||
this.cancelButtonOnCommand();
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
if (aEvent.target.className == "expander-up" ||
|
||||
aEvent.target.className == "expander-down" ||
|
||||
aEvent.target.id == "editBMPanel_newFolderButton") {
|
||||
//XXX Why is this necessary? The getPreventDefault() check should
|
||||
// be enough.
|
||||
break;
|
||||
}
|
||||
this.panel.hidePopup();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -89,7 +89,7 @@
|
||||
<preference id="privacy.cpd.history" name="privacy.cpd.history" type="bool"/>
|
||||
<preference id="privacy.cpd.formdata" name="privacy.cpd.formdata" type="bool"/>
|
||||
<preference id="privacy.cpd.passwords" name="privacy.cpd.passwords" type="bool"/>
|
||||
<preference id="privacy.cpd.downloads" name="privacy.cpd.downloads" type="bool"/>
|
||||
<preference id="privacy.cpd.downloads" name="privacy.cpd.downloads" type="bool" disabled="true"/>
|
||||
<preference id="privacy.cpd.cookies" name="privacy.cpd.cookies" type="bool"/>
|
||||
<preference id="privacy.cpd.cache" name="privacy.cpd.cache" type="bool"/>
|
||||
<preference id="privacy.cpd.offlineApps" name="privacy.cpd.offlineApps" type="bool"/>
|
||||
@ -128,6 +128,8 @@
|
||||
value="&clearTimeDuration.suffix;"/>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
#ifdef CRH_DIALOG_TREE_VIEW
|
||||
<deck id="durationDeck">
|
||||
<tree id="placesTree" flex="1" hidecolumnpicker="true" rows="10"
|
||||
@ -149,7 +151,7 @@
|
||||
<spacer flex="1"/>
|
||||
<hbox align="center">
|
||||
<image id="sanitizeEverythingWarningIcon"/>
|
||||
<vbox id="sanitizeEverythingWarningDescBox">
|
||||
<vbox id="sanitizeEverythingWarningDescBox" flex="1">
|
||||
<description id="sanitizeEverythingWarning"/>
|
||||
<description id="sanitizeEverythingUndoWarning">&sanitizeEverythingUndoWarning;</description>
|
||||
</vbox>
|
||||
@ -174,11 +176,11 @@
|
||||
control="detailsExpander"/>
|
||||
</hbox>
|
||||
<listbox id="itemList" rows="4" collapsed="true">
|
||||
<listitem id="history-downloads-checkbox"
|
||||
label="&itemHistoryAndDownloads.label;"
|
||||
<listitem label="&itemHistoryAndDownloads.label;"
|
||||
type="checkbox"
|
||||
accesskey="&itemHistoryAndDownloads.accesskey;"
|
||||
oncommand="gSanitizePromptDialog.onReadGeneric();"/>
|
||||
preference="privacy.cpd.history"
|
||||
onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
|
||||
<listitem label="&itemFormSearchHistory.label;"
|
||||
type="checkbox"
|
||||
accesskey="&itemFormSearchHistory.accesskey;"
|
||||
@ -207,17 +209,5 @@
|
||||
onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
|
||||
</listbox>
|
||||
|
||||
<!-- The separate history and downloads prefs are subsumed by the combined
|
||||
history-downloads checkbox, but by hiding them here we can take
|
||||
advantage of the onsyncfrompreference events. -->
|
||||
<checkbox id="history-checkbox"
|
||||
preference="privacy.cpd.history"
|
||||
onsyncfrompreference="return gSanitizePromptDialog.onReadHistoryOrDownloads();"
|
||||
hidden="true"/>
|
||||
<checkbox id="downloads-checkbox"
|
||||
preference="privacy.cpd.downloads"
|
||||
onsyncfrompreference="return gSanitizePromptDialog.onReadHistoryOrDownloads();"
|
||||
hidden="true"/>
|
||||
|
||||
</prefpane>
|
||||
</prefwindow>
|
||||
|
@ -199,22 +199,15 @@ var gSanitizePromptDialog = {
|
||||
*/
|
||||
onReadGeneric: function ()
|
||||
{
|
||||
// We don't update the separate history and downloads prefs until
|
||||
// dialogaccept. So we need to handle the checked state of the combined
|
||||
// history-downloads checkbox specially.
|
||||
var combinedCb = document.getElementById("history-downloads-checkbox");
|
||||
var found = combinedCb.checked;
|
||||
var found = false;
|
||||
|
||||
// Find any other pref that's checked and enabled.
|
||||
var i = 0;
|
||||
while (!found && i < this.sanitizePreferences.childNodes.length) {
|
||||
var preference = this.sanitizePreferences.childNodes[i];
|
||||
|
||||
// We took into account history and downloads above; don't do it again.
|
||||
found = !!preference.value &&
|
||||
!preference.disabled &&
|
||||
preference.id !== "privacy.cpd.history" &&
|
||||
preference.id !== "privacy.cpd.downloads";
|
||||
!preference.disabled;
|
||||
i++;
|
||||
}
|
||||
|
||||
@ -225,23 +218,6 @@ var gSanitizePromptDialog = {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the values of the history and downloads preference elements are
|
||||
* synced from the actual prefs. Sets the state of the combined history-
|
||||
* downloads checkbox appropriately.
|
||||
*/
|
||||
onReadHistoryOrDownloads: function ()
|
||||
{
|
||||
// Call the common function that will update the accept button
|
||||
this.onReadGeneric();
|
||||
|
||||
var historyPref = document.getElementById("privacy.cpd.history");
|
||||
var downloadsPref = document.getElementById("privacy.cpd.downloads");
|
||||
var combinedCb = document.getElementById("history-downloads-checkbox");
|
||||
combinedCb.disabled = historyPref.disabled && downloadsPref.disabled;
|
||||
combinedCb.checked = historyPref.value || downloadsPref.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sanitizer.prototype.sanitize() requires the prefs to be up-to-date.
|
||||
* Because the type of this prefwindow is "child" -- and that's needed because
|
||||
@ -255,14 +231,9 @@ var gSanitizePromptDialog = {
|
||||
var tsPref = document.getElementById("privacy.sanitize.timeSpan");
|
||||
Sanitizer.prefs.setIntPref("timeSpan", this.selectedTimespan);
|
||||
|
||||
// First set the values of the separate history and downloads pref
|
||||
// elements based on the combined history-downloads checkbox.
|
||||
var combinedCbChecked =
|
||||
document.getElementById("history-downloads-checkbox").checked;
|
||||
var historyPref = document.getElementById("privacy.cpd.history");
|
||||
historyPref.value = !historyPref.disabled && combinedCbChecked;
|
||||
var downloadsPref = document.getElementById("privacy.cpd.downloads");
|
||||
downloadsPref.value = !downloadsPref.disabled && combinedCbChecked;
|
||||
// Keep the pref for the download history in sync with the history pref.
|
||||
document.getElementById("privacy.cpd.downloads").value =
|
||||
document.getElementById("privacy.cpd.history").value;
|
||||
|
||||
// Now manually set the prefs from their corresponding preference
|
||||
// elements.
|
||||
|
@ -31,6 +31,7 @@
|
||||
- Michael Ventnor <ventnor.bugzilla@yahoo.com.au>
|
||||
- Mark Pilgrim <pilgrim@gmail.com>
|
||||
- Dão Gottwald <dao@mozilla.com>
|
||||
- Paul O’Shannessy <paul@oshannessy.com>
|
||||
-
|
||||
- Alternatively, the contents of this file may be used under the terms of
|
||||
- either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -1487,7 +1488,7 @@
|
||||
<parameter name="aTab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this._endRemoveTab(this._beginRemoveTab(aTab, true, null, true));
|
||||
this._endRemoveTab(this._beginRemoveTab(aTab, false, null, true));
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
@ -1501,7 +1502,7 @@
|
||||
<!-- Returns everything that _endRemoveTab needs in an array. -->
|
||||
<method name="_beginRemoveTab">
|
||||
<parameter name="aTab"/>
|
||||
<parameter name="aFireBeforeUnload"/>
|
||||
<parameter name="aTabWillBeMoved"/>
|
||||
<parameter name="aCloseWindowWithLastTab"/>
|
||||
<parameter name="aCloseWindowFastpath"/>
|
||||
<body>
|
||||
@ -1511,7 +1512,7 @@
|
||||
|
||||
var browser = this.getBrowserForTab(aTab);
|
||||
|
||||
if (aFireBeforeUnload) {
|
||||
if (!aTabWillBeMoved) {
|
||||
let ds = browser.docShell;
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload())
|
||||
return null;
|
||||
@ -1550,8 +1551,8 @@
|
||||
// Dispatch a notification.
|
||||
// We dispatch it before any teardown so that event listeners can
|
||||
// inspect the tab that's about to close.
|
||||
var evt = document.createEvent("Events");
|
||||
evt.initEvent("TabClose", true, false);
|
||||
var evt = document.createEvent("UIEvent");
|
||||
evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
|
||||
aTab.dispatchEvent(evt);
|
||||
|
||||
// Remove the tab's filter and progress listener.
|
||||
@ -1716,7 +1717,7 @@
|
||||
// First, start teardown of the other browser. Make sure to not
|
||||
// fire the beforeunload event in the process. Close the other
|
||||
// window if this was its last tab.
|
||||
var endRemoveArgs = remoteBrowser._beginRemoveTab(aOtherTab, false, true);
|
||||
var endRemoveArgs = remoteBrowser._beginRemoveTab(aOtherTab, true, true);
|
||||
|
||||
// Unhook our progress listener
|
||||
var ourIndex = aOurTab._tPos;
|
||||
@ -2280,7 +2281,7 @@
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.mTabs.length == 1)
|
||||
return;
|
||||
return null;
|
||||
|
||||
// tell a new window to take the "dropped" tab
|
||||
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
|
||||
|
@ -115,6 +115,7 @@ _BROWSER_FILES = browser_sanitize-timespans.js \
|
||||
browser_overflowScroll.js \
|
||||
browser_sanitizeDialog.js \
|
||||
browser_tabs_owner.js \
|
||||
browser_bug491431.js \
|
||||
$(NULL)
|
||||
|
||||
ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
|
||||
|
@ -44,6 +44,15 @@ function step5()
|
||||
// listener, and focuses the current tab if another tab previously had focus
|
||||
is(document.activeElement, tab2, "mouse on another tab while focused still activeElement");
|
||||
|
||||
content.focus();
|
||||
EventUtils.synthesizeMouse(tab2, 2, 2, {button: 1, type: "mousedown"});
|
||||
setTimeout(step6, 0);
|
||||
}
|
||||
|
||||
function step6()
|
||||
{
|
||||
isnot(document.activeElement, tab2, "tab not focused via middle click");
|
||||
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
|
||||
|
69
browser/base/content/test/browser_bug491431.js
Normal file
69
browser/base/content/test/browser_bug491431.js
Normal file
@ -0,0 +1,69 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is bug 491431 test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Paul O’Shannessy <paul@oshannessy.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let testPage = "data:text/plain,test bug 491431 Page";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let newWin, tabA, tabB;
|
||||
|
||||
// test normal close
|
||||
tabA = gBrowser.addTab(testPage);
|
||||
gBrowser.addEventListener("TabClose", function(aEvent) {
|
||||
gBrowser.removeEventListener("TabClose", arguments.callee, true);
|
||||
ok(!aEvent.detail, "This was a normal tab close");
|
||||
|
||||
// test tab close by moving
|
||||
tabB = gBrowser.addTab(testPage);
|
||||
gBrowser.addEventListener("TabClose", function(aEvent) {
|
||||
gBrowser.removeEventListener("TabClose", arguments.callee, true);
|
||||
executeSoon(function() {
|
||||
ok(aEvent.detail, "This was a tab closed by moving");
|
||||
|
||||
// cleanup
|
||||
newWin.close();
|
||||
executeSoon(finish);
|
||||
});
|
||||
}, true);
|
||||
newWin = gBrowser.replaceTabWithWindow(tabB);
|
||||
}, true);
|
||||
gBrowser.removeTab(tabA);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ var gAllTests = [
|
||||
let wh = new WindowHelper();
|
||||
wh.onload = function () {
|
||||
this.selectDuration(Sanitizer.TIMESPAN_HOUR);
|
||||
this.checkPrefCheckbox("history-downloads-checkbox", false);
|
||||
this.checkPrefCheckbox("history", false);
|
||||
this.checkDetails();
|
||||
this.toggleDetails();
|
||||
this.checkDetails();
|
||||
@ -125,7 +125,7 @@ var gAllTests = [
|
||||
let wh = new WindowHelper();
|
||||
wh.onload = function () {
|
||||
this.selectDuration(Sanitizer.TIMESPAN_HOUR);
|
||||
this.checkPrefCheckbox("history-downloads-checkbox", true);
|
||||
this.checkPrefCheckbox("history", true);
|
||||
this.acceptDialog();
|
||||
|
||||
intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_HOUR,
|
||||
@ -133,10 +133,10 @@ var gAllTests = [
|
||||
"hour selected");
|
||||
boolPrefIs("cpd.history", true,
|
||||
"history pref should be true after accepting dialog with " +
|
||||
"combined history-downloads checkbox checked");
|
||||
"history checkbox checked");
|
||||
boolPrefIs("cpd.downloads", true,
|
||||
"downloads pref should be true after accepting dialog with " +
|
||||
"combined history-downloads checkbox checked");
|
||||
"history checkbox checked");
|
||||
|
||||
// History visits and downloads within one hour should be cleared.
|
||||
ensureHistoryClearedState(uris, true);
|
||||
@ -180,8 +180,8 @@ var gAllTests = [
|
||||
"with a predefined timespan");
|
||||
this.selectDuration(Sanitizer.TIMESPAN_HOUR);
|
||||
|
||||
// Remove only form entries, leave history and downloads.
|
||||
this.checkPrefCheckbox("history-downloads-checkbox", false);
|
||||
// Remove only form entries, leave history (including downloads).
|
||||
this.checkPrefCheckbox("history", false);
|
||||
this.checkPrefCheckbox("formdata", true);
|
||||
this.acceptDialog();
|
||||
|
||||
@ -190,10 +190,10 @@ var gAllTests = [
|
||||
"hour selected");
|
||||
boolPrefIs("cpd.history", false,
|
||||
"history pref should be false after accepting dialog with " +
|
||||
"combined history-downloads checkbox unchecked");
|
||||
"history checkbox unchecked");
|
||||
boolPrefIs("cpd.downloads", false,
|
||||
"downloads pref should be false after accepting dialog with " +
|
||||
"combined history-downloads checkbox unchecked");
|
||||
"history checkbox unchecked");
|
||||
|
||||
// Of the three only form entries should be cleared.
|
||||
ensureHistoryClearedState(uris, false);
|
||||
@ -225,7 +225,7 @@ var gAllTests = [
|
||||
"Warning panel should be hidden after previously accepting dialog " +
|
||||
"with a predefined timespan");
|
||||
this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
|
||||
this.checkPrefCheckbox("history-downloads-checkbox", true);
|
||||
this.checkPrefCheckbox("history", true);
|
||||
this.checkDetails();
|
||||
this.toggleDetails();
|
||||
this.checkDetails();
|
||||
@ -257,7 +257,7 @@ var gAllTests = [
|
||||
"Warning panel should be visible after previously accepting dialog " +
|
||||
"with clearing everything");
|
||||
this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
|
||||
this.checkPrefCheckbox("history-downloads-checkbox", true);
|
||||
this.checkPrefCheckbox("history", true);
|
||||
this.acceptDialog();
|
||||
|
||||
intPrefIs("sanitize.timeSpan", Sanitizer.TIMESPAN_EVERYTHING,
|
||||
@ -336,22 +336,17 @@ WindowHelper.prototype = {
|
||||
* form history, etc.).
|
||||
*
|
||||
* @param aPrefName
|
||||
* Either the ID of the checkbox or the final portion of its
|
||||
* privacy.cpd.* preference name
|
||||
* 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) {
|
||||
let checkBoxes = this.win.document.getElementsByTagName("listitem");
|
||||
for (let i = 0; i < checkBoxes.length; i++) {
|
||||
let cb = checkBoxes[i];
|
||||
if (cb.id === aPrefName ||
|
||||
(cb.hasAttribute("preference") &&
|
||||
cb.getAttribute("preference") === "privacy.cpd." + aPrefName)) {
|
||||
cb.checked = aCheckState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -111,7 +111,7 @@ var gAllTests = [
|
||||
wh.getRowCount() - 1);
|
||||
|
||||
// Cancel the dialog, make sure history visits are not cleared.
|
||||
wh.checkPrefCheckbox("history-downloads-checkbox", false);
|
||||
wh.checkPrefCheckbox("history", false);
|
||||
|
||||
wh.cancelDialog();
|
||||
ensureHistoryClearedState(uris, false);
|
||||
@ -157,7 +157,7 @@ var gAllTests = [
|
||||
|
||||
// Accept the dialog, make sure history visits and downloads within one
|
||||
// hour are cleared.
|
||||
wh.checkPrefCheckbox("history-downloads-checkbox", true);
|
||||
wh.checkPrefCheckbox("history", true);
|
||||
wh.acceptDialog();
|
||||
ensureHistoryClearedState(uris, true);
|
||||
ensureDownloadsClearedState(downloadIDs, true);
|
||||
@ -200,8 +200,8 @@ var gAllTests = [
|
||||
"duration",
|
||||
wh.getRowCount() - 1);
|
||||
|
||||
// Remove only form entries, leave history and downloads.
|
||||
wh.checkPrefCheckbox("history-downloads-checkbox", false);
|
||||
// Remove only form entries, leave history (including downloads).
|
||||
wh.checkPrefCheckbox("history", false);
|
||||
wh.checkPrefCheckbox("formdata", true);
|
||||
wh.acceptDialog();
|
||||
|
||||
@ -232,7 +232,7 @@ var gAllTests = [
|
||||
openWindow(function (aWin) {
|
||||
let wh = new WindowHelper(aWin);
|
||||
wh.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
|
||||
wh.checkPrefCheckbox("history-downloads-checkbox", true);
|
||||
wh.checkPrefCheckbox("history", true);
|
||||
wh.acceptDialog();
|
||||
ensureHistoryClearedState(uris, true);
|
||||
});
|
||||
@ -304,22 +304,17 @@ WindowHelper.prototype = {
|
||||
* form history, etc.).
|
||||
*
|
||||
* @param aPrefName
|
||||
* Either the ID of the checkbox or the final portion of its
|
||||
* privacy.cpd.* preference name
|
||||
* 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) {
|
||||
let checkBoxes = this.win.document.getElementsByTagName("listitem");
|
||||
for (let i = 0; i < checkBoxes.length; i++) {
|
||||
let cb = checkBoxes[i];
|
||||
if (cb.id === aPrefName ||
|
||||
(cb.hasAttribute("preference") &&
|
||||
cb.getAttribute("preference") === "privacy.cpd." + aPrefName)) {
|
||||
cb.checked = aCheckState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1216,7 +1216,11 @@ nsOperaProfileMigrator::CopySmartKeywords(nsINavBookmarksService* aBMS,
|
||||
section.AppendInt(sectionIndex++);
|
||||
|
||||
rv = parser.GetString(section.get(), "Name", name);
|
||||
if (NS_FAILED(rv) || name.IsEmpty())
|
||||
if (NS_FAILED(rv)) {
|
||||
// No more smart keywords found, stop parsing the file.
|
||||
break;
|
||||
}
|
||||
if (name.IsEmpty())
|
||||
continue;
|
||||
|
||||
rv = parser.GetString(section.get(), "URL", url);
|
||||
|
@ -374,10 +374,6 @@ var BookmarkPropertiesPanel = {
|
||||
this._element("siteLocationField")
|
||||
.addEventListener("input", this, false);
|
||||
}
|
||||
|
||||
// Set on document to get the event before an autocomplete popup could
|
||||
// be hidden on Enter.
|
||||
document.addEventListener("keypress", this, true);
|
||||
}
|
||||
|
||||
window.sizeToContent();
|
||||
@ -388,27 +384,6 @@ var BookmarkPropertiesPanel = {
|
||||
handleEvent: function BPP_handleEvent(aEvent) {
|
||||
var target = aEvent.target;
|
||||
switch (aEvent.type) {
|
||||
case "keypress":
|
||||
function canAcceptDialog(aElement) {
|
||||
// on Enter we accept the dialog unless:
|
||||
// - the folder tree is focused
|
||||
// - an expander is focused
|
||||
// - an autocomplete (eg. tags) popup is open
|
||||
// - a menulist is open
|
||||
// - a multiline textbox is focused
|
||||
return aElement.localName != "tree" &&
|
||||
aElement.className != "expander-up" &&
|
||||
aElement.className != "expander-down" &&
|
||||
!aElement.popupOpen &&
|
||||
!aElement.open &&
|
||||
!(aElement.localName == "textbox" &&
|
||||
aElement.getAttribute("multiline") == "true");
|
||||
}
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN &&
|
||||
canAcceptDialog(target))
|
||||
document.documentElement.acceptDialog();
|
||||
break;
|
||||
|
||||
case "input":
|
||||
if (target.id == "editBMPanel_locationField" ||
|
||||
target.id == "editBMPanel_feedLocationField" ||
|
||||
@ -506,7 +481,6 @@ var BookmarkPropertiesPanel = {
|
||||
// currently registered EventListener on the EventTarget has no effect.
|
||||
this._element("tagsSelectorRow")
|
||||
.removeEventListener("DOMAttrModified", this, false);
|
||||
document.removeEventListener("keypress", this, true);
|
||||
this._element("folderTreeRow")
|
||||
.removeEventListener("DOMAttrModified", this, false);
|
||||
this._element("locationField")
|
||||
|
@ -54,7 +54,6 @@
|
||||
|
||||
<dialog id="bookmarkproperties"
|
||||
buttons="accept, cancel"
|
||||
defaultButton="none"
|
||||
ondialogaccept="BookmarkPropertiesPanel.onDialogAccept();"
|
||||
ondialogcancel="BookmarkPropertiesPanel.onDialogCancel();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
|
@ -125,7 +125,7 @@
|
||||
var scrollDir = anonid == "scrollbutton-up" ? -1 :
|
||||
anonid == "scrollbutton-down" ? 1 : 0;
|
||||
if (scrollDir != 0) {
|
||||
this._scrollBox.scrollByIndex(scrollDir);
|
||||
this._scrollBox.scrollByIndex(scrollDir, false);
|
||||
}
|
||||
|
||||
// Check if we should hide the drop indicator for this target
|
||||
|
@ -285,25 +285,34 @@ var PlacesOrganizer = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle clicks on the tree. If the user middle clicks on a URL, load that
|
||||
* URL according to rules. Single clicks or modified clicks do not result in
|
||||
* any special action, since they're related to selection.
|
||||
* Handle clicks on the tree.
|
||||
* Single Left click, right click or modified click do not result in any
|
||||
* special action, since they're related to selection.
|
||||
* @param aEvent
|
||||
* The mouse event.
|
||||
*/
|
||||
onTreeClick: function PO_onTreeClick(aEvent) {
|
||||
// Only handle clicks on tree children.
|
||||
if (aEvent.target.localName != "treechildren")
|
||||
return;
|
||||
|
||||
var currentView = aEvent.currentTarget;
|
||||
var selectedNode = currentView.selectedNode;
|
||||
if (selectedNode && aEvent.button == 1) {
|
||||
if (PlacesUtils.nodeIsURI(selectedNode))
|
||||
PlacesUIUtils.openNodeWithEvent(selectedNode, aEvent);
|
||||
else if (PlacesUtils.nodeIsContainer(selectedNode)) {
|
||||
// The command execution function will take care of seeing the
|
||||
// selection is a folder/container and loading its contents in
|
||||
// tabs for us.
|
||||
if (selectedNode) {
|
||||
var doubleClickOnFlatList = (aEvent.button == 0 && aEvent.detail == 2 &&
|
||||
aEvent.target.parentNode.flatList);
|
||||
var middleClick = (Event.button == 1 && aEvent.detail == 1);
|
||||
|
||||
if (PlacesUtils.nodeIsURI(selectedNode) &&
|
||||
(doubleClickOnFlatList || middleClick)) {
|
||||
// Open associated uri in the browser.
|
||||
PlacesOrganizer.openSelectedNode(aEvent);
|
||||
}
|
||||
else if (middleClick &&
|
||||
PlacesUtils.nodeIsContainer(selectedNode)) {
|
||||
// The command execution function will take care of seeing if the
|
||||
// selection is a folder or a different container type, and will
|
||||
// load its contents in tabs.
|
||||
PlacesUIUtils.openContainerNodeInTabs(selectedNode, aEvent);
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +464,6 @@
|
||||
flatList="true"
|
||||
enableColumnDrag="true"
|
||||
onkeypress="if (event.keyCode == KeyEvent.DOM_VK_RETURN) PlacesOrganizer.openSelectedNode(event);"
|
||||
ondblclick="PlacesOrganizer.openSelectedNode(event);"
|
||||
onopenflatcontainer="PlacesOrganizer.openFlatContainer(aContainer);"
|
||||
onselect="PlacesOrganizer.onContentTreeSelect();"
|
||||
onfocus="PlacesOrganizer.onTreeFocus(event);"
|
||||
|
@ -43,8 +43,18 @@ const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
// DOM ids of Places sidebar trees.
|
||||
const SIDEBAR_HISTORY_ID = "historyTree";
|
||||
const SIDEBAR_BOOKMARKS_ID = "bookmarks-view";
|
||||
const SIDEBAR_HISTORY_TREE_ID = "historyTree";
|
||||
const SIDEBAR_BOOKMARKS_TREE_ID = "bookmarks-view";
|
||||
|
||||
const SIDEBAR_HISTORY_ID = "viewHistorySidebar";
|
||||
const SIDEBAR_BOOKMARKS_ID = "viewBookmarksSidebar";
|
||||
|
||||
// For history sidebar.
|
||||
const SIDEBAR_HISTORY_BYLASTVISITED_VIEW = "bylastvisited";
|
||||
const SIDEBAR_HISTORY_BYMOSTVISITED_VIEW = "byvisited";
|
||||
const SIDEBAR_HISTORY_BYDATE_VIEW = "byday";
|
||||
const SIDEBAR_HISTORY_BYSITE_VIEW = "bysite";
|
||||
const SIDEBAR_HISTORY_BYDATEANDSITE_VIEW = "bydateandsite";
|
||||
|
||||
// Action to execute on the current node.
|
||||
const ACTION_EDIT = 0;
|
||||
@ -94,7 +104,8 @@ gTests.push({
|
||||
desc: "Bug Description",
|
||||
sidebar: SIDEBAR_BOOKMARKS_ID, // See SIDEBAR_ constants above.
|
||||
action: ACTION_EDIT, // See ACTION_ constants above.
|
||||
itemType: null, // See TYPE_ constants above, required for ACTION_ADD.
|
||||
itemType: null, // See TYPE_ constants above, required for ACTION_ADD, only for Bookmarks sidebar.
|
||||
historyView: SIDEBAR_HISTORY_BYLASTVISITED_VIEW, // See constants above, only for History sidebar.
|
||||
window: null, // Will contain handle of dialog window
|
||||
|
||||
setup: function() {
|
||||
@ -112,7 +123,7 @@ gTests.push({
|
||||
finish: function() {
|
||||
// Close window, toggle sidebar and goto next test.
|
||||
this.window.document.documentElement.cancelDialog();
|
||||
toggleSidebar("viewBookmarksSidebar", false);
|
||||
toggleSidebar(this.sidebar, false);
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
@ -170,7 +181,7 @@ gTests.push({
|
||||
|
||||
finish: function() {
|
||||
this.window.document.documentElement.cancelDialog();
|
||||
toggleSidebar("viewBookmarksSidebar", false);
|
||||
toggleSidebar(this.sidebar, false);
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
@ -265,7 +276,7 @@ gTests.push({
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
toggleSidebar("viewBookmarksSidebar", false);
|
||||
toggleSidebar(this.sidebar, false);
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
@ -323,7 +334,7 @@ gTests.push({
|
||||
|
||||
finish: function() {
|
||||
// Window is already closed.
|
||||
toggleSidebar("viewBookmarksSidebar", false);
|
||||
toggleSidebar(this.sidebar, false);
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
@ -423,7 +434,7 @@ gTests.push({
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
toggleSidebar("viewBookmarksSidebar", false);
|
||||
toggleSidebar(this.sidebar, false);
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
@ -439,6 +450,82 @@ gTests.push({
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Bug 491269 - Test that editing folder name in bookmarks properties dialog does not accept the dialog
|
||||
|
||||
gTests.push({
|
||||
desc: " Bug 491269 - Test that editing folder name in bookmarks properties dialog does not accept the dialog",
|
||||
sidebar: SIDEBAR_HISTORY_ID,
|
||||
action: ACTION_ADD,
|
||||
historyView: SIDEBAR_HISTORY_BYLASTVISITED_VIEW,
|
||||
window: null,
|
||||
|
||||
setup: function() {
|
||||
// Add a visit.
|
||||
add_visit(PlacesUtils._uri(TEST_URL), Date.now() * 1000);
|
||||
// Sanity check.
|
||||
var gh = PlacesUtils.history.QueryInterface(Ci.nsIGlobalHistory2);
|
||||
ok(gh.isVisited(PlacesUtils._uri(TEST_URL)), TEST_URL + " is a visited url.");
|
||||
},
|
||||
|
||||
selectNode: function(tree) {
|
||||
var visitNode = tree.view.nodeForTreeIndex(0);
|
||||
tree.selectNode(visitNode);
|
||||
is(tree.selectedNode.uri, TEST_URL, "The correct visit has been selected");
|
||||
is(tree.selectedNode.itemId, -1, "The selected node is not bookmarked");
|
||||
},
|
||||
|
||||
run: function() {
|
||||
// Open folder selector.
|
||||
var foldersExpander = this.window.document.getElementById("editBMPanel_foldersExpander");
|
||||
var folderTree = this.window.document.getElementById("editBMPanel_folderTree");
|
||||
var self = this;
|
||||
|
||||
var windowObserver = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic === "domwindowclosed") {
|
||||
ww.unregisterNotification(this);
|
||||
ok(self._cleanShutdown,
|
||||
"Dialog window should not be closed by pressing ESC in folder name textbox");
|
||||
self.finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
ww.registerNotification(windowObserver);
|
||||
|
||||
folderTree.addEventListener("DOMAttrModified", function onDOMAttrModified(event) {
|
||||
if (event.attrName != "place")
|
||||
return;
|
||||
folderTree.removeEventListener("DOMAttrModified", arguments.callee, false);
|
||||
executeSoon(function () {
|
||||
// Create a new folder.
|
||||
var newFolderButton = self.window.document.getElementById("editBMPanel_newFolderButton");
|
||||
newFolderButton.doCommand();
|
||||
ok(folderTree.hasAttribute("editing"),
|
||||
"We are editing new folder name in folder tree");
|
||||
|
||||
// Press Escape to discard editing new folder name.
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, self.window);
|
||||
ok(!folderTree.hasAttribute("editing"),
|
||||
"We have finished editing folder name in folder tree");
|
||||
self._cleanShutdown = true;
|
||||
self.window.document.documentElement.cancelDialog();
|
||||
});
|
||||
}, false);
|
||||
foldersExpander.doCommand();
|
||||
},
|
||||
|
||||
finish: function() {
|
||||
toggleSidebar(this.sidebar, false);
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
var bh = PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory);
|
||||
bh.removeAllPages();
|
||||
}
|
||||
});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
function test() {
|
||||
@ -484,14 +571,23 @@ function execute_test_in_sidebar() {
|
||||
// Need to executeSoon since the tree is initialized on sidebar load.
|
||||
executeSoon(open_properties_dialog);
|
||||
}, true);
|
||||
toggleSidebar("viewBookmarksSidebar", true);
|
||||
toggleSidebar(gCurrentTest.sidebar, true);
|
||||
}
|
||||
|
||||
function open_properties_dialog() {
|
||||
var sidebar = document.getElementById("sidebar");
|
||||
|
||||
// If this is history sidebar, set the required view.
|
||||
if (gCurrentTest.sidebar == SIDEBAR_HISTORY_ID)
|
||||
sidebar.contentDocument.getElementById(gCurrentTest.historyView).doCommand();
|
||||
|
||||
// Get sidebar's Places tree.
|
||||
var tree = sidebar.contentDocument.getElementById(gCurrentTest.sidebar);
|
||||
var sidebarTreeID = gCurrentTest.sidebar == SIDEBAR_BOOKMARKS_ID ?
|
||||
SIDEBAR_BOOKMARKS_TREE_ID :
|
||||
SIDEBAR_HISTORY_TREE_ID;
|
||||
var tree = sidebar.contentDocument.getElementById(sidebarTreeID);
|
||||
ok(tree, "Sidebar tree has been loaded");
|
||||
|
||||
// Ask current test to select the node to edit.
|
||||
gCurrentTest.selectNode(tree);
|
||||
ok(tree.selectedNode,
|
||||
@ -528,19 +624,23 @@ function open_properties_dialog() {
|
||||
command = "placesCmd_show:info";
|
||||
break;
|
||||
case ACTION_ADD:
|
||||
if (gCurrentTest.itemType == TYPE_FOLDER)
|
||||
command = "placesCmd_new:folder";
|
||||
else if (gCurrentTest.itemType == TYPE_BOOKMARK)
|
||||
command = "placesCmd_new:bookmark";
|
||||
if (gCurrentTest.sidebar == SIDEBAR_BOOKMARKS_ID) {
|
||||
if (gCurrentTest.itemType == TYPE_FOLDER)
|
||||
command = "placesCmd_new:folder";
|
||||
else if (gCurrentTest.itemType == TYPE_BOOKMARK)
|
||||
command = "placesCmd_new:bookmark";
|
||||
else
|
||||
ok(false, "You didn't set a valid itemType for adding an item");
|
||||
}
|
||||
else
|
||||
ok(false, "You didn't set a valid itemType for adding an item");
|
||||
command = "placesCmd_createBookmark";
|
||||
break;
|
||||
default:
|
||||
ok(false, "You didn't set a valid action for this test");
|
||||
}
|
||||
// Ensure command is enabled for this node.
|
||||
ok(tree.controller.isCommandEnabled(command),
|
||||
"Properties command on current selected node is enabled");
|
||||
" command '" + command + "' on current selected node is enabled");
|
||||
|
||||
// This will open the dialog.
|
||||
tree.controller.doCommand(command);
|
||||
|
@ -58,5 +58,5 @@ that require it. -->
|
||||
mockup at bug 480169 -->
|
||||
<!ENTITY sanitizeEverythingUndoWarning "This action cannot be undone.">
|
||||
|
||||
<!ENTITY dialog.width "32em">
|
||||
<!ENTITY dialog.width "28em">
|
||||
<!ENTITY column.width "14em">
|
||||
|
@ -157,18 +157,6 @@ radio[pane=paneAdvanced] {
|
||||
/**
|
||||
* Clear Private Data
|
||||
*/
|
||||
#SanitizeDurationBox {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#sanitizeDurationChoice {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sanitizeDurationLabel {
|
||||
-moz-margin-start: 3px;
|
||||
}
|
||||
|
||||
#SanitizeDialogPane > groupbox {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
#sanitizeDurationChoice {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
/* Align the duration label with the warning box and item list */
|
||||
#sanitizeDurationLabel {
|
||||
-moz-margin-start: 3px;
|
||||
}
|
||||
|
||||
|
||||
/* Hide the duration dropdown suffix label if it's empty. Otherwise it
|
||||
takes up a little space, causing the end of the dropdown to not be aligned
|
||||
with the warning box. */
|
||||
@ -32,9 +42,8 @@
|
||||
}
|
||||
|
||||
#sanitizeEverythingWarningDescBox {
|
||||
padding: 0;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
|
||||
@ -43,8 +52,8 @@
|
||||
padding: 0;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
margin-left: -6px;
|
||||
margin-right: 0;
|
||||
-moz-margin-start: -6px;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.expander-up,
|
||||
@ -73,16 +82,15 @@
|
||||
|
||||
/* Make the item list the same width as the warning box */
|
||||
#itemList {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
-moz-margin-start: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Make the rightmost dialog button ("accept") align wih right side of the
|
||||
warning box */
|
||||
/* Align the last dialog button with the end of the warning box */
|
||||
.prefWindow-dlgbuttons {
|
||||
margin-right: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
.dialog-button[dlgtype="accept"] {
|
||||
margin-right: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
@ -276,18 +276,6 @@ caption {
|
||||
/**
|
||||
* Clear Private Data
|
||||
*/
|
||||
#SanitizeDurationBox {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#sanitizeDurationChoice {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sanitizeDurationLabel {
|
||||
-moz-margin-start: 3px;
|
||||
}
|
||||
|
||||
#SanitizeDialogPane > groupbox {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
@ -1,3 +1,9 @@
|
||||
/* Align the duration label with the warning box and item list */
|
||||
#sanitizeDurationLabel {
|
||||
-moz-margin-start: 1px;
|
||||
}
|
||||
|
||||
|
||||
/* Hide the duration dropdown suffix label if it's empty. Otherwise it
|
||||
takes up a little space, causing the end of the dropdown to not be aligned
|
||||
with the warning box. */
|
||||
@ -32,9 +38,8 @@
|
||||
}
|
||||
|
||||
#sanitizeEverythingWarningDescBox {
|
||||
padding: 0;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
|
||||
@ -43,8 +48,8 @@
|
||||
padding: 0;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
margin-left: -2px;
|
||||
margin-right: 0;
|
||||
-moz-margin-start: -2px;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.expander-up,
|
||||
@ -74,16 +79,15 @@
|
||||
|
||||
/* Make the item list the same width as the warning box */
|
||||
#itemList {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
-moz-margin-start: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Make the rightmost dialog button ("accept") align wih right side of the
|
||||
warning box */
|
||||
/* Align the last dialog button with the end of the warning box */
|
||||
.prefWindow-dlgbuttons {
|
||||
margin-right: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
.dialog-button[dlgtype="accept"] {
|
||||
margin-right: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
@ -177,18 +177,6 @@ radio[pane=paneAdvanced][selected="true"] {
|
||||
/**
|
||||
* Clear Private Data
|
||||
*/
|
||||
#SanitizeDurationBox {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
#sanitizeDurationChoice {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sanitizeDurationLabel {
|
||||
-moz-margin-start: 3px;
|
||||
}
|
||||
|
||||
#SanitizeDialogPane > groupbox {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
#sanitizeDurationChoice {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
/* Align the duration label with the warning box and item list */
|
||||
#sanitizeDurationLabel {
|
||||
-moz-margin-start: 3px;
|
||||
}
|
||||
|
||||
|
||||
/* Hide the duration dropdown suffix label if it's empty. Otherwise it
|
||||
takes up a little space, causing the end of the dropdown to not be aligned
|
||||
with the warning box. */
|
||||
@ -32,9 +42,8 @@
|
||||
}
|
||||
|
||||
#sanitizeEverythingWarningDescBox {
|
||||
padding: 0;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
|
||||
@ -43,8 +52,8 @@
|
||||
padding: 0;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
margin-left: -1px;
|
||||
margin-right: 0;
|
||||
-moz-margin-start: -1px;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
.expander-up,
|
||||
@ -69,16 +78,15 @@
|
||||
|
||||
/* Make the item list the same width as the warning box */
|
||||
#itemList {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
-moz-margin-start: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Make the rightmost dialog button ("cancel") align wih right side of the
|
||||
warning box */
|
||||
/* Align the last dialog button with the end of the warning box */
|
||||
.prefWindow-dlgbuttons {
|
||||
margin-right: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
.dialog-button[dlgtype="cancel"] {
|
||||
margin-right: 0;
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
@ -1,228 +0,0 @@
|
||||
#!perl -w
|
||||
package Moz::MacCVS;
|
||||
|
||||
# package Mac::Apps::MacCVS; this should really be the name of the package
|
||||
# but due to our directory hierarchy in mozilla, I am not doing it
|
||||
|
||||
require 5.004;
|
||||
require Exporter;
|
||||
|
||||
use strict;
|
||||
use Exporter;
|
||||
|
||||
use vars qw($VERSION @ISA @EXPORT);
|
||||
|
||||
use Cwd;
|
||||
|
||||
use File::Basename;
|
||||
|
||||
use Mac::StandardFile;
|
||||
use Mac::AppleEvents;
|
||||
use Mac::AppleEvents::Simple;
|
||||
|
||||
@ISA = qw(Exporter);
|
||||
@EXPORT = qw(new describe checkout update);
|
||||
$VERSION = "1.00";
|
||||
|
||||
# If you want to understand the gobbldeygook that's used to build Apple Events,
|
||||
# you should start by reading the AEGizmos documentation.
|
||||
|
||||
|
||||
# Architecture:
|
||||
# cvs session object:
|
||||
# name - session name
|
||||
# session_file - session file
|
||||
#
|
||||
#
|
||||
|
||||
my($last_error) = 0;
|
||||
my($gAppSig) = 'Mcvs'; # MacCVS Pro
|
||||
|
||||
#
|
||||
# utility routines
|
||||
#
|
||||
|
||||
|
||||
sub _checkForEventError($)
|
||||
{
|
||||
my($evt) = @_;
|
||||
|
||||
if ($evt->{ERRNO} != 0)
|
||||
{
|
||||
print STDERR "Error. Script returned '$evt->{ERROR} (error $evt->{ERRNO})\n";
|
||||
$last_error = $evt->{ERRNO};
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1; # success
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Session object methods
|
||||
#
|
||||
|
||||
sub new
|
||||
{
|
||||
my ( $proto, $session_file) = @_;
|
||||
my $class = ref($proto) || $proto;
|
||||
my $self = {};
|
||||
|
||||
if ( defined($session_file) && ( -e $session_file) )
|
||||
{
|
||||
$self->{"name"} = basename( $session_file );
|
||||
$self->{"session_file"} = $session_file;
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
else
|
||||
{
|
||||
print STDERR "MacCVS->new cvs file < $session_file > does not exist\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# makes sure that the session is open
|
||||
# assertSessionOpen()
|
||||
# returns 1 on success
|
||||
sub assertSessionOpen()
|
||||
{
|
||||
my ($self) = shift;
|
||||
|
||||
$last_error = 0;
|
||||
|
||||
my($prm) =
|
||||
q"'----':obj {form:name, want:type(alis), seld:TEXT(@), from:'null'()}";
|
||||
|
||||
my($evt) = do_event(qw/aevt odoc/, $gAppSig, $prm, $self->{session_file});
|
||||
return _checkForEventError($evt);
|
||||
}
|
||||
|
||||
# prints the cvs object, used mostly for debugging
|
||||
sub describe
|
||||
{
|
||||
my($self) = shift;
|
||||
$last_error = 0;
|
||||
print "MacCVS:: name: ", $self->{name}, " session file: ", $self->{session_file}, "\n";
|
||||
}
|
||||
|
||||
# checkout( self, module, revision, date)
|
||||
# MacCVS checkout command
|
||||
# returns 1 on success.
|
||||
sub checkout()
|
||||
{
|
||||
my($self, $module, $revision, $date ) = @_;
|
||||
unless( defined ($module) ) { $module = ""; } # get rid of the pesky undefined warnings
|
||||
unless( defined ($revision) ) { $revision = ""; }
|
||||
unless( defined ($date) ) { $date = ""; }
|
||||
|
||||
$last_error = 0;
|
||||
|
||||
$self->assertSessionOpen() || die "Error: failed to open MacCVS session file at $self->{session_file}\n";
|
||||
|
||||
my($revstring) = ($revision ne "") ? $revision : "(none)";
|
||||
my($datestring) = ($date ne "") ? $date : "(none)";
|
||||
|
||||
print "Checking out $module with revision $revstring, date $datestring\n";
|
||||
|
||||
my($prm) =
|
||||
q"'----':obj {form:name, want:type(docu), seld:TEXT(@), from:'null'()}, ".
|
||||
q"modl:'TEXT'(@), tagr:'TEXT'(@), tagd:'TEXT'(@) ";
|
||||
|
||||
my($evt) = do_event(qw/MCvs cout/, $gAppSig, $prm, $self->{name}, $module, $revision, $date);
|
||||
return _checkForEventError($evt);
|
||||
}
|
||||
|
||||
|
||||
# update( self, branch tag, list of paths)
|
||||
# MacCVS udate command
|
||||
# returns 1 on success.
|
||||
# NOTE: MacCVS Pro does not correctly support this stuff yet (as of version 2.7d5).
|
||||
sub update()
|
||||
{
|
||||
my($self, $branch, $paths ) = @_;
|
||||
|
||||
$last_error = 0;
|
||||
|
||||
$self->assertSessionOpen() || die "Error: failed to open MacCVS session file at $self->{session_file}\n";
|
||||
|
||||
if ($branch eq "HEAD") {
|
||||
$branch = "";
|
||||
}
|
||||
|
||||
my($paths_list) = "";
|
||||
|
||||
my($path);
|
||||
foreach $path (@$paths)
|
||||
{
|
||||
if ($paths_list ne "") {
|
||||
$paths_list = $paths_list.", ";
|
||||
}
|
||||
|
||||
$paths_list = $paths_list."Ò".$path."Ó";
|
||||
}
|
||||
|
||||
my($prm) =
|
||||
q"'----':obj {form:name, want:type(docu), seld:TEXT(@), from:'null'()}, ".
|
||||
q"tagr:'TEXT'(@), tFls:[";
|
||||
|
||||
$prm = $prm.$paths_list."]";
|
||||
|
||||
my($evt) = do_event(qw/MCvs updt/, $gAppSig, $prm, $self->{name}, $branch);
|
||||
return _checkForEventError($evt);
|
||||
};
|
||||
|
||||
|
||||
sub getLastError()
|
||||
{
|
||||
return $last_error;
|
||||
}
|
||||
|
||||
1;
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
MacCVS - Interface to MacCVS
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use MacCVS;
|
||||
$session = MacCVS->new( <session_file_path>) || die "cannot create session";
|
||||
$session->checkout([module] [revision] [date]) || die "Could not check out";
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a MacCVS interface for talking to MacCVS Pro client.
|
||||
MacCVSSession is the class used to manipulate the session
|
||||
|
||||
=item new
|
||||
MacCVS->new( <cvs session file path>);
|
||||
|
||||
Creates a new session. Returns undef on failure.
|
||||
|
||||
=item checkout( <module> [revision] [date] )
|
||||
|
||||
cvs checkout command. Revision and date are optional
|
||||
returns 0 on failure
|
||||
|
||||
=cut
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=over
|
||||
|
||||
=item MacCVS Home Page
|
||||
|
||||
http://www.maccvs.org/
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Aleks Totic atotic@netscape.com
|
||||
Simon Fraser sfraser@netscape.com
|
||||
|
||||
=cut
|
||||
|
||||
__END__
|
@ -529,13 +529,36 @@ private:
|
||||
nsresult
|
||||
SavePrincipal(nsIPrincipal* aToSave);
|
||||
|
||||
/**
|
||||
* Check capability levels for an |aObj| that implements
|
||||
* nsISecurityCheckedComponent.
|
||||
*
|
||||
* NB: This function also checks to see if aObj is a plugin and the user
|
||||
* has set the "security.xpconnect.plugin.unrestricted" pref to allow
|
||||
* anybody to script plugin objects from anywhere.
|
||||
*
|
||||
* @param aObj The nsISupports representation of the object in question
|
||||
* object, possibly null.
|
||||
* @param aJSObject The JSObject representation of the object in question.
|
||||
* Only used if |aObjectSecurityLevel| is "sameOrigin".
|
||||
* @param aSubjectPrincipal The nominal subject principal used when
|
||||
* aObjectSecurityLevel is "sameOrigin".
|
||||
* @param aObjectSecurityLevel Can be one of three values:
|
||||
* - allAccess: Allow access no matter what.
|
||||
* - noAccess: Deny access no matter what.
|
||||
* - sameOrigin: If both a subject principal and JS
|
||||
* object have been passed in, returns
|
||||
* true if the subject subsumes the object,
|
||||
* otherwise, behaves like noAccess.
|
||||
*/
|
||||
nsresult
|
||||
CheckXPCPermissions(nsISupports* aObj,
|
||||
CheckXPCPermissions(nsISupports* aObj, JSObject* aJSObject,
|
||||
nsIPrincipal* aSubjectPrincipal,
|
||||
const char* aObjectSecurityLevel);
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
|
||||
nsresult
|
||||
InitPrefs();
|
||||
|
||||
|
@ -784,7 +784,8 @@ nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = CheckXPCPermissions(aObj, objectSecurityLevel);
|
||||
rv = CheckXPCPermissions(aObj, aJSObject, subjectPrincipal,
|
||||
objectSecurityLevel);
|
||||
#ifdef DEBUG_CAPS_CheckPropertyAccessImpl
|
||||
if(NS_SUCCEEDED(rv))
|
||||
printf("CheckXPCPerms GRANTED.\n");
|
||||
@ -2858,7 +2859,7 @@ nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
|
||||
if (checkedComponent)
|
||||
checkedComponent->CanCreateWrapper((nsIID *)&aIID, getter_Copies(objectSecurityLevel));
|
||||
|
||||
nsresult rv = CheckXPCPermissions(aObj, objectSecurityLevel);
|
||||
nsresult rv = CheckXPCPermissions(aObj, nsnull, nsnull, objectSecurityLevel);
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
//-- Access denied, report an error
|
||||
@ -2969,7 +2970,7 @@ nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
|
||||
nsCRT::free(cidStr);
|
||||
#endif
|
||||
|
||||
nsresult rv = CheckXPCPermissions(nsnull, nsnull);
|
||||
nsresult rv = CheckXPCPermissions(nsnull, nsnull, nsnull, nsnull);
|
||||
if (NS_FAILED(rv))
|
||||
#ifdef XPC_IDISPATCH_SUPPORT
|
||||
{
|
||||
@ -3006,7 +3007,7 @@ nsScriptSecurityManager::CanGetService(JSContext *cx,
|
||||
nsCRT::free(cidStr);
|
||||
#endif
|
||||
|
||||
nsresult rv = CheckXPCPermissions(nsnull, nsnull);
|
||||
nsresult rv = CheckXPCPermissions(nsnull, nsnull, nsnull, nsnull);
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
//-- Access denied, report an error
|
||||
@ -3045,7 +3046,8 @@ nsScriptSecurityManager::CanAccess(PRUint32 aAction,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScriptSecurityManager::CheckXPCPermissions(nsISupports* aObj,
|
||||
nsScriptSecurityManager::CheckXPCPermissions(nsISupports* aObj, JSObject* aJSObject,
|
||||
nsIPrincipal* aSubjectPrincipal,
|
||||
const char* aObjectSecurityLevel)
|
||||
{
|
||||
//-- Check for the all-powerful UniversalXPConnect privilege
|
||||
@ -3058,6 +3060,22 @@ nsScriptSecurityManager::CheckXPCPermissions(nsISupports* aObj,
|
||||
{
|
||||
if (PL_strcasecmp(aObjectSecurityLevel, "allAccess") == 0)
|
||||
return NS_OK;
|
||||
if (aSubjectPrincipal && aJSObject &&
|
||||
PL_strcasecmp(aObjectSecurityLevel, "sameOrigin") == 0)
|
||||
{
|
||||
nsIPrincipal* objectPrincipal = doGetObjectPrincipal(aJSObject);
|
||||
|
||||
// Only do anything if we have both a subject and object
|
||||
// principal.
|
||||
if (objectPrincipal)
|
||||
{
|
||||
PRBool subsumes;
|
||||
nsresult rv = aSubjectPrincipal->Subsumes(objectPrincipal, &subsumes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (subsumes)
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
else if (PL_strcasecmp(aObjectSecurityLevel, "noAccess") != 0)
|
||||
{
|
||||
PRBool canAccess = PR_FALSE;
|
||||
|
@ -1490,6 +1490,9 @@ public:
|
||||
|
||||
// Returns PR_FALSE if something erroneous happened.
|
||||
PRBool Push(nsPIDOMEventTarget *aCurrentTarget);
|
||||
// If nothing has been pushed to stack, this works like Push.
|
||||
// Otherwise if context will change, Pop and Push will be called.
|
||||
PRBool RePush(nsPIDOMEventTarget *aCurrentTarget);
|
||||
// If a null JSContext is passed to Push(), that will cause no
|
||||
// push to happen and false to be returned.
|
||||
PRBool Push(JSContext *cx);
|
||||
|
@ -2736,6 +2736,34 @@ nsCxPusher::Push(nsPIDOMEventTarget *aCurrentTarget)
|
||||
return Push(cx);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsCxPusher::RePush(nsPIDOMEventTarget *aCurrentTarget)
|
||||
{
|
||||
if (!mPushedSomething) {
|
||||
return Push(aCurrentTarget);
|
||||
}
|
||||
|
||||
if (aCurrentTarget) {
|
||||
nsresult rv;
|
||||
nsIScriptContext* scx =
|
||||
aCurrentTarget->GetContextForEventHandlers(&rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
Pop();
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
// If we have the same script context and native context is still
|
||||
// alive, no need to Pop/Push.
|
||||
if (scx && scx == mScx &&
|
||||
scx->GetNativeContext()) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Pop();
|
||||
return Push(aCurrentTarget);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsCxPusher::Push(JSContext *cx)
|
||||
{
|
||||
@ -3737,7 +3765,7 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
|
||||
// i is unsigned, so i >= is always true
|
||||
for (PRUint32 i = 0; i < childCount; ++i) {
|
||||
nsIContent* child = aContent->GetChildAt(removeIndex);
|
||||
if (removeIndex == 0 && child->IsNodeOfType(nsINode::eTEXT)) {
|
||||
if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
|
||||
nsresult rv = child->SetText(aValue, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsISelection2.h"
|
||||
#include "nsIMEStateManager.h"
|
||||
|
||||
@ -730,6 +731,46 @@ nsContentEventHandler::OnQuerySelectionAsTransferable(nsQueryContentEvent* aEven
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
nsresult rv = Init(aEvent);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIFrame* rootFrame = mPresShell->GetRootFrame();
|
||||
nsPoint ptInRoot =
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame);
|
||||
nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
|
||||
if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame) {
|
||||
// there is no character at the point.
|
||||
aEvent->mReply.mOffset = nsQueryContentEvent::NOT_FOUND;
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
nsPoint ptInTarget = ptInRoot - targetFrame->GetOffsetTo(rootFrame);
|
||||
nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame);
|
||||
nsIFrame::ContentOffsets offsets =
|
||||
textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
|
||||
NS_ENSURE_TRUE(offsets.content, NS_ERROR_FAILURE);
|
||||
PRUint32 nativeOffset;
|
||||
rv = GetFlatTextOffsetOfRange(mRootContent, offsets.content, offsets.offset,
|
||||
&nativeOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsQueryContentEvent textRect(PR_TRUE, NS_QUERY_TEXT_RECT, aEvent->widget);
|
||||
textRect.InitForQueryTextRect(nativeOffset, 1);
|
||||
rv = OnQueryTextRect(&textRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
|
||||
|
||||
// currently, we don't need to get the actual text.
|
||||
aEvent->mReply.mOffset = nativeOffset;
|
||||
aEvent->mReply.mRect = textRect.mReply.mRect;
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
|
||||
nsINode* aNode,
|
||||
|
@ -81,6 +81,8 @@ public:
|
||||
nsresult OnQueryContentState(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_SELECTION_AS_TRANSFERABLE event handler
|
||||
nsresult OnQuerySelectionAsTransferable(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_CHARACTER_AT_POINT event handler
|
||||
nsresult OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent);
|
||||
|
||||
// NS_SELECTION_* event
|
||||
nsresult OnSelectionEvent(nsSelectionEvent* aEvent);
|
||||
|
@ -1082,10 +1082,7 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
|
||||
}
|
||||
}
|
||||
|
||||
// nsCxPusher will push and pop (automatically) the current cx onto the
|
||||
// context stack
|
||||
nsCxPusher pusher;
|
||||
if (NS_SUCCEEDED(result) && pusher.Push(aCurrentTarget)) {
|
||||
if (NS_SUCCEEDED(result)) {
|
||||
// nsIDOMEvent::currentTarget is set in nsEventDispatcher.
|
||||
result = aListener->HandleEvent(aDOMEvent);
|
||||
}
|
||||
@ -1157,6 +1154,7 @@ found:
|
||||
nsAutoTObserverArray<nsListenerStruct, 2>::EndLimitedIterator iter(mListeners);
|
||||
nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
|
||||
PRBool hasListener = PR_FALSE;
|
||||
nsCxPusher pusher;
|
||||
while (iter.HasMore()) {
|
||||
nsListenerStruct* ls = &iter.GetNext();
|
||||
PRBool useTypeInterface =
|
||||
@ -1180,9 +1178,11 @@ found:
|
||||
if (*aDOMEvent) {
|
||||
nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = ls->mListener;
|
||||
if (useTypeInterface) {
|
||||
pusher.Pop();
|
||||
DispatchToInterface(*aDOMEvent, ls->mListener,
|
||||
dispData->method, *typeData->iid);
|
||||
} else if (useGenericInterface) {
|
||||
} else if (useGenericInterface &&
|
||||
pusher.RePush(aCurrentTarget)) {
|
||||
HandleEventSubType(ls, ls->mListener, *aDOMEvent,
|
||||
aCurrentTarget, aFlags);
|
||||
}
|
||||
|
@ -1758,6 +1758,12 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
||||
handler.OnQuerySelectionAsTransferable(static_cast<nsQueryContentEvent*>(aEvent));
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_CHARACTER_AT_POINT:
|
||||
{
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnQueryCharacterAtPoint(static_cast<nsQueryContentEvent*>(aEvent));
|
||||
}
|
||||
break;
|
||||
case NS_SELECTION_SET:
|
||||
{
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
|
@ -172,11 +172,15 @@ void nsHTMLMediaElement::QueueLoadFromSourceTask()
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener
|
||||
class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener,
|
||||
public nsIChannelEventSink,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
public:
|
||||
MediaLoadListener(nsHTMLMediaElement* aElement)
|
||||
@ -190,7 +194,9 @@ private:
|
||||
nsCOMPtr<nsIStreamListener> mNextListener;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver, nsIStreamListener)
|
||||
NS_IMPL_ISUPPORTS4(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver,
|
||||
nsIStreamListener, nsIChannelEventSink,
|
||||
nsIInterfaceRequestor)
|
||||
|
||||
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
@ -252,6 +258,21 @@ NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest*
|
||||
return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIChannelEventSink> sink = do_QueryInterface(mNextListener);
|
||||
if (sink)
|
||||
return sink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
|
||||
|
||||
@ -513,8 +534,10 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI)
|
||||
// The listener holds a strong reference to us. This creates a reference
|
||||
// cycle which is manually broken in the listener's OnStartRequest method
|
||||
// after it is finished with the element.
|
||||
nsCOMPtr<nsIStreamListener> loadListener = new MediaLoadListener(this);
|
||||
nsRefPtr<MediaLoadListener> loadListener = new MediaLoadListener(this);
|
||||
if (!loadListener) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mChannel->SetNotificationCallbacks(loadListener);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
if (ShouldCheckAllowOrigin()) {
|
||||
|
@ -89,6 +89,16 @@ class nsAudioStream
|
||||
// Block until buffered audio data has been consumed.
|
||||
void Drain();
|
||||
|
||||
// Pause audio playback
|
||||
void Pause();
|
||||
|
||||
// Resume audio playback
|
||||
void Resume();
|
||||
|
||||
// Return the position in seconds of the sample being played by the
|
||||
// audio hardware.
|
||||
float GetPosition();
|
||||
|
||||
private:
|
||||
double mVolume;
|
||||
void* mAudioHandle;
|
||||
|
@ -41,6 +41,8 @@
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
/**
|
||||
* Media applications want fast, "on demand" random access to media data,
|
||||
@ -186,6 +188,13 @@
|
||||
* we must not acquire any nsMediaDecoder locks or nsMediaStream locks
|
||||
* while holding the nsMediaCache lock. But it's OK to hold those locks
|
||||
* and then get the nsMediaCache lock.
|
||||
*
|
||||
* nsMediaCache associates a principal with each stream. CacheClientSeek
|
||||
* can trigger new HTTP requests; due to redirects to other domains,
|
||||
* each HTTP load can return data with a different principal. This
|
||||
* principal must be passed to NotifyDataReceived, and nsMediaCache
|
||||
* will detect when different principals are associated with data in the
|
||||
* same stream, and replace them with a null principal.
|
||||
*/
|
||||
class nsMediaCache;
|
||||
// defined in nsMediaStream.h
|
||||
@ -215,7 +224,8 @@ public:
|
||||
mStreamOffset(0), mStreamLength(-1), mPlaybackBytesPerSecond(10000),
|
||||
mPinCount(0), mCurrentMode(MODE_PLAYBACK), mClosed(PR_FALSE),
|
||||
mIsSeekable(PR_FALSE), mCacheSuspended(PR_FALSE),
|
||||
mMetadataInPartialBlockBuffer(PR_FALSE) {}
|
||||
mMetadataInPartialBlockBuffer(PR_FALSE),
|
||||
mUsingNullPrincipal(PR_FALSE) {}
|
||||
~nsMediaCacheStream();
|
||||
|
||||
// Set up this stream with the cache. Can fail on OOM. Must be called
|
||||
@ -236,6 +246,8 @@ public:
|
||||
void Close();
|
||||
// This returns true when the stream has been closed
|
||||
PRBool IsClosed() const { return mClosed; }
|
||||
// Get the principal for this stream.
|
||||
nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; }
|
||||
|
||||
// These callbacks are called on the main thread by the client
|
||||
// when data has been received via the channel.
|
||||
@ -263,7 +275,9 @@ public:
|
||||
// the starting offset is known via NotifyDataStarted or because
|
||||
// the cache requested the offset in
|
||||
// nsMediaChannelStream::CacheClientSeek, or because it defaulted to 0.
|
||||
void NotifyDataReceived(PRInt64 aSize, const char* aData);
|
||||
// We pass in the principal that was used to load this data.
|
||||
void NotifyDataReceived(PRInt64 aSize, const char* aData,
|
||||
nsIPrincipal* aPrincipal);
|
||||
// Notifies the cache that the channel has closed with the given status.
|
||||
void NotifyDataEnded(nsresult aStatus);
|
||||
|
||||
@ -363,9 +377,12 @@ private:
|
||||
// This is used to NotifyAll to wake up threads that might be
|
||||
// blocked on reading from this stream.
|
||||
void CloseInternal(nsAutoMonitor* aMonitor);
|
||||
// Update mPrincipal given that data has been received from aPrincipal
|
||||
void UpdatePrincipal(nsIPrincipal* aPrincipal);
|
||||
|
||||
// This field is main-thread-only.
|
||||
nsMediaChannelStream* mClient;
|
||||
// These fields are main-thread-only.
|
||||
nsMediaChannelStream* mClient;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// All other fields are all protected by the cache's monitor and
|
||||
// can be accessed by by any thread.
|
||||
@ -401,6 +418,9 @@ private:
|
||||
PRPackedBool mCacheSuspended;
|
||||
// true if some data in mPartialBlockBuffer has been read as metadata
|
||||
PRPackedBool mMetadataInPartialBlockBuffer;
|
||||
// true if mPrincipal is a null principal because we saw data from
|
||||
// multiple origins
|
||||
PRPackedBool mUsingNullPrincipal;
|
||||
|
||||
// Data received for the block containing mChannelOffset. Data needs
|
||||
// to wait here so we can write back a complete block. The first
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "prlock.h"
|
||||
#include "nsMediaCache.h"
|
||||
#include "nsTimeStamp.h"
|
||||
@ -149,8 +151,6 @@ public:
|
||||
}
|
||||
|
||||
// The following can be called on the main thread only:
|
||||
// Get the current principal for the channel
|
||||
already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
|
||||
// Get the decoder
|
||||
nsMediaDecoder* Decoder() { return mDecoder; }
|
||||
// Close the stream, stop any listeners, channels, etc.
|
||||
@ -161,6 +161,8 @@ public:
|
||||
virtual void Suspend() = 0;
|
||||
// Resume any downloads that have been suspended.
|
||||
virtual void Resume() = 0;
|
||||
// Get the current principal for the channel
|
||||
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
|
||||
|
||||
// These methods are called off the main thread.
|
||||
// The mode is initially MODE_PLAYBACK.
|
||||
@ -317,6 +319,7 @@ public:
|
||||
virtual nsresult Close();
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
|
||||
// Return PR_TRUE if the stream has been closed.
|
||||
PRBool IsClosed() const { return mCacheStream.IsClosed(); }
|
||||
|
||||
@ -337,13 +340,18 @@ public:
|
||||
virtual PRBool IsSuspendedByCache();
|
||||
|
||||
protected:
|
||||
class Listener : public nsIStreamListener {
|
||||
class Listener : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink
|
||||
{
|
||||
public:
|
||||
Listener(nsMediaChannelStream* aStream) : mStream(aStream) {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
void Revoke() { mStream = nsnull; }
|
||||
|
||||
@ -358,10 +366,12 @@ protected:
|
||||
nsresult OnDataAvailable(nsIRequest* aRequest,
|
||||
nsIInputStream* aStream,
|
||||
PRUint32 aCount);
|
||||
nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, PRUint32 aFlags);
|
||||
|
||||
// Opens the channel, using an HTTP byte range request to start at aOffset
|
||||
// if possible. Main thread only.
|
||||
nsresult OpenChannel(nsIStreamListener** aStreamListener, PRInt64 aOffset);
|
||||
void SetupChannelHeaders();
|
||||
// Closes the channel. Main thread only.
|
||||
void CloseChannel();
|
||||
|
||||
@ -373,9 +383,9 @@ protected:
|
||||
PRUint32 *aWriteCount);
|
||||
|
||||
// Main thread access only
|
||||
PRInt64 mLastSeekOffset;
|
||||
nsRefPtr<Listener> mListener;
|
||||
PRUint32 mSuspendCount;
|
||||
PRPackedBool mSeeking;
|
||||
|
||||
// Any thread access
|
||||
nsMediaCacheStream mCacheStream;
|
||||
|
@ -190,7 +190,9 @@ PRInt32 nsAudioStream::Available()
|
||||
return FAKE_BUFFER_SIZE;
|
||||
|
||||
size_t s = 0;
|
||||
sa_stream_get_write_size(static_cast<sa_stream_t*>(mAudioHandle), &s);
|
||||
if (sa_stream_get_write_size(static_cast<sa_stream_t*>(mAudioHandle), &s) != SA_SUCCESS)
|
||||
return 0;
|
||||
|
||||
return s / sizeof(short);
|
||||
}
|
||||
|
||||
@ -218,3 +220,35 @@ void nsAudioStream::Drain()
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void nsAudioStream::Pause()
|
||||
{
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
sa_stream_pause(static_cast<sa_stream_t*>(mAudioHandle));
|
||||
}
|
||||
|
||||
void nsAudioStream::Resume()
|
||||
{
|
||||
if (!mAudioHandle)
|
||||
return;
|
||||
|
||||
sa_stream_resume(static_cast<sa_stream_t*>(mAudioHandle));
|
||||
}
|
||||
|
||||
float nsAudioStream::GetPosition()
|
||||
{
|
||||
if (!mAudioHandle)
|
||||
return -1.0;
|
||||
|
||||
PRInt64 position = 0;
|
||||
if (sa_stream_get_position(static_cast<sa_stream_t*>(mAudioHandle),
|
||||
SA_POSITION_WRITE_SOFTWARE,
|
||||
&position) == SA_SUCCESS) {
|
||||
return (position / float(mRate) / mChannels / sizeof(short));
|
||||
}
|
||||
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
|
@ -1390,10 +1390,47 @@ nsMediaCacheStream::NotifyDataStarted(PRInt64 aOffset)
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData)
|
||||
nsMediaCacheStream::UpdatePrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
if (!mPrincipal) {
|
||||
NS_ASSERTION(!mUsingNullPrincipal, "Are we using a null principal or not?");
|
||||
if (mUsingNullPrincipal) {
|
||||
// Don't let mPrincipal be set to anything
|
||||
return;
|
||||
}
|
||||
mPrincipal = aPrincipal;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPrincipal == aPrincipal) {
|
||||
// Common case
|
||||
NS_ASSERTION(!mUsingNullPrincipal, "We can't receive data from a null principal");
|
||||
return;
|
||||
}
|
||||
if (mUsingNullPrincipal) {
|
||||
// We've already fallen back to a null principal, so nothing more
|
||||
// to do.
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool equal;
|
||||
nsresult rv = mPrincipal->Equals(aPrincipal, &equal);
|
||||
if (NS_SUCCEEDED(rv) && equal)
|
||||
return;
|
||||
|
||||
// Principals are not equal, so set mPrincipal to a null principal.
|
||||
mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
|
||||
mUsingNullPrincipal = PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
UpdatePrincipal(aPrincipal);
|
||||
|
||||
nsAutoMonitor mon(gMediaCache->Monitor());
|
||||
PRInt64 size = aSize;
|
||||
const char* data = aData;
|
||||
|
@ -65,7 +65,7 @@ using mozilla::TimeStamp;
|
||||
nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder,
|
||||
nsIChannel* aChannel, nsIURI* aURI)
|
||||
: nsMediaStream(aDecoder, aChannel, aURI),
|
||||
mSuspendCount(0), mSeeking(PR_FALSE),
|
||||
mLastSeekOffset(0), mSuspendCount(0),
|
||||
mCacheStream(this),
|
||||
mLock(nsAutoLock::NewLock("media.channel.stream")),
|
||||
mCacheSuspendCount(0)
|
||||
@ -89,7 +89,9 @@ nsMediaChannelStream::~nsMediaChannelStream()
|
||||
// disconnect the old listener from the nsMediaChannelStream and hook up
|
||||
// a new listener, so notifications from the old channel are discarded
|
||||
// and don't confuse us.
|
||||
NS_IMPL_ISUPPORTS2(nsMediaChannelStream::Listener, nsIRequestObserver, nsIStreamListener)
|
||||
NS_IMPL_ISUPPORTS4(nsMediaChannelStream::Listener,
|
||||
nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
|
||||
nsIInterfaceRequestor)
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::Listener::OnStartRequest(nsIRequest* aRequest,
|
||||
@ -122,6 +124,22 @@ nsMediaChannelStream::Listener::OnDataAvailable(nsIRequest* aRequest,
|
||||
return mStream->OnDataAvailable(aRequest, aStream, aCount);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::Listener::OnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
if (!mStream)
|
||||
return NS_OK;
|
||||
return mStream->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::Listener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
{
|
||||
@ -148,7 +166,7 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
ranges);
|
||||
PRBool acceptsRanges = ranges.EqualsLiteral("bytes");
|
||||
|
||||
if (!mSeeking) {
|
||||
if (mLastSeekOffset == 0) {
|
||||
// Look for duration headers from known Ogg content systems. In the case
|
||||
// of multiple options for obtaining the duration the order of precedence is;
|
||||
// 1) The Media resource metadata if possible (done by the decoder itself).
|
||||
@ -172,12 +190,12 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
|
||||
PRUint32 responseStatus = 0;
|
||||
hc->GetResponseStatus(&responseStatus);
|
||||
if (mSeeking && responseStatus == HTTP_OK_CODE) {
|
||||
if (mLastSeekOffset > 0 && responseStatus == HTTP_OK_CODE) {
|
||||
// If we get an OK response but we were seeking, we have to assume
|
||||
// that seeking doesn't work. We also need to tell the cache that
|
||||
// it's getting data for the start of the stream.
|
||||
mCacheStream.NotifyDataStarted(0);
|
||||
} else if (!mSeeking &&
|
||||
} else if (mLastSeekOffset == 0 &&
|
||||
(responseStatus == HTTP_OK_CODE ||
|
||||
responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
|
||||
// We weren't seeking and got a valid response status,
|
||||
@ -243,6 +261,20 @@ nsMediaChannelStream::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
mChannel = aNew;
|
||||
SetupChannelHeaders();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct CopySegmentClosure {
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsMediaChannelStream* mStream;
|
||||
};
|
||||
|
||||
NS_METHOD
|
||||
nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream,
|
||||
void *aClosure,
|
||||
@ -251,8 +283,9 @@ nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream,
|
||||
PRUint32 aCount,
|
||||
PRUint32 *aWriteCount)
|
||||
{
|
||||
nsMediaChannelStream* stream = static_cast<nsMediaChannelStream*>(aClosure);
|
||||
stream->mCacheStream.NotifyDataReceived(aCount, aFromSegment);
|
||||
CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
|
||||
closure->mStream->mCacheStream.NotifyDataReceived(aCount, aFromSegment,
|
||||
closure->mPrincipal);
|
||||
*aWriteCount = aCount;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -269,10 +302,17 @@ nsMediaChannelStream::OnDataAvailable(nsIRequest* aRequest,
|
||||
mChannelStatistics.AddBytes(aCount);
|
||||
}
|
||||
|
||||
CopySegmentClosure closure;
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (secMan && mChannel) {
|
||||
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
|
||||
}
|
||||
closure.mStream = this;
|
||||
|
||||
PRUint32 count = aCount;
|
||||
while (count > 0) {
|
||||
PRUint32 read;
|
||||
nsresult rv = aStream->ReadSegments(CopySegmentToCache, this, count,
|
||||
nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count,
|
||||
&read);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
@ -310,7 +350,7 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
*aStreamListener = nsnull;
|
||||
}
|
||||
|
||||
mSeeking = aOffset != 0;
|
||||
mLastSeekOffset = aOffset;
|
||||
|
||||
mListener = new Listener(this);
|
||||
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
@ -319,6 +359,8 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
*aStreamListener = mListener;
|
||||
NS_ADDREF(*aStreamListener);
|
||||
} else {
|
||||
mChannel->SetNotificationCallbacks(mListener.get());
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener = mListener.get();
|
||||
|
||||
// Ensure that if we're loading cross domain, that the server is sending
|
||||
@ -342,18 +384,7 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Use a byte range request from the start of the resource.
|
||||
// This enables us to detect if the stream supports byte range
|
||||
// requests, and therefore seeking, early.
|
||||
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
|
||||
if (hc) {
|
||||
nsCAutoString rangeString("bytes=");
|
||||
rangeString.AppendInt(aOffset);
|
||||
rangeString.Append("-");
|
||||
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
|
||||
} else {
|
||||
NS_ASSERTION(aOffset == 0, "Don't know how to seek on this channel type");
|
||||
}
|
||||
SetupChannelHeaders();
|
||||
|
||||
nsresult rv = mChannel->AsyncOpen(listener, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -362,6 +393,23 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsMediaChannelStream::SetupChannelHeaders()
|
||||
{
|
||||
// Always use a byte range request even if we're reading from the start
|
||||
// of the resource.
|
||||
// This enables us to detect if the stream supports byte range
|
||||
// requests, and therefore seeking, early.
|
||||
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
|
||||
if (hc) {
|
||||
nsCAutoString rangeString("bytes=");
|
||||
rangeString.AppendInt(mLastSeekOffset);
|
||||
rangeString.Append("-");
|
||||
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
|
||||
} else {
|
||||
NS_ASSERTION(mLastSeekOffset == 0, "Don't know how to seek on this channel type");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsMediaChannelStream::Close()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
@ -371,6 +419,14 @@ nsresult nsMediaChannelStream::Close()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal> nsMediaChannelStream::GetCurrentPrincipal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = mCacheStream.GetCurrentPrincipal();
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
void nsMediaChannelStream::CloseChannel()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
@ -616,6 +672,7 @@ public:
|
||||
virtual nsresult Close();
|
||||
virtual void Suspend() {}
|
||||
virtual void Resume() {}
|
||||
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
|
||||
|
||||
// These methods are called off the main thread.
|
||||
|
||||
@ -757,6 +814,18 @@ nsresult nsMediaFileStream::Close()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal> nsMediaFileStream::GetCurrentPrincipal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (!secMan || !mChannel)
|
||||
return nsnull;
|
||||
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
nsresult nsMediaFileStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
@ -831,18 +900,6 @@ nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal> nsMediaStream::GetCurrentPrincipal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (!secMan || !mChannel)
|
||||
return nsnull;
|
||||
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
void nsMediaStream::MoveLoadsToBackground() {
|
||||
NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
|
||||
mLoadInBackground = PR_TRUE;
|
||||
|
@ -157,11 +157,8 @@ public:
|
||||
// Write the audio data from the frame to the Audio stream.
|
||||
void Write(nsAudioStream* aStream)
|
||||
{
|
||||
PRUint32 length = mAudioData.Length();
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
aStream->Write(mAudioData.Elements(), length);
|
||||
aStream->Write(mAudioData.Elements(), mAudioData.Length());
|
||||
mAudioData.Clear();
|
||||
}
|
||||
|
||||
void SetVideoHeader(OggPlayDataHeader* aVideoHeader)
|
||||
@ -230,6 +227,20 @@ public:
|
||||
return !mEmpty && mHead == mTail;
|
||||
}
|
||||
|
||||
float ResetTimes(float aPeriod)
|
||||
{
|
||||
float time = 0.0;
|
||||
if (!mEmpty) {
|
||||
PRInt32 current = mHead;
|
||||
do {
|
||||
mQueue[current]->mTime = time;
|
||||
time += aPeriod;
|
||||
current = (current + 1) % OGGPLAY_BUFFER_SIZE;
|
||||
} while (current != mTail);
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
private:
|
||||
FrameData* mQueue[OGGPLAY_BUFFER_SIZE];
|
||||
PRInt32 mHead;
|
||||
@ -292,8 +303,11 @@ public:
|
||||
// must be locked when calling this method.
|
||||
void PlayVideo(FrameData* aFrame);
|
||||
|
||||
// Play the audio data from the given frame. The decode monitor must
|
||||
// be locked when calling this method.
|
||||
// Plays the audio for the frame, plus any outstanding audio data
|
||||
// buffered by nsAudioStream and not yet written to the
|
||||
// hardware. The audio data for the frame is cleared out so
|
||||
// subsequent calls with the same frame do not re-write the data.
|
||||
// The decode monitor must be locked when calling this method.
|
||||
void PlayAudio(FrameData* aFrame);
|
||||
|
||||
// Called from the main thread to get the current frame time. The decoder
|
||||
@ -365,11 +379,22 @@ protected:
|
||||
void StopAudio();
|
||||
|
||||
// Start playback of media. Must be called with the decode monitor held.
|
||||
// This opens or re-opens the audio stream for playback to start.
|
||||
void StartPlayback();
|
||||
|
||||
// Stop playback of media. Must be called with the decode monitor held.
|
||||
// This actually closes the audio stream and releases any OS resources.
|
||||
void StopPlayback();
|
||||
|
||||
// Pause playback of media. Must be called with the decode monitor held.
|
||||
// This does not close the OS based audio stream - it suspends it to be
|
||||
// resumed later.
|
||||
void PausePlayback();
|
||||
|
||||
// Resume playback of media. Must be called with the decode monitor held.
|
||||
// This resumes a paused audio stream.
|
||||
void ResumePlayback();
|
||||
|
||||
// Update the playback position. This can result in a timeupdate event
|
||||
// and an invalidate of the frame being dispatched asynchronously if
|
||||
// there is no such event currently queued.
|
||||
@ -377,6 +402,11 @@ protected:
|
||||
// the decode monitor held.
|
||||
void UpdatePlaybackPosition(float aTime);
|
||||
|
||||
// Takes decoded frames from liboggplay's internal buffer and
|
||||
// places them in our frame queue. Must be called with the decode
|
||||
// monitor held.
|
||||
void QueueDecodedFrames();
|
||||
|
||||
private:
|
||||
// *****
|
||||
// The follow fields are only accessed by the decoder thread
|
||||
@ -753,7 +783,7 @@ void nsOggDecodeStateMachine::PlayFrame() {
|
||||
|
||||
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
|
||||
if (!mPlaying) {
|
||||
StartPlayback();
|
||||
ResumePlayback();
|
||||
}
|
||||
|
||||
if (!mDecodedFrames.IsEmpty()) {
|
||||
@ -769,7 +799,14 @@ void nsOggDecodeStateMachine::PlayFrame() {
|
||||
|
||||
double time;
|
||||
for (;;) {
|
||||
time = (TimeStamp::Now() - mPlayStartTime - mPauseDuration).ToSeconds();
|
||||
// Even if the frame has had its audio data written we call
|
||||
// PlayAudio to ensure that any data we have buffered in the
|
||||
// nsAudioStream is written to the hardware.
|
||||
PlayAudio(frame);
|
||||
double hwtime = mAudioStream ? mAudioStream->GetPosition() : -1.0;
|
||||
time = hwtime < 0.0 ?
|
||||
(TimeStamp::Now() - mPlayStartTime - mPauseDuration).ToSeconds() :
|
||||
hwtime;
|
||||
if (time < frame->mTime) {
|
||||
mon.Wait(PR_MillisecondsToInterval(PRInt64((frame->mTime - time)*1000)));
|
||||
if (mState == DECODER_STATE_SHUTDOWN)
|
||||
@ -780,24 +817,33 @@ void nsOggDecodeStateMachine::PlayFrame() {
|
||||
}
|
||||
|
||||
mDecodedFrames.Pop();
|
||||
QueueDecodedFrames();
|
||||
|
||||
// Skip frames up to the one we should be showing.
|
||||
while (!mDecodedFrames.IsEmpty() && time >= mDecodedFrames.Peek()->mTime) {
|
||||
LOG(PR_LOG_DEBUG, ("Skipping frame time %f with audio at time %f", mDecodedFrames.Peek()->mTime, time));
|
||||
PlayAudio(frame);
|
||||
delete frame;
|
||||
frame = mDecodedFrames.Peek();
|
||||
mDecodedFrames.Pop();
|
||||
}
|
||||
PlayAudio(frame);
|
||||
PlayVideo(frame);
|
||||
mDecoder->mPlaybackPosition = frame->mEndStreamPosition;
|
||||
UpdatePlaybackPosition(frame->mDecodedFrameTime);
|
||||
delete frame;
|
||||
if (time < frame->mTime + mCallbackPeriod) {
|
||||
PlayAudio(frame);
|
||||
PlayVideo(frame);
|
||||
mDecoder->mPlaybackPosition = frame->mEndStreamPosition;
|
||||
UpdatePlaybackPosition(frame->mDecodedFrameTime);
|
||||
delete frame;
|
||||
}
|
||||
else {
|
||||
PlayAudio(frame);
|
||||
delete frame;
|
||||
frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mPlaying) {
|
||||
StopPlayback();
|
||||
PausePlayback();
|
||||
}
|
||||
|
||||
if (mState == DECODER_STATE_DECODING) {
|
||||
@ -904,16 +950,52 @@ void nsOggDecodeStateMachine::StartPlayback()
|
||||
// Null out mPauseStartTime
|
||||
mPauseStartTime = TimeStamp();
|
||||
}
|
||||
mPlayStartTime = TimeStamp::Now();
|
||||
mPauseDuration = 0;
|
||||
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::StopPlayback()
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "StopPlayback() called without acquiring decoder monitor");
|
||||
mLastFrameTime = mDecodedFrames.ResetTimes(mCallbackPeriod);
|
||||
StopAudio();
|
||||
mPlaying = PR_FALSE;
|
||||
mPauseStartTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::PausePlayback()
|
||||
{
|
||||
if (!mAudioStream) {
|
||||
StopPlayback();
|
||||
return;
|
||||
}
|
||||
|
||||
mAudioStream->Pause();
|
||||
mPlaying = PR_FALSE;
|
||||
mPauseStartTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::ResumePlayback()
|
||||
{
|
||||
if (!mAudioStream) {
|
||||
StartPlayback();
|
||||
return;
|
||||
}
|
||||
|
||||
mAudioStream->Resume();
|
||||
mPlaying = PR_TRUE;
|
||||
|
||||
// Compute duration spent paused
|
||||
if (!mPauseStartTime.IsNull()) {
|
||||
mPauseDuration += TimeStamp::Now() - mPauseStartTime;
|
||||
// Null out mPauseStartTime
|
||||
mPauseStartTime = TimeStamp();
|
||||
}
|
||||
mPlayStartTime = TimeStamp::Now();
|
||||
mPauseDuration = 0;
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::UpdatePlaybackPosition(float aTime)
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "UpdatePlaybackPosition() called without acquiring decoder monitor");
|
||||
@ -926,6 +1008,15 @@ void nsOggDecodeStateMachine::UpdatePlaybackPosition(float aTime)
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::QueueDecodedFrames()
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "QueueDecodedFrames() called without acquiring decoder monitor");
|
||||
FrameData* frame;
|
||||
while (!mDecodedFrames.IsFull() && (frame = NextFrame())) {
|
||||
mDecodedFrames.Push(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void nsOggDecodeStateMachine::ClearPositionChangeFlag()
|
||||
{
|
||||
// NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "ClearPositionChangeFlag() called without acquiring decoder monitor");
|
||||
@ -1096,10 +1187,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
mon.Wait(PR_MillisecondsToInterval(PRInt64(mCallbackPeriod*500)));
|
||||
if (mState != DECODER_STATE_DECODING)
|
||||
break;
|
||||
FrameData* frame = NextFrame();
|
||||
if (frame) {
|
||||
mDecodedFrames.Push(frame);
|
||||
}
|
||||
QueueDecodedFrames();
|
||||
}
|
||||
|
||||
if (mState != DECODER_STATE_DECODING)
|
||||
@ -1108,11 +1196,11 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
if (mDecodingCompleted) {
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING to COMPLETED"));
|
||||
mState = DECODER_STATE_COMPLETED;
|
||||
mStepDecodeThread->Shutdown();
|
||||
mStepDecodeThread = nsnull;
|
||||
mDecodingCompleted = PR_FALSE;
|
||||
mBufferExhausted = PR_FALSE;
|
||||
mon.NotifyAll();
|
||||
mStepDecodeThread->Shutdown();
|
||||
mStepDecodeThread = nsnull;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1130,7 +1218,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
// more data to load. Let's buffer to make sure we can play a
|
||||
// decent amount of video in the future.
|
||||
if (mPlaying) {
|
||||
StopPlayback();
|
||||
PausePlayback();
|
||||
}
|
||||
|
||||
// We need to tell the element that buffering has started.
|
||||
@ -1153,7 +1241,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
BUFFERING_RATE(playbackRate) * BUFFERING_WAIT;
|
||||
mState = DECODER_STATE_BUFFERING;
|
||||
if (mPlaying) {
|
||||
StopPlayback();
|
||||
PausePlayback();
|
||||
}
|
||||
LOG(PR_LOG_DEBUG, ("Changed state from DECODING to BUFFERING"));
|
||||
} else {
|
||||
@ -1284,7 +1372,7 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) {
|
||||
if (!mPlaying) {
|
||||
StartPlayback();
|
||||
ResumePlayback();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1296,23 +1384,18 @@ nsresult nsOggDecodeStateMachine::Run()
|
||||
{
|
||||
// Get all the remaining decoded frames in the liboggplay buffer and
|
||||
// place them in the frame queue.
|
||||
FrameData* frame;
|
||||
do {
|
||||
frame = NextFrame();
|
||||
if (frame) {
|
||||
mDecodedFrames.Push(frame);
|
||||
}
|
||||
} while (frame);
|
||||
QueueDecodedFrames();
|
||||
|
||||
// Play the remaining frames in the frame queue
|
||||
while (mState == DECODER_STATE_COMPLETED &&
|
||||
!mDecodedFrames.IsEmpty()) {
|
||||
PlayFrame();
|
||||
if (mState != DECODER_STATE_SHUTDOWN) {
|
||||
if (mState == DECODER_STATE_COMPLETED) {
|
||||
// Wait for the time of one frame so we don't tight loop
|
||||
// and we need to release the monitor so timeupdate and
|
||||
// invalidate's on the main thread can occur.
|
||||
mon.Wait(PR_MillisecondsToInterval(PRInt64(mCallbackPeriod*1000)));
|
||||
QueueDecodedFrames();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ _TEST_FILES = \
|
||||
|
||||
ifdef MOZ_OGG
|
||||
_TEST_FILES += \
|
||||
dynamic_redirect.sjs \
|
||||
test_access_control.html \
|
||||
file_access_controls.html \
|
||||
test_bug448534.html \
|
||||
@ -82,6 +83,7 @@ _TEST_FILES += \
|
||||
test_info_leak.html \
|
||||
test_onloadedmetadata.html \
|
||||
test_load_candidates.html \
|
||||
test_mixed_principals.html \
|
||||
test_play.html \
|
||||
test_progress1.html \
|
||||
test_progress3.html \
|
||||
|
39
content/media/video/test/dynamic_redirect.sjs
Normal file
39
content/media/video/test/dynamic_redirect.sjs
Normal file
@ -0,0 +1,39 @@
|
||||
// Return seek.ogv file content for the first request with a given key.
|
||||
// All subsequent requests return a redirect to a different-origin resource.
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var key = request.queryString.match(/^key=(.*)$/);
|
||||
|
||||
if (getState(key[1]) == "redirect") {
|
||||
var origin = request.host == "localhost" ? "example.org" : "localhost:8888";
|
||||
response.setStatusLine(request.httpVersion, 303, "See Other");
|
||||
response.setHeader("Location", "http://" + origin + "/tests/content/media/video/test/seek.ogv");
|
||||
response.setHeader("Content-Type", "text/html");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(key[1], "redirect");
|
||||
|
||||
var file = Components.classes["@mozilla.org/file/directory_service;1"].
|
||||
getService(Components.interfaces.nsIProperties).
|
||||
get("CurWorkD", Components.interfaces.nsILocalFile);
|
||||
var fis = Components.classes['@mozilla.org/network/file-input-stream;1'].
|
||||
createInstance(Components.interfaces.nsIFileInputStream);
|
||||
var bis = Components.classes["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Components.interfaces.nsIBinaryInputStream);
|
||||
var paths = "tests/content/media/video/test/seek.ogv";
|
||||
var split = paths.split("/");
|
||||
for(var i = 0; i < split.length; ++i) {
|
||||
file.append(split[i]);
|
||||
}
|
||||
fis.init(file, -1, -1, false);
|
||||
dump("file=" + file + "\n");
|
||||
bis.setInputStream(fis);
|
||||
var bytes = bis.readBytes(bis.available());
|
||||
response.setStatusLine(request.httpVersion, 206, "Partial Content");
|
||||
response.setHeader("Content-Range", "bytes 0-" + (bytes.length - 1) + "/" + bytes.length);
|
||||
response.setHeader("Content-Length", ""+bytes.length, false);
|
||||
response.setHeader("Content-Type", "video/ogg", false);
|
||||
response.write(bytes, bytes.length);
|
||||
bis.close();
|
||||
}
|
68
content/media/video/test/test_mixed_principals.html
Normal file
68
content/media/video/test/test_mixed_principals.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=489415
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 489415</title>
|
||||
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489415">Mozilla Bug 489415</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<video id="v1" autoplay onended="loaded('v1')"></video>
|
||||
<video id="v2" autoplay onended="loaded('v2')"></video>
|
||||
|
||||
<pre id="test">
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var v1 = document.getElementById("v1");
|
||||
var v2 = document.getElementById("v2");
|
||||
|
||||
var count = 0;
|
||||
|
||||
function loaded(id) {
|
||||
var c = document.createElement("canvas");
|
||||
var ctx = c.getContext("2d");
|
||||
var v = document.getElementById(id);
|
||||
ctx.drawImage(v, 0, 0);
|
||||
try {
|
||||
c.toDataURL();
|
||||
ok(false, "Failed to throw exception in toDataURL for " + id);
|
||||
} catch (ex) {
|
||||
ok(true, "Threw exception in toDataURL for " + id);
|
||||
}
|
||||
if (++count == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a random key. The first load with that key will return
|
||||
// data, the second and subsequent loads with that key will return a redirect
|
||||
// to a different origin ('localhost:8888' will be redirected to 'example.org',
|
||||
// and 'example.org' will be redirected to 'localhost:8888'). We rely on the
|
||||
// fact that Ogg will do a seek to the end of the resource, triggering a new
|
||||
// load with the same key which will return a same-origin resource.
|
||||
// Loading data from two different origins should be detected by the media
|
||||
// cache and result in a null principal so that the canvas usage above fails.
|
||||
var key = Math.floor(Math.random()*100000000);
|
||||
|
||||
// In v1, try loading from same-origin first and then getting redirected to
|
||||
// another origin.
|
||||
v1.src = "http://localhost:8888/tests/content/media/video/test/dynamic_redirect.sjs?key=v1_" + key;
|
||||
v1.load();
|
||||
|
||||
// In v2, try loading cross-origin first and then getting redirected to
|
||||
// our origin.
|
||||
v2.src = "http://example.org/tests/content/media/video/test/dynamic_redirect.sjs?key=v2_" + key;
|
||||
v2.load();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -265,7 +265,6 @@ nsXBLDocGlobalObject::SetContext(nsIScriptContext *aScriptContext)
|
||||
nsresult rv;
|
||||
rv = aScriptContext->InitContext(nsnull);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Script Language's InitContext failed");
|
||||
aScriptContext->SetGCOnDestruction(PR_FALSE);
|
||||
aScriptContext->DidInitializeContext();
|
||||
// and we set up our global manually
|
||||
mScriptContext = aScriptContext;
|
||||
|
@ -693,7 +693,6 @@ nsXULPDGlobalObject::SetScriptContext(PRUint32 lang_id, nsIScriptContext *aScrip
|
||||
void *script_glob = nsnull;
|
||||
|
||||
if (aScriptContext) {
|
||||
aScriptContext->SetGCOnDestruction(PR_FALSE);
|
||||
aScriptContext->DidInitializeContext();
|
||||
script_glob = aScriptContext->GetNativeGlobal();
|
||||
NS_ASSERTION(script_glob, "GetNativeGlobal returned NULL!");
|
||||
|
@ -278,6 +278,21 @@ nsDOMScriptObjectFactory::Observe(nsISupports *aSubject,
|
||||
cache->Flush();
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIThreadJSContextStack> stack =
|
||||
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
|
||||
|
||||
if (stack) {
|
||||
JSContext *cx = nsnull;
|
||||
|
||||
stack->GetSafeJSContext(&cx);
|
||||
|
||||
if (cx) {
|
||||
// Do one final GC to clean things up before shutdown.
|
||||
|
||||
::JS_GC(cx);
|
||||
}
|
||||
}
|
||||
|
||||
nsGlobalWindow::ShutDown();
|
||||
nsDOMClassInfo::ShutDown();
|
||||
|
||||
|
@ -7,7 +7,7 @@ installedplugins_label=Installed plugins
|
||||
nopluginsareinstalled_label=No plugins are installed
|
||||
findmore_label=Find more information about browser plugins at
|
||||
installhelp_label=Help for installing plugins is available from
|
||||
filename_label=File name:
|
||||
file_label=File:
|
||||
version_label=Version:
|
||||
mimetype_label=MIME Type
|
||||
description_label=Description
|
||||
|
@ -166,16 +166,14 @@ WifiGeoPositionProvider.prototype = {
|
||||
this.timer = null;
|
||||
}
|
||||
|
||||
// Although we aren't using cookies, we should error on the side of not
|
||||
// Although we aren't using cookies, we should err on the side of not
|
||||
// saving any access tokens if the user asked us not to save cookies or
|
||||
// has changed the lifetimePolicy. The access token in these cases is
|
||||
// used and valid for the life of this object (eg. between startup and
|
||||
// shutdown).e
|
||||
let prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
if (prefService.getIntPref("network.cookie.lifetimePolicy") != 0) {
|
||||
let branch = prefService.getBranch("geo.wifi.access_token.");
|
||||
branch.deleteBranch("");
|
||||
}
|
||||
let prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
if (prefBranch.getIntPref("network.cookie.lifetimePolicy") != 0)
|
||||
prefBranch.deleteBranch("geo.wifi.access_token.");
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.removeObserver(this, "private-browsing");
|
||||
|
@ -318,6 +318,8 @@ NS_IMETHODIMP IMETextTxn::CollapseTextSelection(void)
|
||||
|
||||
if(nsIPrivateTextRange::TEXTRANGE_CARETPOSITION == textRangeType)
|
||||
{
|
||||
NS_ASSERTION(selectionStart == selectionEnd,
|
||||
"nsEditor doesn't support wide caret");
|
||||
// Set the caret....
|
||||
result = selection->Collapse(mElement,
|
||||
mOffset+selectionStart);
|
||||
|
@ -2670,6 +2670,15 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Joining a list item to its parent is a NOP.
|
||||
if (nsHTMLEditUtils::IsList(*aLeftBlock) && nsHTMLEditUtils::IsListItem(*aRightBlock))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
(*aRightBlock)->GetParentNode(getter_AddRefs(rightParent));
|
||||
if (rightParent == *aLeftBlock)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// special rule here: if we are trying to join list items, and they are in different lists,
|
||||
// join the lists instead.
|
||||
PRBool bMergeLists = PR_FALSE;
|
||||
@ -2816,7 +2825,7 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsIDOMNode> *aLeftBlock,
|
||||
* nsIDOMNode *aLeftBlock parent to receive moved content
|
||||
* nsIDOMNode *aRightBlock parent to provide moved content
|
||||
* PRInt32 aLeftOffset offset in aLeftBlock to move content to
|
||||
* PRInt32 aRightOffset offset in aLeftBlock to move content to
|
||||
* PRInt32 aRightOffset offset in aRightBlock to move content from
|
||||
*/
|
||||
nsresult
|
||||
nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, PRInt32 aLeftOffset, PRInt32 aRightOffset)
|
||||
@ -2854,7 +2863,7 @@ nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, PRIn
|
||||
* inserted content.
|
||||
* nsIDOMNode *aSource the selection.
|
||||
* nsIDOMNode *aDest parent to receive moved content
|
||||
* PRInt32 *aOffset offset in aNewParent to move content to
|
||||
* PRInt32 *aOffset offset in aDest to move content to
|
||||
*/
|
||||
nsresult
|
||||
nsHTMLEditRules::MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
|
||||
@ -2891,13 +2900,14 @@ nsHTMLEditRules::MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *
|
||||
* inserted content. aSource is deleted.
|
||||
* nsIDOMNode *aSource the selection.
|
||||
* nsIDOMNode *aDest parent to receive moved content
|
||||
* PRInt32 *aOffset offset in aNewParent to move content to
|
||||
* PRInt32 *aOffset offset in aDest to move content to
|
||||
*/
|
||||
nsresult
|
||||
nsHTMLEditRules::MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, PRInt32 *aOffset)
|
||||
{
|
||||
if (!aSource || !aDest || !aOffset) return NS_ERROR_NULL_POINTER;
|
||||
if (aSource == aDest) return NS_ERROR_ILLEGAL_VALUE;
|
||||
NS_ASSERTION(!mHTMLEditor->IsTextNode(aSource), "#text does not have contents");
|
||||
|
||||
nsCOMPtr<nsIDOMNode> child;
|
||||
nsAutoString tag;
|
||||
|
@ -51,6 +51,7 @@ _TEST_FILES = \
|
||||
test_bug456244.html \
|
||||
test_bug478725.html \
|
||||
test_bug480972.html \
|
||||
test_bug487524.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
67
editor/libeditor/html/tests/test_bug487524.html
Normal file
67
editor/libeditor/html/tests/test_bug487524.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html><head>
|
||||
<title>Test for bug 487524</title>
|
||||
<style src="/tests/SimpleTest/test.css" type="text/css"></style>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
function runTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
function setupIframe(e,html,focus_id) {
|
||||
var doc = e.contentDocument;
|
||||
doc.body.innerHTML = html;
|
||||
doc.designMode = "on";
|
||||
e = doc.getElementById(focus_id);
|
||||
doc.defaultView.focus();
|
||||
if (e) e.focus();
|
||||
return e;
|
||||
}
|
||||
|
||||
var i1 = document.getElementById('i1')
|
||||
var li1 = setupIframe(i1,'<ul><li id="li1">one</li><li>two</li><ul><li>a</li></ul></ul>','li1')
|
||||
var doc = li1.ownerDocument;
|
||||
|
||||
var selection = doc.defaultView.getSelection();
|
||||
selection.removeAllRanges();
|
||||
|
||||
var range = doc.createRange();
|
||||
range.setStart(li1,0);
|
||||
range.setEnd(li1.nextSibling,0);
|
||||
selection.addRange(range);
|
||||
|
||||
sendKey('delete', li1);
|
||||
is(doc.body.innerHTML,'<ul><li>two</li><ul><li>a</li></ul></ul>','delete 1st LI');
|
||||
|
||||
var li2 = setupIframe(i1,'<ul><li id="li2">two</li><ul><li>a</li></ul></ul>','li2')
|
||||
selection = doc.defaultView.getSelection();
|
||||
selection.removeAllRanges();
|
||||
|
||||
range = doc.createRange();
|
||||
range.setStart(li2,0);
|
||||
range.setEnd(li2.nextSibling.firstChild,0);
|
||||
selection.addRange(range);
|
||||
|
||||
sendKey('delete', li2);
|
||||
is(doc.body.innerHTML,'<ul><ul><li>a</li></ul></ul>','delete 2nd LI');
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(runTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=487524">Mozilla Bug 487524</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<iframe id="i1" width="200" height="100" src="about:blank"></iframe><br>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -48,6 +48,8 @@ wrap-source_image.patch: make sure we don't free the source image until we're do
|
||||
|
||||
zero-sized.patch: deal with zero sized surface in ways less likely to crash.
|
||||
|
||||
text-path-filling-threshold.patch: use path filling instead of platform glyph rasterization at a smaller size threshold of 256 device pixels, if the backend supports native filling (which we assume will be fast).
|
||||
|
||||
==== pixman patches ====
|
||||
|
||||
pixman-neon.patch: add ARM NEON optimized compositing functions
|
||||
|
@ -38,7 +38,6 @@
|
||||
|
||||
#include "cairo-analysis-surface-private.h"
|
||||
#include "cairo-paginated-private.h"
|
||||
#include "cairo-region-private.h"
|
||||
#include "cairo-meta-surface-private.h"
|
||||
|
||||
typedef struct {
|
||||
@ -215,7 +214,7 @@ _add_operation (cairo_analysis_surface_t *surface,
|
||||
* region there is no benefit in emitting a native operation as
|
||||
* the fallback image will be painted on top.
|
||||
*/
|
||||
if (_cairo_region_contains_rectangle (&surface->fallback_region, rect) == PIXMAN_REGION_IN)
|
||||
if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN)
|
||||
return CAIRO_INT_STATUS_IMAGE_FALLBACK;
|
||||
|
||||
if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) {
|
||||
@ -226,7 +225,7 @@ _add_operation (cairo_analysis_surface_t *surface,
|
||||
* natively supported and the backend will blend the
|
||||
* transparency into the white background.
|
||||
*/
|
||||
if (_cairo_region_contains_rectangle (&surface->supported_region, rect) == PIXMAN_REGION_OUT)
|
||||
if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
|
||||
backend_status = CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -235,9 +234,7 @@ _add_operation (cairo_analysis_surface_t *surface,
|
||||
* this region will be emitted as native operations.
|
||||
*/
|
||||
surface->has_supported = TRUE;
|
||||
status = _cairo_region_union_rect (&surface->supported_region,
|
||||
&surface->supported_region,
|
||||
rect);
|
||||
status = cairo_region_union_rectangle (&surface->supported_region, rect);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -246,9 +243,7 @@ _add_operation (cairo_analysis_surface_t *surface,
|
||||
* emitted.
|
||||
*/
|
||||
surface->has_unsupported = TRUE;
|
||||
status = _cairo_region_union_rect (&surface->fallback_region,
|
||||
&surface->fallback_region,
|
||||
rect);
|
||||
status = cairo_region_union_rectangle (&surface->fallback_region, rect);
|
||||
|
||||
/* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate
|
||||
* unsupported operations to the meta surface as using
|
||||
@ -778,14 +773,14 @@ _cairo_analysis_surface_create (cairo_surface_t *target,
|
||||
surface->has_supported = FALSE;
|
||||
surface->has_unsupported = FALSE;
|
||||
|
||||
_cairo_region_init (&surface->supported_region);
|
||||
_cairo_region_init (&surface->fallback_region);
|
||||
|
||||
surface->page_bbox.p1.x = 0;
|
||||
surface->page_bbox.p1.y = 0;
|
||||
surface->page_bbox.p2.x = 0;
|
||||
surface->page_bbox.p2.y = 0;
|
||||
|
||||
_cairo_region_init (&surface->supported_region);
|
||||
_cairo_region_init (&surface->fallback_region);
|
||||
|
||||
if (width == -1 && height == -1) {
|
||||
surface->current_clip.x = CAIRO_RECT_INT_MIN;
|
||||
surface->current_clip.y = CAIRO_RECT_INT_MIN;
|
||||
|
@ -125,6 +125,9 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional)
|
||||
if (required_size > INT_MAX || required_size < array->num_elements)
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (required_size <= old_size)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
|
@ -1660,6 +1660,9 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
|
||||
if (0 == polygon->num_edges)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
has_limits = _cairo_traps_get_limit (traps, &limit);
|
||||
|
||||
edges = stack_edges;
|
||||
|
@ -40,7 +40,6 @@
|
||||
#include "cairo-compiler-private.h"
|
||||
#include "cairo-path-fixed-private.h"
|
||||
#include "cairo-reference-count-private.h"
|
||||
#include "cairo-region-private.h"
|
||||
|
||||
extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil;
|
||||
|
||||
@ -78,8 +77,7 @@ struct _cairo_clip {
|
||||
/*
|
||||
* A clip region that can be placed in the surface
|
||||
*/
|
||||
cairo_region_t region;
|
||||
cairo_bool_t has_region;
|
||||
cairo_region_t *region;
|
||||
/*
|
||||
* If the surface supports path clipping, we store the list of
|
||||
* clipping paths that has been set here as a linked list.
|
||||
|
@ -64,8 +64,7 @@ _cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target)
|
||||
|
||||
clip->serial = 0;
|
||||
|
||||
_cairo_region_init (&clip->region);
|
||||
clip->has_region = FALSE;
|
||||
clip->region = NULL;
|
||||
|
||||
clip->path = NULL;
|
||||
}
|
||||
@ -76,28 +75,29 @@ _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
|
||||
clip->mode = other->mode;
|
||||
|
||||
clip->all_clipped = other->all_clipped;
|
||||
|
||||
|
||||
clip->surface = cairo_surface_reference (other->surface);
|
||||
clip->surface_rect = other->surface_rect;
|
||||
|
||||
clip->serial = other->serial;
|
||||
|
||||
_cairo_region_init (&clip->region);
|
||||
|
||||
if (other->has_region) {
|
||||
if (other->region) {
|
||||
cairo_status_t status;
|
||||
|
||||
clip->region = cairo_region_copy (other->region);
|
||||
|
||||
status = _cairo_region_copy (&clip->region, &other->region);
|
||||
status = cairo_region_status (clip->region);
|
||||
if (unlikely (status)) {
|
||||
_cairo_region_fini (&clip->region);
|
||||
cairo_surface_destroy (clip->surface);
|
||||
cairo_region_destroy (clip->region);
|
||||
clip->region = NULL;
|
||||
|
||||
return status;
|
||||
}
|
||||
clip->has_region = TRUE;
|
||||
} else {
|
||||
clip->has_region = FALSE;
|
||||
clip->region = NULL;
|
||||
}
|
||||
|
||||
|
||||
clip->path = _cairo_clip_path_reference (other->path);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
@ -114,14 +114,10 @@ _cairo_clip_reset (cairo_clip_t *clip)
|
||||
|
||||
clip->serial = 0;
|
||||
|
||||
if (clip->has_region) {
|
||||
/* _cairo_region_fini just releases the resources used but
|
||||
* doesn't bother with leaving the region in a valid state.
|
||||
* So _cairo_region_init has to be called afterwards. */
|
||||
_cairo_region_fini (&clip->region);
|
||||
_cairo_region_init (&clip->region);
|
||||
if (clip->region) {
|
||||
cairo_region_destroy (clip->region);
|
||||
|
||||
clip->has_region = FALSE;
|
||||
clip->region = NULL;
|
||||
}
|
||||
|
||||
_cairo_clip_path_destroy (clip->path);
|
||||
@ -178,10 +174,10 @@ _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip,
|
||||
return status;
|
||||
}
|
||||
|
||||
if (clip->has_region) {
|
||||
if (clip->region) {
|
||||
cairo_rectangle_int_t extents;
|
||||
|
||||
_cairo_region_get_extents (&clip->region, &extents);
|
||||
cairo_region_get_extents (clip->region, &extents);
|
||||
is_empty = _cairo_rectangle_intersect (rectangle, &extents);
|
||||
if (is_empty)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
@ -194,7 +190,7 @@ _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip,
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_clip_intersect_to_region (cairo_clip_t *clip,
|
||||
_cairo_clip_intersect_to_region (cairo_clip_t *clip,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
cairo_status_t status;
|
||||
@ -202,40 +198,21 @@ _cairo_clip_intersect_to_region (cairo_clip_t *clip,
|
||||
if (!clip)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (clip->all_clipped) {
|
||||
cairo_region_t clip_rect;
|
||||
|
||||
_cairo_region_init_rect (&clip_rect, &clip->surface_rect);
|
||||
|
||||
status = _cairo_region_intersect (region, &clip_rect, region);
|
||||
|
||||
_cairo_region_fini (&clip_rect);
|
||||
|
||||
return status;
|
||||
}
|
||||
if (clip->all_clipped)
|
||||
return cairo_region_intersect_rectangle (region, &clip->surface_rect);
|
||||
|
||||
if (clip->path) {
|
||||
/* Intersect clip path into region. */
|
||||
}
|
||||
|
||||
if (clip->has_region) {
|
||||
status = _cairo_region_intersect (region, &clip->region, region);
|
||||
if (clip->region) {
|
||||
status = cairo_region_intersect (region, clip->region);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
}
|
||||
|
||||
if (clip->surface) {
|
||||
cairo_region_t clip_rect;
|
||||
|
||||
_cairo_region_init_rect (&clip_rect, &clip->surface_rect);
|
||||
|
||||
status = _cairo_region_intersect (region, &clip_rect, region);
|
||||
|
||||
_cairo_region_fini (&clip_rect);
|
||||
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
}
|
||||
if (clip->surface)
|
||||
return cairo_region_intersect_rectangle (region, &clip->surface_rect);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@ -344,7 +321,7 @@ _cairo_clip_intersect_region (cairo_clip_t *clip,
|
||||
cairo_traps_t *traps,
|
||||
cairo_surface_t *target)
|
||||
{
|
||||
cairo_region_t region;
|
||||
cairo_region_t *region;
|
||||
cairo_int_status_t status;
|
||||
|
||||
if (clip->all_clipped)
|
||||
@ -357,29 +334,21 @@ _cairo_clip_intersect_region (cairo_clip_t *clip,
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (!clip->has_region) {
|
||||
status = _cairo_region_copy (&clip->region, ®ion);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
clip->has_region = TRUE;
|
||||
if (clip->region) {
|
||||
status = cairo_region_intersect (clip->region, region);
|
||||
} else {
|
||||
cairo_region_t intersection;
|
||||
clip->region = cairo_region_copy (region);
|
||||
|
||||
_cairo_region_init (&intersection);
|
||||
|
||||
status = _cairo_region_intersect (&intersection,
|
||||
&clip->region,
|
||||
®ion);
|
||||
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = _cairo_region_copy (&clip->region, &intersection);
|
||||
|
||||
_cairo_region_fini (&intersection);
|
||||
assert (clip->region != NULL);
|
||||
|
||||
if ((status = cairo_region_status (clip->region)))
|
||||
clip->region = NULL;
|
||||
}
|
||||
|
||||
clip->serial = _cairo_surface_allocate_clip_serial (target);
|
||||
_cairo_region_fini (®ion);
|
||||
cairo_region_destroy (region);
|
||||
|
||||
if (! _cairo_region_not_empty (&clip->region))
|
||||
if (!clip->region || cairo_region_is_empty (clip->region))
|
||||
_cairo_clip_set_all_clipped (clip, target);
|
||||
|
||||
return status;
|
||||
@ -736,10 +705,10 @@ _cairo_clip_translate (cairo_clip_t *clip,
|
||||
if (clip->all_clipped)
|
||||
return;
|
||||
|
||||
if (clip->has_region) {
|
||||
_cairo_region_translate (&clip->region,
|
||||
_cairo_fixed_integer_part (tx),
|
||||
_cairo_fixed_integer_part (ty));
|
||||
if (clip->region) {
|
||||
cairo_region_translate (clip->region,
|
||||
_cairo_fixed_integer_part (tx),
|
||||
_cairo_fixed_integer_part (ty));
|
||||
}
|
||||
|
||||
if (clip->surface) {
|
||||
@ -794,12 +763,10 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip,
|
||||
/* We should reapply the original clip path in this case, and let
|
||||
* whatever the right handling is happen */
|
||||
} else {
|
||||
if (other->has_region) {
|
||||
status = _cairo_region_copy (&clip->region, &other->region);
|
||||
if (unlikely (status))
|
||||
if (other->region) {
|
||||
clip->region = cairo_region_copy (other->region);
|
||||
if (unlikely ((status = cairo_region_status (clip->region))))
|
||||
goto BAIL;
|
||||
|
||||
clip->has_region = TRUE;
|
||||
}
|
||||
|
||||
if (other->surface) {
|
||||
@ -831,8 +798,8 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip,
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
BAIL:
|
||||
if (clip->has_region)
|
||||
_cairo_region_fini (&clip->region);
|
||||
if (clip->region)
|
||||
cairo_region_destroy (clip->region);
|
||||
if (clip->surface)
|
||||
cairo_surface_destroy (clip->surface);
|
||||
|
||||
@ -873,7 +840,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
|
||||
{
|
||||
cairo_rectangle_list_t *list;
|
||||
cairo_rectangle_t *rectangles = NULL;
|
||||
int n_boxes = 0;
|
||||
int n_rects = 0;
|
||||
|
||||
if (clip->all_clipped)
|
||||
goto DONE;
|
||||
@ -883,28 +850,22 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
|
||||
return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable;
|
||||
}
|
||||
|
||||
if (clip->has_region) {
|
||||
if (clip->region) {
|
||||
int i;
|
||||
|
||||
n_boxes = _cairo_region_num_boxes (&clip->region);
|
||||
n_rects = cairo_region_num_rectangles (clip->region);
|
||||
|
||||
if (n_boxes) {
|
||||
rectangles = _cairo_malloc_ab (n_boxes, sizeof (cairo_rectangle_t));
|
||||
if (n_rects) {
|
||||
rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
|
||||
if (unlikely (rectangles == NULL)) {
|
||||
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
||||
return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_boxes; ++i) {
|
||||
cairo_box_int_t box;
|
||||
for (i = 0; i < n_rects; ++i) {
|
||||
cairo_rectangle_int_t clip_rect;
|
||||
|
||||
_cairo_region_get_box (&clip->region, i, &box);
|
||||
|
||||
clip_rect.x = box.p1.x;
|
||||
clip_rect.y = box.p1.y;
|
||||
clip_rect.width = box.p2.x - box.p1.x;
|
||||
clip_rect.height = box.p2.y - box.p1.y;
|
||||
cairo_region_get_rectangle (clip->region, i, &clip_rect);
|
||||
|
||||
if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) {
|
||||
_cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
|
||||
@ -916,7 +877,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
|
||||
} else {
|
||||
cairo_rectangle_int_t extents;
|
||||
|
||||
n_boxes = 1;
|
||||
n_rects = 1;
|
||||
|
||||
rectangles = malloc(sizeof (cairo_rectangle_t));
|
||||
if (unlikely (rectangles == NULL)) {
|
||||
@ -943,7 +904,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
|
||||
|
||||
list->status = CAIRO_STATUS_SUCCESS;
|
||||
list->rectangles = rectangles;
|
||||
list->num_rectangles = n_boxes;
|
||||
list->num_rectangles = n_rects;
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(WINCE)
|
||||
#if defined(_MSC_VER) && defined(_M_IX86)
|
||||
/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together.
|
||||
The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and
|
||||
will never be folded into another one. Something like this might eventually
|
||||
|
@ -37,12 +37,11 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#if CAIRO_HAS_DDRAW_SURFACE
|
||||
|
||||
#include "cairo-clip-private.h"
|
||||
#include "cairo-ddraw-private.h"
|
||||
#include "cairo-region-private.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <ddraw.h>
|
||||
|
@ -1299,39 +1299,39 @@ _cairo_directfb_surface_set_clip_region (void *abstract_surface,
|
||||
__FUNCTION__, surface, region);
|
||||
|
||||
if (region) {
|
||||
int n_boxes;
|
||||
int n_rects;
|
||||
cairo_status_t status;
|
||||
int i;
|
||||
|
||||
surface->has_clip = TRUE;
|
||||
|
||||
n_boxes = _cairo_region_num_boxes (region);
|
||||
n_rects = cairo_region_num_rectangles (region);
|
||||
|
||||
if (n_boxes == 0)
|
||||
if (n_rects == 0)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (surface->n_clips != n_boxes) {
|
||||
if (surface->n_clips != n_rects) {
|
||||
if (surface->clips)
|
||||
free (surface->clips);
|
||||
|
||||
surface->clips = _cairo_malloc_ab (n_boxes, sizeof (DFBRegion));
|
||||
surface->clips = _cairo_malloc_ab (n_rects, sizeof (DFBRegion));
|
||||
if (!surface->clips) {
|
||||
surface->n_clips = 0;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
surface->n_clips = n_boxes;
|
||||
surface->n_clips = n_rects;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_boxes; i++) {
|
||||
cairo_box_int_t box;
|
||||
for (i = 0; i < n_rects; i++) {
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
_cairo_region_get_box (region, i, &box);
|
||||
cairo_region_get_rectangle (region, i, &rect);
|
||||
|
||||
surface->clips[i].x1 = box.p1.x;
|
||||
surface->clips[i].y1 = box.p1.y;
|
||||
surface->clips[i].x2 = box.p2.x - 1;
|
||||
surface->clips[i].y2 = box.p2.y - 1;
|
||||
surface->clips[i].x1 = rect.x;
|
||||
surface->clips[i].y1 = rect.y;
|
||||
surface->clips[i].x2 = rect.x + rect.width - 1;
|
||||
surface->clips[i].y2 = rect.y + rect.height - 1;
|
||||
}
|
||||
} else {
|
||||
surface->has_clip = FALSE;
|
||||
|
@ -418,11 +418,12 @@ _cairo_ft_unscaled_font_keys_equal (const void *key_a,
|
||||
/* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from
|
||||
* pattern. Returns a new reference to the unscaled font.
|
||||
*/
|
||||
static cairo_ft_unscaled_font_t *
|
||||
static cairo_status_t
|
||||
_cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
|
||||
char *filename,
|
||||
int id,
|
||||
FT_Face font_face)
|
||||
FT_Face font_face,
|
||||
cairo_ft_unscaled_font_t **out)
|
||||
{
|
||||
cairo_ft_unscaled_font_t key, *unscaled;
|
||||
cairo_ft_unscaled_font_map_t *font_map;
|
||||
@ -430,7 +431,7 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
|
||||
|
||||
font_map = _cairo_ft_unscaled_font_map_lock ();
|
||||
if (unlikely (font_map == NULL))
|
||||
goto UNWIND;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
_cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face);
|
||||
|
||||
@ -439,14 +440,13 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
|
||||
&key.base.hash_entry);
|
||||
if (unscaled != NULL) {
|
||||
_cairo_unscaled_font_reference (&unscaled->base);
|
||||
_cairo_ft_unscaled_font_map_unlock ();
|
||||
return unscaled;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
/* Otherwise create it and insert into hash table. */
|
||||
unscaled = malloc (sizeof (cairo_ft_unscaled_font_t));
|
||||
if (unlikely (unscaled == NULL)) {
|
||||
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto UNWIND_FONT_MAP_LOCK;
|
||||
}
|
||||
|
||||
@ -460,9 +460,10 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
|
||||
if (unlikely (status))
|
||||
goto UNWIND_UNSCALED_FONT_INIT;
|
||||
|
||||
DONE:
|
||||
_cairo_ft_unscaled_font_map_unlock ();
|
||||
|
||||
return unscaled;
|
||||
*out = unscaled;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
UNWIND_UNSCALED_FONT_INIT:
|
||||
_cairo_ft_unscaled_font_fini (unscaled);
|
||||
@ -470,39 +471,52 @@ UNWIND_UNSCALED_MALLOC:
|
||||
free (unscaled);
|
||||
UNWIND_FONT_MAP_LOCK:
|
||||
_cairo_ft_unscaled_font_map_unlock ();
|
||||
UNWIND:
|
||||
return NULL;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
#if CAIRO_HAS_FC_FONT
|
||||
static cairo_ft_unscaled_font_t *
|
||||
_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern)
|
||||
static cairo_status_t
|
||||
_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern,
|
||||
cairo_ft_unscaled_font_t **out)
|
||||
{
|
||||
FT_Face font_face = NULL;
|
||||
char *filename = NULL;
|
||||
int id = 0;
|
||||
FcResult ret;
|
||||
|
||||
if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face) == FcResultMatch)
|
||||
goto DONE;
|
||||
|
||||
if (FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename) == FcResultMatch) {
|
||||
/* If FC_INDEX is not set, we just use 0 */
|
||||
FcPatternGetInteger (pattern, FC_INDEX, 0, &id);
|
||||
ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face);
|
||||
switch ((int) ret) {
|
||||
case FcResultMatch:
|
||||
goto DONE;
|
||||
case FcResultOutOfMemory:
|
||||
break;
|
||||
default:
|
||||
if (FcPatternGetString (pattern, FC_FILE, 0,
|
||||
(FcChar8 **) &filename) == FcResultMatch)
|
||||
{
|
||||
/* If FC_INDEX is not set, we just use 0 */
|
||||
if (FcPatternGetInteger (pattern,
|
||||
FC_INDEX, 0, &id) != FcResultOutOfMemory)
|
||||
goto DONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
DONE:
|
||||
return _cairo_ft_unscaled_font_create_internal (font_face != NULL, filename, id, font_face);
|
||||
return _cairo_ft_unscaled_font_create_internal (font_face != NULL,
|
||||
filename, id, font_face,
|
||||
out);
|
||||
}
|
||||
#endif
|
||||
|
||||
static cairo_ft_unscaled_font_t *
|
||||
_cairo_ft_unscaled_font_create_from_face (FT_Face face)
|
||||
static cairo_status_t
|
||||
_cairo_ft_unscaled_font_create_from_face (FT_Face face,
|
||||
cairo_ft_unscaled_font_t **out)
|
||||
{
|
||||
return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face);
|
||||
return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2317,7 +2331,6 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face,
|
||||
*scaled_font = _cairo_scaled_font_create_in_error (status);
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@ -2325,11 +2338,11 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face,
|
||||
ft_options = font_face->ft_options;
|
||||
}
|
||||
|
||||
return _cairo_ft_scaled_font_create (unscaled,
|
||||
&font_face->base,
|
||||
font_matrix, ctm,
|
||||
options, ft_options,
|
||||
scaled_font);
|
||||
return _cairo_ft_scaled_font_create (unscaled,
|
||||
&font_face->base,
|
||||
font_matrix, ctm,
|
||||
options, ft_options,
|
||||
scaled_font);
|
||||
}
|
||||
|
||||
const cairo_font_face_backend_t _cairo_ft_font_face_backend = {
|
||||
@ -2358,7 +2371,7 @@ _cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
|
||||
font_face->next = NULL;
|
||||
|
||||
font_face->pattern = FcPatternDuplicate (pattern);
|
||||
if (unlikely (pattern == NULL)) {
|
||||
if (unlikely (font_face->pattern == NULL)) {
|
||||
free (font_face);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
}
|
||||
@ -2564,6 +2577,8 @@ _cairo_ft_resolve_pattern (FcPattern *pattern,
|
||||
return status;
|
||||
|
||||
pattern = FcPatternDuplicate (pattern);
|
||||
if (pattern == NULL)
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
@ -2587,11 +2602,9 @@ _cairo_ft_resolve_pattern (FcPattern *pattern,
|
||||
goto FREE_PATTERN;
|
||||
}
|
||||
|
||||
*unscaled = _cairo_ft_unscaled_font_create_for_pattern (resolved);
|
||||
if (!*unscaled) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
status = _cairo_ft_unscaled_font_create_for_pattern (resolved, unscaled);
|
||||
if (unlikely (status))
|
||||
goto FREE_RESOLVED;
|
||||
}
|
||||
|
||||
_get_pattern_ft_options (resolved, ft_options);
|
||||
|
||||
@ -2647,10 +2660,12 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
|
||||
cairo_ft_unscaled_font_t *unscaled;
|
||||
cairo_font_face_t *font_face;
|
||||
cairo_ft_options_t ft_options;
|
||||
cairo_status_t status;
|
||||
|
||||
unscaled = _cairo_ft_unscaled_font_create_for_pattern (pattern);
|
||||
status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
|
||||
if (unlikely (status))
|
||||
return (cairo_font_face_t *) &_cairo_font_face_nil;
|
||||
if (unlikely (unscaled == NULL)) {
|
||||
cairo_status_t status;
|
||||
/* Store the pattern. We will resolve it and create unscaled
|
||||
* font when creating scaled fonts */
|
||||
status = _cairo_ft_font_face_create_for_pattern (pattern,
|
||||
@ -2722,12 +2737,11 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face,
|
||||
cairo_ft_unscaled_font_t *unscaled;
|
||||
cairo_font_face_t *font_face;
|
||||
cairo_ft_options_t ft_options;
|
||||
cairo_status_t status;
|
||||
|
||||
unscaled = _cairo_ft_unscaled_font_create_from_face (face);
|
||||
if (unlikely (unscaled == NULL)) {
|
||||
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
||||
status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled);
|
||||
if (unlikely (status))
|
||||
return (cairo_font_face_t *)&_cairo_font_face_nil;
|
||||
}
|
||||
|
||||
ft_options.load_flags = load_flags;
|
||||
ft_options.extra_flags = 0;
|
||||
|
@ -222,6 +222,9 @@ _cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist)
|
||||
cairo_gstate_t *top;
|
||||
cairo_status_t status;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
top = *freelist;
|
||||
if (top == NULL) {
|
||||
top = malloc (sizeof (cairo_gstate_t));
|
||||
@ -1678,16 +1681,21 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
|
||||
|
||||
/* For really huge font sizes, we can just do path;fill instead of
|
||||
* show_glyphs, as show_glyphs would put excess pressure on the cache,
|
||||
* and moreover, not all components below us correctly handle huge font
|
||||
* sizes. I wanted to set the limit at 256. But alas, seems like cairo's
|
||||
* not all components below us correctly handle huge font sizes, and
|
||||
* path filling can be cheaper since parts of glyphs are likely to be
|
||||
* clipped out. 256 seems like a good limit. But alas, seems like cairo's
|
||||
* rasterizer is something like ten times slower than freetype's for huge
|
||||
* sizes. So, no win just yet. For now, do it for insanely-huge sizes,
|
||||
* just to make sure we don't make anyone unhappy. When we get a really
|
||||
* fast rasterizer in cairo, we may want to readjust this.
|
||||
* sizes. So, no win just yet when we're using cairo's rasterizer.
|
||||
* For now, if we're using cairo's rasterizer, use path filling only
|
||||
* for insanely-huge sizes, just to make sure we don't make anyone
|
||||
* unhappy. When we get a really fast rasterizer in cairo, we may
|
||||
* want to readjust this. The threshold calculation is
|
||||
* encapsulated in _cairo_surface_get_text_path_fill_threshold.
|
||||
*
|
||||
* Needless to say, do this only if show_text_glyphs is not available. */
|
||||
if (cairo_surface_has_show_text_glyphs (gstate->target) ||
|
||||
_cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240) {
|
||||
_cairo_scaled_font_get_max_scale (gstate->scaled_font) <=
|
||||
_cairo_surface_get_text_path_fill_threshold (gstate->target)) {
|
||||
status = _cairo_surface_show_text_glyphs (gstate->target,
|
||||
gstate->op,
|
||||
source_pattern,
|
||||
|
@ -198,6 +198,9 @@ _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices)
|
||||
cairo_hull_t *hull;
|
||||
int num_hull = *num_vertices;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (num_hull > ARRAY_LENGTH (hull_stack)) {
|
||||
hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t));
|
||||
if (unlikely (hull == NULL))
|
||||
|
@ -1044,6 +1044,9 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface,
|
||||
|
||||
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
pixman_color.red = color->red_short;
|
||||
pixman_color.green = color->green_short;
|
||||
pixman_color.blue = color->blue_short;
|
||||
@ -1112,6 +1115,9 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op,
|
||||
if (height == 0 || width == 0)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
/* Convert traps to pixman traps */
|
||||
if (num_traps > ARRAY_LENGTH (stack_traps)) {
|
||||
pixman_traps = _cairo_malloc_ab (num_traps, sizeof (pixman_trapezoid_t));
|
||||
@ -1427,7 +1433,7 @@ _cairo_image_surface_set_clip_region (void *abstract_surface,
|
||||
{
|
||||
cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface;
|
||||
|
||||
if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn))
|
||||
if (! pixman_image_set_clip_region32 (surface->pixman_image, region? ®ion->rgn : NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
surface->has_clip = region != NULL;
|
||||
|
@ -39,6 +39,13 @@
|
||||
|
||||
#include "cairo-wideint-private.h"
|
||||
|
||||
#if HAVE_MEMFAULT
|
||||
#include <memfault.h>
|
||||
#define CAIRO_INJECT_FAULT() VALGRIND_INJECT_FAULT()
|
||||
#else
|
||||
#define CAIRO_INJECT_FAULT() 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* _cairo_malloc:
|
||||
* @size: size in bytes
|
||||
|
@ -123,9 +123,10 @@ cairo_status_to_string (cairo_status_t status)
|
||||
return "invalid value for an input #cairo_font_weight_t";
|
||||
case CAIRO_STATUS_INVALID_SIZE:
|
||||
return "invalid value for the size of the input (surface, pattern, etc.)";
|
||||
default:
|
||||
case CAIRO_STATUS_LAST_STATUS:
|
||||
return "<unknown error status>";
|
||||
}
|
||||
|
||||
return "<unknown error status>";
|
||||
}
|
||||
|
||||
|
||||
@ -610,7 +611,7 @@ _cairo_lround (double d)
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
|
||||
#if !WINCE
|
||||
#if !_WIN32_WCE
|
||||
/* tmpfile() replacement for Windows.
|
||||
*
|
||||
* On Windows tmpfile() creates the file in the root directory. This
|
||||
@ -660,7 +661,7 @@ _cairo_win32_tmpfile (void)
|
||||
|
||||
return fp;
|
||||
}
|
||||
#endif /* !WINCE */
|
||||
#endif /* !_WIN32_WCE */
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
@ -703,6 +704,9 @@ _cairo_intern_string (const char **str_inout, int len)
|
||||
cairo_intern_string_t tmpl, *istring;
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (len < 0)
|
||||
len = strlen (str);
|
||||
tmpl.hash_entry.hash = _intern_string_hash (str, len);
|
||||
|
@ -244,7 +244,7 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface,
|
||||
|
||||
static cairo_int_status_t
|
||||
_paint_fallback_image (cairo_paginated_surface_t *surface,
|
||||
cairo_box_int_t *box)
|
||||
cairo_rectangle_int_t *rect)
|
||||
{
|
||||
double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution;
|
||||
double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution;
|
||||
@ -254,10 +254,10 @@ _paint_fallback_image (cairo_paginated_surface_t *surface,
|
||||
cairo_surface_t *image;
|
||||
cairo_surface_pattern_t pattern;
|
||||
|
||||
x = box->p1.x;
|
||||
y = box->p1.y;
|
||||
width = box->p2.x - x;
|
||||
height = box->p2.y - y;
|
||||
x = rect->x;
|
||||
y = rect->y;
|
||||
width = rect->width;
|
||||
height = rect->height;
|
||||
image = _cairo_paginated_surface_create_image_surface (surface,
|
||||
ceil (width * x_scale),
|
||||
ceil (height * y_scale));
|
||||
@ -365,23 +365,23 @@ _paint_page (cairo_paginated_surface_t *surface)
|
||||
}
|
||||
|
||||
if (has_page_fallback) {
|
||||
cairo_box_int_t box;
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
surface->backend->set_paginated_mode (surface->target,
|
||||
CAIRO_PAGINATED_MODE_FALLBACK);
|
||||
|
||||
box.p1.x = 0;
|
||||
box.p1.y = 0;
|
||||
box.p2.x = surface->width;
|
||||
box.p2.y = surface->height;
|
||||
status = _paint_fallback_image (surface, &box);
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = surface->width;
|
||||
rect.height = surface->height;
|
||||
status = _paint_fallback_image (surface, &rect);
|
||||
if (unlikely (status))
|
||||
goto FAIL;
|
||||
}
|
||||
|
||||
if (has_finegrained_fallback) {
|
||||
cairo_region_t *region;
|
||||
int num_boxes, i;
|
||||
int num_rects, i;
|
||||
|
||||
surface->backend->set_paginated_mode (surface->target,
|
||||
CAIRO_PAGINATED_MODE_FALLBACK);
|
||||
@ -397,13 +397,13 @@ _paint_page (cairo_paginated_surface_t *surface)
|
||||
|
||||
region = _cairo_analysis_surface_get_unsupported (analysis);
|
||||
|
||||
num_boxes = _cairo_region_num_boxes (region);
|
||||
for (i = 0; i < num_boxes; i++) {
|
||||
cairo_box_int_t box;
|
||||
num_rects = cairo_region_num_rectangles (region);
|
||||
for (i = 0; i < num_rects; i++) {
|
||||
cairo_rectangle_int_t rect;
|
||||
|
||||
_cairo_region_get_box (region, i, &box);
|
||||
cairo_region_get_rectangle (region, i, &rect);
|
||||
|
||||
status = _paint_fallback_image (surface, &box);
|
||||
status = _paint_fallback_image (surface, &rect);
|
||||
|
||||
if (unlikely (status))
|
||||
goto FAIL;
|
||||
|
@ -201,15 +201,19 @@ _cairo_in_fill_curve_to (void *closure,
|
||||
if (c->y > bot) bot = c->y;
|
||||
if (d->y < top) top = d->y;
|
||||
if (d->y > bot) bot = d->y;
|
||||
if (bot < in_fill->y || top > in_fill->y)
|
||||
if (bot < in_fill->y || top > in_fill->y) {
|
||||
in_fill->current_point = *d;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
left = in_fill->current_point.x;
|
||||
if (b->x < left) left = b->x;
|
||||
if (c->x < left) left = c->x;
|
||||
if (d->x < left) left = d->x;
|
||||
if (left > in_fill->x)
|
||||
if (left > in_fill->x) {
|
||||
in_fill->current_point = *d;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* XXX Investigate direct inspection of the inflections? */
|
||||
if (! _cairo_spline_init (&spline,
|
||||
|
@ -1278,6 +1278,8 @@ _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
|
||||
cairo_bool_t is_horizontal,
|
||||
cairo_bool_t has_join)
|
||||
{
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (stroker->num_segments == stroker->segments_size) {
|
||||
int new_size = stroker->segments_size * 2;
|
||||
|
@ -119,6 +119,9 @@ static cairo_status_t
|
||||
_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern,
|
||||
const cairo_gradient_pattern_t *other)
|
||||
{
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR)
|
||||
{
|
||||
cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
|
||||
@ -250,9 +253,10 @@ _cairo_pattern_fini (cairo_pattern_t *pattern)
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_pattern_create_copy (cairo_pattern_t **pattern,
|
||||
_cairo_pattern_create_copy (cairo_pattern_t **pattern_out,
|
||||
const cairo_pattern_t *other)
|
||||
{
|
||||
cairo_pattern_t *pattern;
|
||||
cairo_status_t status;
|
||||
|
||||
if (other->status)
|
||||
@ -260,29 +264,32 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern,
|
||||
|
||||
switch (other->type) {
|
||||
case CAIRO_PATTERN_TYPE_SOLID:
|
||||
*pattern = malloc (sizeof (cairo_solid_pattern_t));
|
||||
pattern = malloc (sizeof (cairo_solid_pattern_t));
|
||||
break;
|
||||
case CAIRO_PATTERN_TYPE_SURFACE:
|
||||
*pattern = malloc (sizeof (cairo_surface_pattern_t));
|
||||
pattern = malloc (sizeof (cairo_surface_pattern_t));
|
||||
break;
|
||||
case CAIRO_PATTERN_TYPE_LINEAR:
|
||||
*pattern = malloc (sizeof (cairo_linear_pattern_t));
|
||||
pattern = malloc (sizeof (cairo_linear_pattern_t));
|
||||
break;
|
||||
case CAIRO_PATTERN_TYPE_RADIAL:
|
||||
*pattern = malloc (sizeof (cairo_radial_pattern_t));
|
||||
pattern = malloc (sizeof (cairo_radial_pattern_t));
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED;
|
||||
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
|
||||
}
|
||||
if (unlikely (*pattern == NULL))
|
||||
if (unlikely (pattern == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
status = _cairo_pattern_init_copy (*pattern, other);
|
||||
status = _cairo_pattern_init_copy (pattern, other);
|
||||
if (unlikely (status)) {
|
||||
free (*pattern);
|
||||
free (pattern);
|
||||
return status;
|
||||
}
|
||||
|
||||
CAIRO_REFERENCE_COUNT_INIT (&(*pattern)->ref_count, 1);
|
||||
|
||||
CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1);
|
||||
*pattern_out = pattern;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@ -837,6 +844,9 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
assert (pattern->n_stops <= pattern->stops_size);
|
||||
|
||||
if (pattern->stops == pattern->stops_embedded) {
|
||||
@ -1253,6 +1263,9 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
|
||||
int clone_offset_x, clone_offset_y;
|
||||
cairo_matrix_t matrix = pattern->base.matrix;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
|
||||
pixman_stops = _cairo_malloc_ab (pattern->n_stops,
|
||||
sizeof(pixman_gradient_stop_t));
|
||||
|
@ -1015,18 +1015,21 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
|
||||
static cairo_status_t
|
||||
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
|
||||
{
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
cairo_status_t status;
|
||||
long length;
|
||||
|
||||
if (! surface->pdf_stream.active)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
if (surface->pdf_stream.compressed) {
|
||||
status = _cairo_output_stream_destroy (surface->output);
|
||||
cairo_status_t status2;
|
||||
|
||||
status2 = _cairo_output_stream_destroy (surface->output);
|
||||
if (likely (status == CAIRO_STATUS_SUCCESS))
|
||||
status = status2;
|
||||
|
||||
surface->output = surface->pdf_stream.old_output;
|
||||
_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
|
||||
surface->pdf_stream.old_output = NULL;
|
||||
@ -1051,7 +1054,7 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
|
||||
|
||||
surface->pdf_stream.active = FALSE;
|
||||
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
if (likely (status == CAIRO_STATUS_SUCCESS))
|
||||
status = _cairo_output_stream_get_status (surface->output);
|
||||
|
||||
return status;
|
||||
@ -1324,10 +1327,12 @@ _cairo_pdf_surface_finish (void *abstract_surface)
|
||||
"%%%%EOF\n",
|
||||
offset);
|
||||
|
||||
status2 = _cairo_pdf_operators_fini (&surface->pdf_operators);
|
||||
/* pdf_operators has already been flushed when the last stream was
|
||||
* closed so we should never be writing anything here. */
|
||||
assert(status2 == CAIRO_STATUS_SUCCESS);
|
||||
* closed so we should never be writing anything here - however,
|
||||
* the stream may itself be in an error state. */
|
||||
status2 = _cairo_pdf_operators_fini (&surface->pdf_operators);
|
||||
if (status == CAIRO_STATUS_SUCCESS)
|
||||
status = status2;
|
||||
|
||||
/* close any active streams still open due to fatal errors */
|
||||
status2 = _cairo_pdf_surface_close_stream (surface);
|
||||
@ -1723,6 +1728,8 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface,
|
||||
|
||||
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
|
||||
&mime_data, &mime_data_length);
|
||||
if (unlikely (source->status))
|
||||
return source->status;
|
||||
if (mime_data == NULL)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
@ -1790,7 +1797,7 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
|
||||
|
||||
status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra);
|
||||
if (unlikely (status))
|
||||
goto BAIL;
|
||||
return status;
|
||||
|
||||
pad_image = &image->base;
|
||||
if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) {
|
||||
@ -1840,10 +1847,10 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
|
||||
*origin_x = x;
|
||||
*origin_y = y;
|
||||
|
||||
BAIL:
|
||||
if (pad_image != &image->base)
|
||||
cairo_surface_destroy (pad_image);
|
||||
|
||||
BAIL:
|
||||
_cairo_surface_release_source_image (pattern->surface, image, image_extra);
|
||||
|
||||
return status;
|
||||
@ -3927,6 +3934,11 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su
|
||||
null_stream,
|
||||
_cairo_pdf_emit_imagemask,
|
||||
surface->font_subsets);
|
||||
if (unlikely (type3_surface->status)) {
|
||||
status2 = _cairo_output_stream_destroy (null_stream);
|
||||
return type3_surface->status;
|
||||
}
|
||||
|
||||
_cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
|
||||
_cairo_pdf_surface_add_font,
|
||||
surface);
|
||||
@ -3983,6 +3995,12 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface,
|
||||
NULL,
|
||||
_cairo_pdf_emit_imagemask,
|
||||
surface->font_subsets);
|
||||
if (unlikely (type3_surface->status)) {
|
||||
free (glyphs);
|
||||
free (widths);
|
||||
return type3_surface->status;
|
||||
}
|
||||
|
||||
_cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
|
||||
_cairo_pdf_surface_add_font,
|
||||
surface);
|
||||
|
@ -55,6 +55,9 @@ _cairo_pen_init (cairo_pen_t *pen,
|
||||
int i;
|
||||
int reflect;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
pen->radius = radius;
|
||||
pen->tolerance = tolerance;
|
||||
|
||||
@ -109,6 +112,9 @@ _cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other)
|
||||
{
|
||||
*pen = *other;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
pen->vertices = pen->vertices_embedded;
|
||||
if (pen->num_vertices) {
|
||||
if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) {
|
||||
@ -132,6 +138,9 @@ _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
|
||||
int num_vertices;
|
||||
int i;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
num_vertices = pen->num_vertices + num_points;
|
||||
if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) ||
|
||||
pen->vertices != pen->vertices_embedded)
|
||||
|
@ -64,6 +64,11 @@ _cairo_polygon_grow (cairo_polygon_t *polygon)
|
||||
int old_size = polygon->edges_size;
|
||||
int new_size = 4 * old_size;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ()) {
|
||||
polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (polygon->edges == polygon->edges_embedded) {
|
||||
new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t));
|
||||
if (new_edges != NULL)
|
||||
|
@ -2138,6 +2138,8 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface,
|
||||
|
||||
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
|
||||
&mime_data, &mime_data_length);
|
||||
if (unlikely (source->status))
|
||||
return source->status;
|
||||
if (mime_data == NULL)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
@ -3315,6 +3317,9 @@ _cairo_ps_surface_stroke (void *abstract_surface,
|
||||
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
return _cairo_pdf_operators_stroke (&surface->pdf_operators,
|
||||
path,
|
||||
style,
|
||||
|
@ -33,55 +33,64 @@
|
||||
* Contributor(s):
|
||||
* Owen Taylor <otaylor@redhat.com>
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
* Søren Sandmann <sandmann@daimi.au.dk>
|
||||
*/
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
static const cairo_region_t _cairo_region_nil = {
|
||||
CAIRO_STATUS_NO_MEMORY, /* status */
|
||||
};
|
||||
|
||||
/**
|
||||
* _cairo_region_set_error:
|
||||
* @region: a region
|
||||
* @status: a status value indicating an error
|
||||
*
|
||||
* Atomically sets region->status to @status and calls _cairo_error;
|
||||
* Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal
|
||||
* status values.
|
||||
*
|
||||
* All assignments of an error status to region->status should happen
|
||||
* through _cairo_region_set_error(). Note that due to the nature of
|
||||
* the atomic operation, it is not safe to call this function on the
|
||||
* nil objects.
|
||||
*
|
||||
* The purpose of this function is to allow the user to set a
|
||||
* breakpoint in _cairo_error() to generate a stack trace for when the
|
||||
* user causes cairo to detect an error.
|
||||
*
|
||||
* Return value: the error status.
|
||||
**/
|
||||
static cairo_status_t
|
||||
_cairo_region_set_error (cairo_region_t *region,
|
||||
cairo_status_t status)
|
||||
{
|
||||
if (! _cairo_status_is_error (status))
|
||||
return status;
|
||||
|
||||
/* Don't overwrite an existing error. This preserves the first
|
||||
* error, which is the most significant. */
|
||||
_cairo_status_set_error (®ion->status, status);
|
||||
|
||||
return _cairo_error (status);
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_region_init (cairo_region_t *region)
|
||||
{
|
||||
region->status = CAIRO_STATUS_SUCCESS;
|
||||
pixman_region32_init (®ion->rgn);
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_region_init_rect (cairo_region_t *region,
|
||||
cairo_rectangle_int_t *rect)
|
||||
_cairo_region_init_rectangle (cairo_region_t *region,
|
||||
const cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
region->status = CAIRO_STATUS_SUCCESS;
|
||||
pixman_region32_init_rect (®ion->rgn,
|
||||
rect->x, rect->y,
|
||||
rect->width, rect->height);
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_region_init_boxes (cairo_region_t *region,
|
||||
cairo_box_int_t *boxes,
|
||||
int count)
|
||||
{
|
||||
pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)];
|
||||
pixman_box32_t *pboxes = stack_pboxes;
|
||||
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
int i;
|
||||
|
||||
if (count > ARRAY_LENGTH (stack_pboxes)) {
|
||||
pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t));
|
||||
if (unlikely (pboxes == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
pboxes[i].x1 = boxes[i].p1.x;
|
||||
pboxes[i].y1 = boxes[i].p1.y;
|
||||
pboxes[i].x2 = boxes[i].p2.x;
|
||||
pboxes[i].y2 = boxes[i].p2.y;
|
||||
}
|
||||
|
||||
if (! pixman_region32_init_rects (®ion->rgn, pboxes, count))
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (pboxes != stack_pboxes)
|
||||
free (pboxes);
|
||||
|
||||
return status;
|
||||
rectangle->x, rectangle->y,
|
||||
rectangle->width, rectangle->height);
|
||||
}
|
||||
|
||||
void
|
||||
@ -90,108 +99,512 @@ _cairo_region_fini (cairo_region_t *region)
|
||||
pixman_region32_fini (®ion->rgn);
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_region_copy (cairo_region_t *dst, cairo_region_t *src)
|
||||
/**
|
||||
* cairo_region_create:
|
||||
*
|
||||
* Allocates a new empty region object.
|
||||
*
|
||||
* Return value: A newly allocated #cairo_region_t. Free with
|
||||
* cairo_region_destroy(). This function always returns a
|
||||
* valid pointer; if memory cannot be allocated, then a special
|
||||
* error object is returned where all operations on the object do nothing.
|
||||
* You can check for this with cairo_region_status().
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_region_t *
|
||||
cairo_region_create (void)
|
||||
{
|
||||
if (!pixman_region32_copy (&dst->rgn, &src->rgn))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
cairo_region_t *region;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
region = _cairo_malloc (sizeof (cairo_region_t));
|
||||
if (region == NULL)
|
||||
return (cairo_region_t *) &_cairo_region_nil;
|
||||
|
||||
region->status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
pixman_region32_init (®ion->rgn);
|
||||
|
||||
return region;
|
||||
}
|
||||
slim_hidden_def (cairo_region_create);
|
||||
|
||||
int
|
||||
_cairo_region_num_boxes (cairo_region_t *region)
|
||||
/**
|
||||
* cairo_region_create_rectangle:
|
||||
* @rectangle: a #cairo_rectangle_int_t
|
||||
*
|
||||
* Allocates a new region object containing @rectangle.
|
||||
*
|
||||
* Return value: A newly allocated #cairo_region_t. Free with
|
||||
* cairo_region_destroy(). This function always returns a
|
||||
* valid pointer; if memory cannot be allocated, then a special
|
||||
* error object is returned where all operations on the object do nothing.
|
||||
* You can check for this with cairo_region_status().
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_region_t *
|
||||
cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
cairo_region_t *region;
|
||||
|
||||
region = _cairo_malloc (sizeof (cairo_region_t));
|
||||
if (region == NULL)
|
||||
return (cairo_region_t *) &_cairo_region_nil;
|
||||
|
||||
region->status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
pixman_region32_init_rect (®ion->rgn,
|
||||
rectangle->x, rectangle->y,
|
||||
rectangle->width, rectangle->height);
|
||||
|
||||
return region;
|
||||
}
|
||||
slim_hidden_def (cairo_region_create_rectangle);
|
||||
|
||||
/**
|
||||
* cairo_region_copy:
|
||||
* @original: a #cairo_region_t
|
||||
*
|
||||
* Allocates a new region object copying the area from @original.
|
||||
*
|
||||
* Return value: A newly allocated #cairo_region_t. Free with
|
||||
* cairo_region_destroy(). This function always returns a
|
||||
* valid pointer; if memory cannot be allocated, then a special
|
||||
* error object is returned where all operations on the object do nothing.
|
||||
* You can check for this with cairo_region_status().
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_region_t *
|
||||
cairo_region_copy (cairo_region_t *original)
|
||||
{
|
||||
cairo_region_t *copy;
|
||||
|
||||
if (original->status)
|
||||
return (cairo_region_t *) &_cairo_region_nil;
|
||||
|
||||
copy = cairo_region_create ();
|
||||
if (copy->status)
|
||||
return copy;
|
||||
|
||||
if (! pixman_region32_copy (©->rgn, &original->rgn)) {
|
||||
cairo_region_destroy (copy);
|
||||
return (cairo_region_t *) &_cairo_region_nil;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
slim_hidden_def (cairo_region_copy);
|
||||
|
||||
/**
|
||||
* cairo_region_destroy:
|
||||
* @region: a #cairo_region_t
|
||||
*
|
||||
* Destroys a #cairo_region_t object created with
|
||||
* cairo_region_create(), cairo_region_copy(), or
|
||||
* or cairo_region_create_rectangle().
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
void
|
||||
cairo_region_destroy (cairo_region_t *region)
|
||||
{
|
||||
if (region == (cairo_region_t *) &_cairo_region_nil)
|
||||
return;
|
||||
|
||||
pixman_region32_fini (®ion->rgn);
|
||||
free (region);
|
||||
}
|
||||
slim_hidden_def (cairo_region_destroy);
|
||||
|
||||
/**
|
||||
* cairo_region_num_rectangles:
|
||||
* @region: a #cairo_region_t
|
||||
*
|
||||
* Returns the number of rectangles contained in @region.
|
||||
*
|
||||
* Return value: The number of rectangles contained in @region.
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
int
|
||||
cairo_region_num_rectangles (cairo_region_t *region)
|
||||
{
|
||||
if (region->status)
|
||||
return 0;
|
||||
|
||||
return pixman_region32_n_rects (®ion->rgn);
|
||||
}
|
||||
slim_hidden_def (cairo_region_num_rectangles);
|
||||
|
||||
cairo_private void
|
||||
_cairo_region_get_box (cairo_region_t *region,
|
||||
int nth_box,
|
||||
cairo_box_int_t *box)
|
||||
/**
|
||||
* cairo_region_get_rectangle:
|
||||
* @region: a #cairo_region_t
|
||||
* @nth: a number indicating which rectangle should be returned
|
||||
* @rectangle: return location for a #cairo_rectangle_int_t
|
||||
*
|
||||
* Stores the @nth rectangle from the region in @rectangle.
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
void
|
||||
cairo_region_get_rectangle (cairo_region_t *region,
|
||||
int nth,
|
||||
cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
pixman_box32_t *pbox;
|
||||
|
||||
pbox = pixman_region32_rectangles (®ion->rgn, NULL) + nth_box;
|
||||
if (region->status) {
|
||||
rectangle->x = rectangle->y = 0;
|
||||
rectangle->width = rectangle->height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
box->p1.x = pbox->x1;
|
||||
box->p1.y = pbox->y1;
|
||||
box->p2.x = pbox->x2;
|
||||
box->p2.y = pbox->y2;
|
||||
pbox = pixman_region32_rectangles (®ion->rgn, NULL) + nth;
|
||||
|
||||
rectangle->x = pbox->x1;
|
||||
rectangle->y = pbox->y1;
|
||||
rectangle->width = pbox->x2 - pbox->x1;
|
||||
rectangle->height = pbox->y2 - pbox->y1;
|
||||
}
|
||||
slim_hidden_def (cairo_region_get_rectangle);
|
||||
|
||||
/**
|
||||
* _cairo_region_get_extents:
|
||||
* cairo_region_get_extents:
|
||||
* @region: a #cairo_region_t
|
||||
* @rect: rectangle into which to store the extents
|
||||
* @rectangle: rectangle into which to store the extents
|
||||
*
|
||||
* Gets the bounding box of a region as a #cairo_rectangle_int_t
|
||||
* Gets the bounding rectangle of @region as a #cairo_rectangle_int_t
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
void
|
||||
_cairo_region_get_extents (cairo_region_t *region, cairo_rectangle_int_t *extents)
|
||||
cairo_region_get_extents (cairo_region_t *region,
|
||||
cairo_rectangle_int_t *extents)
|
||||
{
|
||||
pixman_box32_t *pextents = pixman_region32_extents (®ion->rgn);
|
||||
pixman_box32_t *pextents;
|
||||
|
||||
if (region->status) {
|
||||
extents->x = extents->y = 0;
|
||||
extents->width = extents->height = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
pextents = pixman_region32_extents (®ion->rgn);
|
||||
|
||||
extents->x = pextents->x1;
|
||||
extents->y = pextents->y1;
|
||||
extents->width = pextents->x2 - pextents->x1;
|
||||
extents->height = pextents->y2 - pextents->y1;
|
||||
}
|
||||
slim_hidden_def (cairo_region_get_extents);
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_region_subtract (cairo_region_t *dst, cairo_region_t *a, cairo_region_t *b)
|
||||
/**
|
||||
* cairo_region_status:
|
||||
* @region: a #cairo_region_t
|
||||
*
|
||||
* Checks whether an error has previous occured for this
|
||||
* region object.
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_status (cairo_region_t *region)
|
||||
{
|
||||
if (!pixman_region32_subtract (&dst->rgn, &a->rgn, &b->rgn))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return region->status;
|
||||
}
|
||||
slim_hidden_def (cairo_region_status);
|
||||
|
||||
/**
|
||||
* cairo_region_subtract:
|
||||
* @dst: a #cairo_region_t
|
||||
* @other: another #cairo_region_t
|
||||
*
|
||||
* Subtracts @other from @dst and places the result in @dst
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other)
|
||||
{
|
||||
if (dst->status)
|
||||
return dst->status;
|
||||
|
||||
if (other->status)
|
||||
return _cairo_region_set_error (dst, other->status);
|
||||
|
||||
if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &other->rgn))
|
||||
return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
slim_hidden_def (cairo_region_subtract);
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_region_intersect (cairo_region_t *dst, cairo_region_t *a, cairo_region_t *b)
|
||||
/**
|
||||
* cairo_region_subtract_rectangle:
|
||||
* @dst: a #cairo_region_t
|
||||
* @rectangle: a #cairo_rectangle_int_t
|
||||
*
|
||||
* Subtracts @rectangle from @dst and places the result in @dst
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_subtract_rectangle (cairo_region_t *dst,
|
||||
const cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
if (!pixman_region32_intersect (&dst->rgn, &a->rgn, &b->rgn))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
pixman_region32_t region;
|
||||
|
||||
if (dst->status)
|
||||
return dst->status;
|
||||
|
||||
pixman_region32_init_rect (®ion,
|
||||
rectangle->x, rectangle->y,
|
||||
rectangle->width, rectangle->height);
|
||||
|
||||
if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion))
|
||||
status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
pixman_region32_fini (®ion);
|
||||
|
||||
return status;
|
||||
}
|
||||
slim_hidden_def (cairo_region_subtract_rectangle);
|
||||
|
||||
/**
|
||||
* cairo_region_intersect:
|
||||
* @dst: a #cairo_region_t
|
||||
* @other: another #cairo_region_t
|
||||
*
|
||||
* Computes the intersection of @dst with @other and places the result in @dst
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_intersect (cairo_region_t *dst, cairo_region_t *other)
|
||||
{
|
||||
if (dst->status)
|
||||
return dst->status;
|
||||
|
||||
if (other->status)
|
||||
return _cairo_region_set_error (dst, other->status);
|
||||
|
||||
if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, &other->rgn))
|
||||
return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
slim_hidden_def (cairo_region_intersect);
|
||||
|
||||
cairo_int_status_t
|
||||
_cairo_region_union_rect (cairo_region_t *dst,
|
||||
cairo_region_t *src,
|
||||
cairo_rectangle_int_t *rect)
|
||||
/**
|
||||
* cairo_region_intersect_rectangle:
|
||||
* @dst: a #cairo_region_t
|
||||
* @rectangle: a #cairo_rectangle_int_t
|
||||
*
|
||||
* Computes the intersection of @dst with @rectangle and places the
|
||||
* result in @dst
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_intersect_rectangle (cairo_region_t *dst,
|
||||
const cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
if (!pixman_region32_union_rect (&dst->rgn, &src->rgn,
|
||||
rect->x, rect->y,
|
||||
rect->width, rect->height))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
pixman_region32_t region;
|
||||
|
||||
if (dst->status)
|
||||
return dst->status;
|
||||
|
||||
pixman_region32_init_rect (®ion,
|
||||
rectangle->x, rectangle->y,
|
||||
rectangle->width, rectangle->height);
|
||||
|
||||
if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion))
|
||||
status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
pixman_region32_fini (®ion);
|
||||
|
||||
return status;
|
||||
}
|
||||
slim_hidden_def (cairo_region_intersect_rectangle);
|
||||
|
||||
/**
|
||||
* cairo_region_union:
|
||||
* @dst: a #cairo_region_t
|
||||
* @other: another #cairo_region_t
|
||||
*
|
||||
* Computes the union of @dst with @other and places the result in @dst
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_union (cairo_region_t *dst,
|
||||
cairo_region_t *other)
|
||||
{
|
||||
if (dst->status)
|
||||
return dst->status;
|
||||
|
||||
if (other->status)
|
||||
return _cairo_region_set_error (dst, other->status);
|
||||
|
||||
if (! pixman_region32_union (&dst->rgn, &dst->rgn, &other->rgn))
|
||||
return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
slim_hidden_def (cairo_region_union);
|
||||
|
||||
/**
|
||||
* cairo_region_union_rectangle:
|
||||
* @dst: a #cairo_region_t
|
||||
* @rectangle: a #cairo_rectangle_int_t
|
||||
*
|
||||
* Computes the union of @dst with @rectangle and places the result in @dst.
|
||||
*
|
||||
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_status_t
|
||||
cairo_region_union_rectangle (cairo_region_t *dst,
|
||||
const cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
pixman_region32_t region;
|
||||
|
||||
if (dst->status)
|
||||
return dst->status;
|
||||
|
||||
pixman_region32_init_rect (®ion,
|
||||
rectangle->x, rectangle->y,
|
||||
rectangle->width, rectangle->height);
|
||||
|
||||
if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion))
|
||||
status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
pixman_region32_fini (®ion);
|
||||
|
||||
return status;
|
||||
}
|
||||
slim_hidden_def (cairo_region_union_rectangle);
|
||||
|
||||
/**
|
||||
* cairo_region_is_empty:
|
||||
* @region: a #cairo_region_t
|
||||
*
|
||||
* Checks whether @region is empty.
|
||||
*
|
||||
* Return value: %TRUE if @region is empty, %FALSE if it isn't.
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_bool_t
|
||||
_cairo_region_not_empty (cairo_region_t *region)
|
||||
cairo_region_is_empty (cairo_region_t *region)
|
||||
{
|
||||
return (cairo_bool_t) pixman_region32_not_empty (®ion->rgn);
|
||||
}
|
||||
if (region->status)
|
||||
return TRUE;
|
||||
|
||||
return ! pixman_region32_not_empty (®ion->rgn);
|
||||
}
|
||||
slim_hidden_def (cairo_region_is_empty);
|
||||
|
||||
/**
|
||||
* cairo_region_translate:
|
||||
* @region: a #cairo_region_t
|
||||
* @dx: Amount to translate in the x direction
|
||||
* @dy: Amount to translate in the y direction
|
||||
*
|
||||
* Translates @region by (@dx, @dy).
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
void
|
||||
_cairo_region_translate (cairo_region_t *region,
|
||||
int x, int y)
|
||||
cairo_region_translate (cairo_region_t *region,
|
||||
int dx, int dy)
|
||||
{
|
||||
pixman_region32_translate (®ion->rgn, x, y);
|
||||
}
|
||||
if (region->status)
|
||||
return;
|
||||
|
||||
pixman_region_overlap_t
|
||||
_cairo_region_contains_rectangle (cairo_region_t *region,
|
||||
const cairo_rectangle_int_t *rect)
|
||||
pixman_region32_translate (®ion->rgn, dx, dy);
|
||||
}
|
||||
slim_hidden_def (cairo_region_translate);
|
||||
|
||||
/**
|
||||
* cairo_region_contains_rectangle:
|
||||
* @region: a #cairo_region_t
|
||||
* @rectangle: a #cairo_rectangle_int_t
|
||||
*
|
||||
* Checks whether @rectangle is inside, outside or partially contained
|
||||
* in @region
|
||||
*
|
||||
* Return value:
|
||||
* %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region,
|
||||
* %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or
|
||||
* %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region.
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_region_overlap_t
|
||||
cairo_region_contains_rectangle (cairo_region_t *region,
|
||||
const cairo_rectangle_int_t *rectangle)
|
||||
{
|
||||
pixman_box32_t pbox;
|
||||
pixman_region_overlap_t poverlap;
|
||||
|
||||
pbox.x1 = rect->x;
|
||||
pbox.y1 = rect->y;
|
||||
pbox.x2 = rect->x + rect->width;
|
||||
pbox.y2 = rect->y + rect->height;
|
||||
if (region->status)
|
||||
return CAIRO_REGION_OVERLAP_OUT;
|
||||
|
||||
return pixman_region32_contains_rectangle (®ion->rgn, &pbox);
|
||||
pbox.x1 = rectangle->x;
|
||||
pbox.y1 = rectangle->y;
|
||||
pbox.x2 = rectangle->x + rectangle->width;
|
||||
pbox.y2 = rectangle->y + rectangle->height;
|
||||
|
||||
poverlap = pixman_region32_contains_rectangle (®ion->rgn, &pbox);
|
||||
switch (poverlap) {
|
||||
default:
|
||||
case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT;
|
||||
case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN;
|
||||
case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART;
|
||||
}
|
||||
}
|
||||
slim_hidden_def (cairo_region_contains_rectangle);
|
||||
|
||||
/**
|
||||
* cairo_region_contains_point:
|
||||
* @region: a #cairo_region_t
|
||||
* @x: the x coordinate of a point
|
||||
* @y: the y coordinate of a point
|
||||
*
|
||||
* Checks whether (@x, @y) is contained in @region.
|
||||
*
|
||||
* Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not.
|
||||
*
|
||||
* Since: 1.10
|
||||
**/
|
||||
cairo_bool_t
|
||||
cairo_region_contains_point (cairo_region_t *region,
|
||||
int x, int y)
|
||||
{
|
||||
pixman_box32_t box;
|
||||
|
||||
if (region->status)
|
||||
return FALSE;
|
||||
|
||||
return pixman_region32_contains_point (®ion->rgn, x, y, &box);
|
||||
}
|
||||
slim_hidden_def (cairo_region_contains_point);
|
||||
|
@ -372,13 +372,16 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
static cairo_status_t
|
||||
_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
|
||||
const char *utf8,
|
||||
int utf8_len)
|
||||
int utf8_len,
|
||||
cairo_bool_t *is_mapped)
|
||||
{
|
||||
*is_mapped = FALSE;
|
||||
|
||||
if (utf8_len < 0)
|
||||
return FALSE;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0')
|
||||
utf8_len--;
|
||||
@ -389,28 +392,25 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
|
||||
memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0)
|
||||
{
|
||||
/* Requested utf8 mapping matches the existing mapping */
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Requested utf8 mapping does not match the existing mapping */
|
||||
return FALSE;
|
||||
*is_mapped = TRUE;
|
||||
}
|
||||
} else {
|
||||
/* No existing mapping. Use the requested mapping */
|
||||
sub_font_glyph->utf8 = malloc (utf8_len + 1);
|
||||
if (unlikely (sub_font_glyph->utf8 == NULL))
|
||||
return CAIRO_STATUS_NO_MEMORY;
|
||||
|
||||
memcpy (sub_font_glyph->utf8, utf8, utf8_len);
|
||||
sub_font_glyph->utf8[utf8_len] = 0;
|
||||
sub_font_glyph->utf8_len = utf8_len;
|
||||
return TRUE;
|
||||
*is_mapped = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* No mapping was requested. */
|
||||
return FALSE;
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static cairo_bool_t
|
||||
static cairo_int_status_t
|
||||
_cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font,
|
||||
unsigned long scaled_font_glyph_index,
|
||||
const char *utf8,
|
||||
@ -418,6 +418,7 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font,
|
||||
cairo_scaled_font_subsets_glyph_t *subset_glyph)
|
||||
{
|
||||
cairo_sub_font_glyph_t key, *sub_font_glyph;
|
||||
cairo_int_status_t status;
|
||||
|
||||
_cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
|
||||
sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
|
||||
@ -430,13 +431,15 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font,
|
||||
subset_glyph->is_composite = sub_font->is_composite;
|
||||
subset_glyph->x_advance = sub_font_glyph->x_advance;
|
||||
subset_glyph->y_advance = sub_font_glyph->y_advance;
|
||||
subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len);
|
||||
status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
|
||||
utf8, utf8_len,
|
||||
&subset_glyph->utf8_is_mapped);
|
||||
subset_glyph->unicode = sub_font_glyph->unicode;
|
||||
|
||||
return TRUE;
|
||||
return status;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
@ -524,10 +527,12 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
|
||||
subset_glyph->is_composite = sub_font->is_composite;
|
||||
subset_glyph->x_advance = sub_font_glyph->x_advance;
|
||||
subset_glyph->y_advance = sub_font_glyph->y_advance;
|
||||
subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len);
|
||||
status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
|
||||
utf8, utf8_len,
|
||||
&subset_glyph->utf8_is_mapped);
|
||||
subset_glyph->unicode = sub_font_glyph->unicode;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -539,6 +544,10 @@ _cairo_sub_font_collect (void *entry, void *closure)
|
||||
int i;
|
||||
unsigned int j;
|
||||
|
||||
if (collection->status)
|
||||
return;
|
||||
|
||||
collection->status = sub_font->scaled_font->status;
|
||||
if (collection->status)
|
||||
return;
|
||||
|
||||
@ -682,11 +691,12 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets,
|
||||
sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts,
|
||||
&key.base);
|
||||
if (sub_font != NULL) {
|
||||
if (_cairo_sub_font_lookup_glyph (sub_font,
|
||||
scaled_font_glyph_index,
|
||||
utf8, utf8_len,
|
||||
subset_glyph))
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
status = _cairo_sub_font_lookup_glyph (sub_font,
|
||||
scaled_font_glyph_index,
|
||||
utf8, utf8_len,
|
||||
subset_glyph);
|
||||
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@ -696,11 +706,12 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets,
|
||||
sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts,
|
||||
&key.base);
|
||||
if (sub_font != NULL) {
|
||||
if (_cairo_sub_font_lookup_glyph (sub_font,
|
||||
scaled_font_glyph_index,
|
||||
utf8, utf8_len,
|
||||
subset_glyph))
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
status = _cairo_sub_font_lookup_glyph (sub_font,
|
||||
scaled_font_glyph_index,
|
||||
utf8, utf8_len,
|
||||
subset_glyph);
|
||||
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Glyph not found. Determine whether the glyph is outline or
|
||||
@ -1021,7 +1032,7 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset
|
||||
if (utf8 && *utf8) {
|
||||
status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
|
||||
if (unlikely (status))
|
||||
return status; /* FIXME */
|
||||
goto CLEANUP_HASH;
|
||||
}
|
||||
|
||||
if (utf16_len == 1) {
|
||||
|
@ -2558,6 +2558,9 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
|
||||
if (unlikely (scaled_font->status))
|
||||
return scaled_font->status;
|
||||
|
||||
if (CAIRO_INJECT_FAULT ())
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
/*
|
||||
* Check cache for glyph
|
||||
*/
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user